种群优化算法:二进制遗传算法(BGA)。第 II 部分·综合运用
🧬

种群优化算法:二进制遗传算法(BGA)。第 II 部分·综合运用

(3/3)·把 BGA 的种群初始化、算子与终止条件拼成可跑的优化闭环,跳过这一步容易只懂概念不会落地

进阶 第 3/3 篇
很多交易者读完算法原理就以为能直接拿来调参,结果卡在二进制编码与适应度函数的对接上。BGA 的真正门槛不在公式,而在把策略参数映射成 0/1 串后还能跑出有意义的回测。本篇把前面两篇的概念收口成一套可执行的流程。

◍ 染色体倒位与变异的执行细节

遗传算法里的交叉段拼接完,还要做倒位(inversion)和变异(mutation)才能生成新个体。倒位就是随机选一个断点 p1,把染色体后半段搬到临时数组头部、前半段接尾部,再整体写回,相当于打乱基因顺序但不改基因本身。 代码里用 RNDfromCI(0.0,1.0) 取均匀随机数 r,当 r < inversionProbab 才触发倒位;p1 越界时强制钳到 lengthChrome-1。这一步的概率参数直接决定种群多样性,外汇与贵金属行情非线性强,倒位概率设太高可能让已收敛的参数组合被撕碎,属高风险调参。 变异则遍历每个基因位 len,r < mutationProbab 就把 0/1 翻转。之后调 ExtractGenes() 解码,再用 SeInDiSp() 把连续参数约束回 [rangeMin,rangeMax] 并按 rangeStep 离散化。popSize 个新个体全部 marked calculated=true 才算这代繁殖完。 Revision() 负责把子代并回父代池排序,若头名 fitness 超过历史最佳 fB 就更新 cB 最优基因数组——这是你开 MT5 跑 EA 后能在日志里盯的收敛信号。

MQL5 / C++
ArrayCopy(points, poRND, class="num">1, class="num">0, WHOLE_ARRAY);
points [class="num">0] = class="num">0;
points [pCount + class="num">1] = lengthChrome - class="num">1;
r = RNDfromCI(class="num">0.0, class="num">1.0);
class="type">int startPoint = r > class="num">0.5 ? class="num">0 : class="num">1;
for (class="type">int p = startPoint; p < pCount + class="num">2; p += class="num">2)
{
  if (p < pCount + class="num">1)
  {
    for (class="type">uint len = points [p]; len < points [p + class="num">1]; len++) a [i].chromosome [len] = parents [pos].chromosome [len];
  }
}
}
class=class="str">"cmt">//perform an inversion------------------------------------------------------
class=class="str">"cmt">//(break the chromosome, swap the received parts, connect them together)
r = RNDfromCI(class="num">0.0, class="num">1.0);
if (r < inversionProbab)
{
  p1 = (class="type">int)RNDfromCI(class="num">0.0, lengthChrome);
  if (p1 >= lengthChrome) p1 = lengthChrome - class="num">1;
  class=class="str">"cmt">//copying the second part to the beginning of the temporary array
  for (class="type">uint len = p1; len < lengthChrome; len++) tempChrome [len - p1] = a [i].chromosome [len];
  class=class="str">"cmt">//copying the first part to the end of the temporary array
  for (class="type">uint len = class="num">0; len < p1; len++)      tempChrome [lengthChrome - p1 + len] = a [i].chromosome [len];
  class=class="str">"cmt">//copying a temporary array back
  for (class="type">uint len = class="num">0; len < lengthChrome; len++)  a [i].chromosome [len] = tempChrome [len];
}
class=class="str">"cmt">//perform mutation---------------------------------------------------------
for (class="type">uint len = class="num">0; len < lengthChrome; len++)
{
  r = RNDfromCI(class="num">0.0, class="num">1.0);
  if (r < mutationProbab) a [i].chromosome [len] = a [i].chromosome [len] == class="num">1 ? class="num">0 : class="num">1;
}
a [i].ExtractGenes();
for (class="type">int c = class="num">0; c < coords; c++) a [i].c [c] = SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
a [i].calculated = true;
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_BGA::Revision()
{
  class=class="str">"cmt">//----------------------------------------------------------------------------
  for (class="type">int i = parentPopSize; i < parentPopSize + popSize; i++)
  {
    parents [i] = a [i - parentPopSize];
  }
  Sorting(parents, parentPopSize + popSize);
  if (parents [class="num">0].f > fB)
  {
    fB = parents [class="num">0].f;
    ArrayCopy(cB, parents [class="num">0].c, class="num">0, class="num">0, WHOLE_ARRAY);
  }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_BGA::PreCalcRoulette()
{
  roulette [class="num">0].start = parents [class="num">0].f;

「轮盘赌选择里那一点防退化补偿」

这段遗传算法父代筛选代码里,轮盘区间不是简单按适应度比例铺开的。前 parentPopSize-1 个个体的区间长度用自身适应度减去最差个体适应度,等于把全局最差当基线抬了一截,避免弱势群体区间被压成零。 末尾那个 else 分支单独处理了最后一名:它的区间长度取前一名与自身适应度差值的 0.1 倍,而不是常规差值。这个 0.1 的缩放是刻意留的逃生口——即使最差个体适应度极低,仍保留约十分之一相对缝隙被抽中,降低种群早熟收敛概率。 SpinRoulette 函数在总区间 [roulette[0].start, roulette[parentPopSize-1].end] 内均匀取随机数 r,线性扫一遍落点返回索引。你在 MT5 里接这套逻辑时,可以把 0.1 改成外部参数,回测不同补偿系数下黄金 1H 参数寻优的多样性衰减曲线。外汇与贵金属杠杆高,遗传寻优结果仅作概率参考,实盘须控仓。

MQL5 / C++
  roulette [class="num">0].end   = roulette [class="num">0].start + (parents [class="num">0].f - parents [parentPopSize - class="num">1].f);
  for (class="type">int s = class="num">1; s < parentPopSize; s++)
  {
    if (s != parentPopSize - class="num">1)
    {
      roulette [s].start = roulette [s - class="num">1].end;
      roulette [s].end   = roulette [s].start + (parents [s].f - parents [parentPopSize - class="num">1].f);
    }
    else
    {
      roulette [s].start = roulette [s - class="num">1].end;
      roulette [s].end   = roulette [s].start + (parents [s - class="num">1].f - parents [s].f) * class="num">0.1;
    }
  }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">int C_AO_BGA::SpinRoulette()
{
  class="type">class="kw">double r = RNDfromCI(roulette [class="num">0].start, roulette [parentPopSize - class="num">1].end);
  for (class="type">int s = class="num">0; s < parentPopSize; s++)
  {
    if (roulette [s].start <= r && r < roulette [s].end) class="kw">return s;
  }
  class="kw">return class="num">0;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————

BGA 跑分台:收敛快但怕离散坑

在 BGA 测试台上,每组函数均跑满 10000 次调用。Hilly 函数在 5/25/500 维度下得分分别为 0.99992 / 0.99484 / 0.50483;Forest 函数为 1.00000 / 0.99975 / 0.32054;Megacity 离散函数则为 0.90667 / 0.96400 / 0.23035。三项合计总分 6.921,折算百分比 76.90%,在 31 个对比算法里排第一。 可视化过程显示种群高度收敛,但有趣的是总有一小撮个体漂在全局极值远处,说明算法仍在探未知区、维持了解多样性。不过 Megacity 这种离散函数让 BGA 也吃了瘪——500 维只拿到 0.23 水平,大多数演化类算法在这上面都翻车。 横向看榜单:排第二的 (P+O)ES 总分 6.496(72.18%),SDSm 5.709(63.44%),DE 差分进化仅 4.955(55.06%)。BGA 在 Hilly 和 Forest 上几乎碾压,Megacity 上虽掉链子但仍优于其它对手。 外汇与贵金属参数优化属高风险操作,回测高分不预示实盘概率优势。开 MT5 把上述维度组合丢进遗传优化器,先验证 Hilly 类连续函数能否复现 0.99+ 收敛,再掂量离散品种该怎么降维。

◍ 别急着下结论

BGA 作为经典 GA 的二进制特例,把独立维度压进一条染色体统一编码,这种结构在实值编码里很难复刻,也让它在平滑、离散乃至大规模随机问题上都跑得动。实验里相关测试分值 ≥0.99 的结果会用白色高亮,直方图标尺 0–100 且越大越好,100 仅是理论极值,别拿它当实盘预期。 交叠算子像摇万花筒:随机切分染色体再拼回,可能把不同解元的好特征揉成更优组合,但结果也可能偏得出奇,不是每次都出钻石。它优势在抗粘连和高收敛,短板是外部参数多、实现复杂、算力吃紧,外汇与贵金属优化属高风险,回测亮眼不代表实盘能复现。 附档 31_The_world_of_AO_BGA.zip(约 567.93 KB)含前几篇算法代码的更新版,作者声明多处改动只为提升搜索力,结论基于实验而非绝对规范。开 MT5 把脚本挂上跑一遍,比盯着文字琢磨更实在。

让小布替你扫参数收敛曲线
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到种群迭代的适应度走势,把重复劳动交给小布,你专注决策。

常见问题

过高突变会破坏已积累的有效基因片段,种群在解空间里随机游走得厉害,优良模式留存概率下降,回测中常表现为适应度震荡不降。
参数范围离散且维度不高时二进制更稳,连续敏感型参数用实数编码精度更好;BGA 适合前者,混血算法常结合两者。
规模过小易丢失多样性,几代内就挤向局部峰,概率上倾向早熟;适度扩容并配合精英保留可缓解。
目前小布内置的是诊断与看板,不代跑用户 EA 的遗传优化;复杂搜索仍建议在 MT5 策略测试器内执行,小布辅助你读结果。
不够,外汇贵金属高风险品种常加适应度 plateau 判定,连续若干代无提升即停,避免无谓计算也防过拟合。