种群优化算法:二进制遗传算法(BGA)。第 I 部分(基础篇)
📘

种群优化算法:二进制遗传算法(BGA)。第 I 部分(基础篇)

第 1/2 篇

「用二进制编码重排交易参数搜索空间」

在 MT5 里做策略寻优,实数编码直观但容易陷入局部极值;二进制遗传算法(BGA)把每个待调参数映射成 0/1 串,再靠选择、交叠、突变三代操作逼近更优解。这种编码方式在 2024-10-14 发布的示例中被用于种群优化,原文标注示例阅读量 722、评论 5 条,说明社区对可落地的寻优骨架有真实需求。 二进制并非简单按位展开。格雷码(Gray code)让相邻十进制数只差一位,能削弱交叠时基因型小幅变动引发表现型跳变的问题,对价格阈值类参数更友好。 开 MT5 新建 EA,把手里的均线周期、止损点数先写成二进制染色体,就能直接套后面的选择与直线交叠逻辑做验证。外汇与贵金属杠杆高,回测优解实盘可能失效,任何寻优结果都只是概率倾向。

◍ 把遗传算法当可拆解的优化工具箱

遗传算法在 MQL5 里不只是黑盒优化器,它底层那套选择、交叉、变异的例程,完全可以抽出来当通用模块用。 本文要做的,是给你一套能在自定义方案里拼装优化逻辑的原语,而不只是跑通一个 GA 模板。 重点在于:同一套框架能验证任意优化算法的问题结构——比如你怀疑自己的网格搜索在参数空间里漏了局部极值,就能用这里的模块做对照实验。外汇与贵金属品种点差跳变频繁,这类实验请在策略测试器用真实 tick 回测,结果仅代表历史样本,实盘仍属高风险。

实数与二进制:优化特征的两种编码底色

在遗传类优化里,被调的参数叫“特征”,分表现型(肉眼看到的参数值)和基因型(算法内部存的形态)。多数算法里两者一致,直接用实数表达;染色体就是一组基因,也就是一组待优参数。 实数带小数点,比如 3.14、0.5,写策略和日常读数都顺手。MQL5 的 double 类型按 IEEE 754 编码,总共只有 16 个有效数位——像 9999999999999999.0、0.9999999999999999 这种,前后加起来顶多 16 个 9,超了就丢精度。这直接关系到EA里参数边界该怎么设。 二进制只用 0 和 1,每位叫一个比特。数字 5 写成 101。它的好处是把多个特征拼进同一个多维空间,突变、交叉这类按位操作直接挪比特就行,比实数省事。 缺点也实在:二进制最终还得转回实数交给优化目标算收益,转换和存比特数组都额外耗时间。外汇和贵金属参数优化本就高波动高风险,编码方式选错可能让搜索卡在局部解。 实数灵活控连续值,二进制擅整体拼空间和位运算。除了遗传算法,不少优化器会混用两者,各取所需。

「用格雷码抹平优化搜索里的盲点」

普通二进制有个恼人的特性:相邻十进制数可能触发多位翻转。7(0111)跳到 8(1000)时四个比特全变,导致搜索空间概率不均,出现“增加概率带”和“盲点”,浮点计算里更麻烦——小数点后微小变动可能让二进制表示大变样。 格雷码(反射二进制码)靠“单位距离属性”解决这事:相邻数只差一个比特位,序列过渡平滑,能回避不少精度丢失问题。 以优化参数边界 最小值=-156.675、最大值=456.6789 为例,距离=613.3539。把负数区间平移成 0~613.3539 的正数,整数和小数分别转格雷码:613(十)=1101010111(格),3539(十)=101100111010(格),用“:”拼成字符串存下,逆向也能还原。 这样实数整数部可塞 16 位、小数部也能到 16 位;ulong 长度够的话总有效位冲到 32 位以上也没压力。小数保 16 位时,最大小数 9999999999999999 的格雷码长达 54 位,613.9999999999999999 就按“整数:小数”格式拼进去。 下面四个函数管编解码。DecimalToGray 用 decimalNumber ^ (decimalNumber>>1) 算 grayCode 再交 IntegerToBinary;IntegerToBinary 循环取余填 char 数组后翻转;GrayToDecimal 借 BinaryToInteger 还原二进制再右移异或;BinaryToInteger 按 startInd~endInd 从字符串抽段转十进制。带起止索引的好处是:一条染色体存全部参数时,能只抠出某段还原,不用整条重算。

MQL5 / C++
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//Converting a decimal number to a Gray code
class="type">void DecimalToGray(class="type">class="kw">ulong decimalNumber, class="type">char &array [])
{
  class="type">class="kw">ulong grayCode = decimalNumber ^(decimalNumber >> class="num">1);
  IntegerToBinary(grayCode, array);
}
class=class="str">"cmt">//Converting a decimal number to a binary number
class="type">void IntegerToBinary(class="type">class="kw">ulong number, class="type">char &array [])
{
  ArrayResize(array, class="num">0);
  class="type">class="kw">ulong temp;
  class="type">int cnt = class="num">0;
  class="kw">while (number > class="num">0)
  {
    ArrayResize(array, cnt + class="num">1);
    temp = number % class="num">2;
    array [cnt] = (class="type">char)temp;
    number = number / class="num">2;
    cnt++;
  }
  ArrayReverse(array, class="num">0, WHOLE_ARRAY);
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//Converting from Gray&class="macro">#x27;s code to a decimal number
class="type">class="kw">ulong GrayToDecimal(const class="type">char &grayCode [], class="type">int startInd, class="type">int endInd)
{
  class="type">class="kw">ulong grayCodeS = BinaryToInteger(grayCode, startInd, endInd);
  class="type">class="kw">ulong result = grayCodeS;
  class="kw">while ((grayCodeS >>= class="num">1) > class="num">0)
  {
    result ^= grayCodeS;
  }
  class="kw">return result;
}
class=class="str">"cmt">//Converting a binary class="type">class="kw">string to a decimal number

常见问题

二进制把每个参数切成固定位长的 0/1 串,搜索空间离散且边界清晰,能避免实数梯度在整数参数上乱跳;建议先把参数范围量化到 8~10 位再跑。
基础验证用种群 30~50、迭代 50~80 代即可看收敛趋势;外汇贵金属波动大,出参后务必用样本外数据复核,高风险品种减半仓位测试。
可以,小布盯盘的 AIGC 已内置这类编码优化流程,打开对应品种页即可让它替你跑搜索并标出概率占优的组合,你只管决策。
普通二进制相邻整数可能翻转多位,导致适应度突变跳变;格雷码保证相邻编码只差 1 位,能抹平搜索盲点,让遗传交叉更平滑。
实数编码对初值敏感且易陷连续谷底;换二进制+格雷码重排空间,配合轮盘赌选择,倾向更快跳出局部洼地。