种群优化算法:树苗播种和成长(SSG)算法·进阶篇
📘

种群优化算法:树苗播种和成长(SSG)算法·进阶篇

第 2/2 篇

◍ 遗传规划森林的初始播种逻辑

这段构造函数把一套类遗传算法的「森林」容器在 MT5 里先搭起来:用 GetTickCount 做随机种子,避免回测与实盘每次跑出同一串伪随机。seedlingsReplacement 参数若给到 ≥1.0 会直接等于树木总数,≤0.0 则强制为 1,中间值按 numberTreesP * 比例 取整,这决定了每轮迭代保留多少新苗。 数组维度全部按 coordinates(变量维度)和 numberTrees(树木数量)用 ArrayResize 展开,包括 pGarden / cGarden / gardenT 三类树群矩阵,以及每棵树内部的 c 坐标数组,初始适应度统一压到 -DBL_MAX。 首播标志 sowing 为 false 时进入第一次 planting:每棵树每个坐标先 RNDfromCI 在 [rangeMin, rangeMax] 内撒点,再过一道 SeInDiSp 按 rangeStep 离散化——这意味着你的搜索粒度直接受 rangeStep 控制,Step 设粗了可能跳过最优参数区。 播种后算一次欧氏半径 r = sqrt(Σ(vec[c]^2)),vec[c] 是各维上下界差,这个 r 后续会作为树木间距离度量的基准。外汇与贵金属 EA 里套这套,参数空间设宽了计算量指数涨,属高算力占用高风险玩法,实盘前务必在策略测试器跑样本。

MQL5 / C++
class="kw">const class="type">class="kw">double seedlingsReplacementP, class=class="str">"cmt">//Seedlings replacement
class="kw">const class="type">class="kw">double probabMatingOperatorP, class=class="str">"cmt">//Probability mating class="kw">operator
class="kw">const class="type">class="kw">double probabBranchOperatorP, class=class="str">"cmt">//Probability branching class="kw">operator
class="kw">const class="type">class="kw">double powerBranchOperatorP)  class=class="str">"cmt">//Power branching class="kw">operator
{
 MathSrand(GetTickCount());
 sowing = class="kw">false;
 fB     = -DBL_MAX;
 coordinates      = coordinatesP;
 numberTrees      = numberTreesP;
 if (seedlingsReplacementP >= class="num">1.0)
 {
  seedlingsReplacement = numberTreesP;
 }
 else
 {
  if (seedlingsReplacementP <= class="num">0.0)
  {
   seedlingsReplacement = class="num">1;
  }
  else seedlingsReplacement = class="type">int(numberTreesP * seedlingsReplacementP);
 }
 probabMatingOperator = probabMatingOperatorP;
 probabBranchOperator = probabBranchOperatorP;
 powerBranchOperator  = powerBranchOperatorP;
 ArrayResize(rangeMax,  coordinates);
 ArrayResize(rangeMin,  coordinates);
 ArrayResize(rangeStep, coordinates);
 ArrayResize(vec,       coordinates);
 ArrayResize(cB,        coordinates);
 ArrayResize(pGarden,  numberTrees);
 ArrayResize(cGarden,  numberTrees);
 ArrayResize(gardenT,  numberTrees);
 ArrayResize(ind,      numberTrees);
 ArrayResize(val,      numberTrees);
 for (class="type">int i = class="num">0; i < numberTrees; i++)
 {
  ArrayResize(pGarden  [i].c, coordinates);
  ArrayResize(cGarden  [i].c, coordinates);
  ArrayResize(gardenT  [i].c, coordinates);
  cGarden [i].f = -DBL_MAX;
 }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//the first planting of trees-------------------------------------------------
if (!sowing)
{
fB = -DBL_MAX;
r = class="num">0.0;
  for (class="type">int t = class="num">0; t < numberTrees; t++)
{
    for (class="type">int c = class="num">0; c < coordinates; c++)
    {
      cGarden [t].c [c] = RNDfromCI(rangeMin [c], rangeMax [c]);
      cGarden [t].c [c] = SeInDiSp(cGarden [t].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
    cGarden [t].f = -DBL_MAX;
  }
  for (class="type">int c = class="num">0; c < coordinates; c++)
  {
    vec [c] = rangeMax [c] - rangeMin [c];
    r += vec [c] * vec [c];
  }
  r = sqrt(r);
  class="kw">return;
}
class=class="str">"cmt">//tree growth-----------------------------------------------------------------
class="type">int child, parent;
class="type">class="kw">double rnd;
class="type">class="kw">double ed; class=class="str">"cmt">//euclidean distance
class="type">class="kw">double eM;
class="type">class="kw">double u;
class="type">class="kw">double temp;
for (class="type">int t = class="num">0; t < numberTrees; t++)
ed = class="num">0.0;
rnd = RNDfromCI(class="num">0.0, numberTrees - class="num">1);
child = (class="type">int)MathRound(rnd);

遗传树的配对与变异算子实现

这段逻辑是群体进化核心:先随机挑两棵树做父代与子代,越界就夹回 [0, numberTrees-1],若撞到同一棵则父代序号 +1 并回绕,保证交叉有意义。 交配算子先算两棵树在 coordinates 维上的欧氏距离 ed,再用 eM = 1 - ed/r 得到相似度,当随机数 ≤ eM 时,按 probabMatingOperator 概率把子代坐标替换为父代坐标,相似度越高越容易整维继承。 分支算子对每维以 probabBranchOperator 概率扰动:r1 取 ±1 决定方向,r2 在 1~20 间取倒数平方做衰减,偏移量 = vec[c] * powerBranchOperator * pow(r2,-2.0),最后用 SeInDiSp 夹回该维的 [rangeMin, rangeMax] 并按 rangeStep 离散化。 疫苗算子先算子代相对父代各维归一化偏差的均值 u,凡单维偏差 ≥ u 的直接回退成父代值,相当于把离群维度『接种』回父本,抑制无效发散。 非播种轮会把子代写回父群并排序,若冠军适应度 fB 被刷新就存进 cB;播种轮则只把排名前 seedlingsReplacement 棵子代替换父群末尾再排序,MT5 里调大 seedlingsReplacement 会明显加快种群换代节奏,但外汇与贵金属行情高风险,回测过拟合概率随之上升,需自己跑样本外验证。

MQL5 / C++
if (child < class="num">0) child = class="num">0;
if (child > numberTrees - class="num">1) child = numberTrees - class="num">1;
rnd = RNDfromCI(class="num">0.0, numberTrees - class="num">1);
parent = (class="type">int)MathRound(rnd);
if (parent < class="num">0) parent = class="num">0;
if (parent > numberTrees - class="num">1) parent = numberTrees - class="num">1;
if (child == parent) parent++;
if (parent > numberTrees - class="num">1) parent = class="num">0;
ArrayCopy(cGarden [t].c, pGarden [child].c, class="num">0, class="num">0, WHOLE_ARRAY);
class=class="str">"cmt">//mating class="kw">operator-----------------------------------------------------------
for (class="type">int c = class="num">0; c < coordinates; c++)
{
temp = pGarden [child].c [c] - pGarden [parent].c [c];
ed += temp * temp;
}
ed = sqrt(ed);
eM = class="num">1.0 - (ed / r);
rnd = RNDfromCI(class="num">0.0, class="num">1.0);
if (rnd <= eM)
{
  for (class="type">int c = class="num">0; c < coordinates; c++)
{
rnd = RNDfromCI(class="num">0.0, class="num">1.0);
    if (rnd <= probabMatingOperator) cGarden [t].c [c] = pGarden [parent].c [c];
  }
}
class=class="str">"cmt">//branching class="kw">operator--------------------------------------------------------
for (class="type">int c = class="num">0; c < coordinates; c++)
{
rnd = RNDfromCI(class="num">0.0, class="num">1.0);
  if (rnd < probabBranchOperator)
{
    class="type">class="kw">double r1 = RNDfromCI(class="num">0.0, class="num">1.0);
    r1 = r1 > class="num">0.5 ? class="num">1.0 : -class="num">1.0;
    class="type">class="kw">double r2 = RNDfromCI(class="num">1.0, class="num">20.0);
    cGarden [t].c [c] = cGarden [t].c [c] + r1 * vec [c] * powerBranchOperator * pow(r2, -class="num">2.0);
    cGarden [t].c [c] = SeInDiSp(cGarden [t].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
  }
}
class=class="str">"cmt">//vaccinating class="kw">operator------------------------------------------------------
u = class="num">0.0;

for (class="type">int c = class="num">0; c < coordinates; c++)
{
  u += fabs(cGarden [t].c [c] - pGarden [parent].c [c]) / vec [c];
}
u /= coordinates;
for (class="type">int c = class="num">0; c < coordinates; c++)
{
  if (fabs(cGarden [t].c [c] - pGarden [parent].c [c]) / vec [c] >= u)
  {
    cGarden [t].c [c] = pGarden [parent].c [c];
  }
}
if (!sowing)
{
  for (class="type">int t = class="num">0; t < numberTrees; t++) pGarden [t] = cGarden [t];
Sorting(pGarden);
  if (pGarden [class="num">0].f > fB)
{
fB = pGarden [class="num">0].f;
    ArrayCopy(cB, pGarden [class="num">0].c, class="num">0, class="num">0, WHOLE_ARRAY);
  }

sowing = true;
  class="kw">return;
}
class=class="str">"cmt">//planting some part from all child trees-------------------------------------
Sorting(cGarden);
if (cGarden [class="num">0].f > fB)
{
  fB = cGarden [class="num">0].f;
  ArrayCopy(cB, cGarden [class="num">0].c, class="num">0, class="num">0, WHOLE_ARRAY);
}
for (class="type">int t = class="num">0; t < seedlingsReplacement; t++) pGarden [numberTrees - seedlingsReplacement + t] = cGarden [t];
Sorting(pGarden);

「SSG 在 M1 回测台上的真实得分」

在 GBPUSD 的 M1 周期上跑 SSG 测试台,日志里直接吐出了一组可复核的数字。单看 Rastrigin 函数:5 个实例跑 10000 次得 77.397,Score 0.959;25 个实例得 52.245,Score 0.647;500 个实例得 0.254,Score 0.143。Forest 与 Megacity 离散函数也按 5/25/500 梯度下滑,全函数累计 All score 5.09109。 这套算法的参数就五个数:C_AO_SSG:50;0.3;0.5;0.4;0.1。NumberTrees_P=50 是树木数量,实验里没动默认值;若提到 100,聚合结果可能更好,但给定花园尺寸的迭代次数被压缩,缩放能力倾向下降。SeedlingsReplacement_P=0.3 管幼苗回迁比例,ProbabMatingOperator_P=0.5 是交叉概率,ProbabBranchOperator_P=0.4 为分杈概率,PowerBranchOperator_P=0.1 是枝杈强度——调到 1.0 以上枝杈会顶到花园边界,0.0 则退化为纯组合工具。 横向拉表看,SSG 在 14 个对比算法里最终结果 100.000 排第一,HS 是 93.370。最夸张的是 Megacity 离散函数:SSG 的 10 参数档 Score 1.00000,HS 同档 1.00000 但 50/1000 档掉到 0.41537,SSG 比最接近对手 HS 的最终档高出近 60%。Forest 上 SSG 也比最佳竞争者 ACOm 高出近 30%。 有意思的是,SSG 不直接用适应度去改决策树,只排子花园和父花园的序。看测试函数动画,树木几乎没有位移形态,只有局部极值处「丛生」,但收敛品质和稳定重现性都偏高。外汇与贵金属市场高风险,这类优化器结论仅代表历史测试环境,实盘迁移需自行在 MT5 跑同款日志验证。

◍ 记住这一条就够了

SSG 这套算法不挑目标函数的可微性或连续性,也不依赖代理个体的适应度或全局最优信息,所以能直接塞进 MT5 优化器去跑那些乱七八糟的非线性问题。作者在实验里观察到,树苗播种成长在收敛品质、结果稳定性和可扩展性上目前没有明显对手。 它优点很直白:实现门槛低,各类函数都能收敛,规模一拉大也不虚;缺点是可调参数偏多,虽然每个都还算直观。外汇和贵金属市场高波动、高杠杆,拿它做参数寻优只是提高概率,不代表实盘能躲过黑天鹅。 随文给的 15_The_world_of_AO_SSG.zip(约 120 KB)里含历次文章代码的当前版,下完丢进 MT5 的 MQL5/Files 解压,就能把自己 EA 的优化方法换成 SSG 试一把。

常见问题

按随机特征子集加轻量常数的方式播下初始种群,避免全零起点;可直接在回测台用默认播种参数先跑 50 代看多样性。
优先变异叶子常数值与单层算子类型,保留主干结构;变异率设在 0.05–0.15 区间较稳,过高易丢失有效逻辑。
小布可接管播种与配对流程,按你给的特征池自动生成森林并标注每代得分,你只需复核最优树结构。
M1 噪声大,SSG 易过拟合;建议先在 M5–M15 粗筛再下探 M1,且样本外窗口至少占 30%。
是,外汇贵金属杠杆高、跳空频繁,SSG 信号仅作概率参考,必须配套止损与仓位上限。