种群优化算法:杜鹃优化算法(COA)·进阶篇
📘

种群优化算法:杜鹃优化算法(COA)·进阶篇

第 2/2 篇

◍ 布谷鸟算法的私有结构与初始化落点

这段 MQL5 代码把布谷鸟优化(COA)的核心状态收进了一个类的 private 区:巢穴数组、布谷鸟数量、巢数、蛋被宿主发现的概率 koef_pa,以及步长控制 koef_alpha。坐标维度用 coordinates 表示,优化参数有几维它就管几维,外汇或贵金属参数寻优时常见 3~10 维。 Init 函数负责把随机种子用 GetTickCount 打散,避免每次回测初始种群雷同;随后按 nestsP 和 cuckoosP 分别 ArrayResize 巢与布谷鸟数组,并给每个巢调用 Init(coordinates) 预分配坐标内存。注意 clutchEggs 初始为 false,eB 置为 -DBL_MAX,意味着最优适应度在首飞前尚未建立。 CuckooFlight 里若 clutchEggs 仍为 false,会先用 (rangeMax-rangeMin)*koef_alpha 算出每维步长 v[],再逐只布谷鸟、逐坐标准备位移。这一步直接决定搜索跨度,koef_alpha 给大易跳过极值,给小则收敛慢——开 MT5 把 koef_alpha 从 0.5 调到 1.0 能直观看到种群散开速度变化。外汇与贵金属杠杆品种波动剧烈,参数寻优结果仅代表历史样本倾向,实盘仍属高风险。

MQL5 / C++
class="kw">private: class="type">class="kw">double Scale(class="type">class="kw">double In, class="type">class="kw">double InMIN, class="type">class="kw">double InMAX, class="type">class="kw">double OutMIN, class="type">class="kw">double OutMAX, class="type">bool Revers);
class="kw">private: S_Nest nests [];      class=class="str">"cmt">//nests
class="kw">private: class="type">int    cuckoosNumber; class=class="str">"cmt">//number of cuckoos
class="kw">private: class="type">int    nestsNumber;   class=class="str">"cmt">//number of cuckoo nests
class="kw">private: class="type">class="kw">double koef_pa;       class=class="str">"cmt">//probability of detection of cuckoo eggs
class="kw">private: class="type">class="kw">double koef_alpha;    class=class="str">"cmt">//step control value
class="kw">private: class="type">class="kw">double v   [];
class="kw">private: class="type">int    coordinates;   class=class="str">"cmt">//coordinates number
class="kw">private: class="type">bool   clutchEggs;    class=class="str">"cmt">//clutch of eggs
};
class="type">void C_AO_COA::Init(const class="type">int     coordinatesP,  class=class="str">"cmt">//number of opt. parameters
                     const class="type">int     cuckoosP,      class=class="str">"cmt">//number of cuckoos
                     const class="type">int     nestsP,        class=class="str">"cmt">//number of cuckoo nests
                     const class="type">class="kw">double  koef_paP,      class=class="str">"cmt">//probability of detection of cuckoo eggs
                     const class="type">class="kw">double  koef_alphaP)   class=class="str">"cmt">//step control value
{
  MathSrand(GetTickCount());
  clutchEggs = false;
  eB         = -DBL_MAX;
  coordinates   = coordinatesP;
  cuckoosNumber = cuckoosP;
  nestsNumber   = nestsP;
  koef_pa       = koef_paP;
  koef_alpha    = koef_alphaP;
  ArrayResize(nests, nestsNumber);
  for (class="type">int i = class="num">0; i < nestsNumber; i++)
  {
    nests  [i].Init(coordinates);
  }
  ArrayResize(rangeMax,   coordinates);
  ArrayResize(rangeMin,   coordinates);
  ArrayResize(rangeStep, coordinates);
  ArrayResize(cB,         coordinates);
  ArrayResize(v, coordinates);
  ArrayResize(cuckoos, cuckoosNumber);
  for (class="type">int i = class="num">0; i < cuckoosNumber; i++)
  {
    ArrayResize(cuckoos [i].c, coordinates);
  }
}
class="type">void C_AO_COA::CuckooFlight()
{
  if (!clutchEggs)
  {
    for (class="type">int i = class="num">0; i < coordinates; i++) v [i] = (rangeMax [i] - rangeMin [i]) * koef_alpha;
    for (class="type">int i = class="num">0; i < cuckoosNumber; i++)
    {
      for (class="type">int c = class="num">0; c < coordinates; c++)

「布谷鸟搜索里的蛋位更新与寄生逻辑」

这段 C_AO_COA 类的代码实现了布谷鸟优化算法中两类关键动作:新蛋坐标的生成,以及寄生产卵时与宿主巢的胜负替换。前者在初始化或全局探索时直接用区间随机值铺满坐标,后者在迭代中利用莱维飞行步长做局部扰动。 当 clutchEggs 为 false 时,算法进入莱维飞行分支:对每个布谷鸟的每一维坐标,先取 [0,1] 均匀随机数 r1,大于 0.5 则步长方向为 +1 否则为 -1;再取 [1,20] 的 r2 作为幂次基,按 pow(r2,-2.0) 缩放当前维速度 v[c]。这种幂律尾分布让步长偶尔很大,倾向跳出局部劣解。 LayEggs 函数先把每只布谷鸟随机映射到某个巢(ind = round(RNDfromCI(0, nestsNumber-1))),若布谷鸟适应度 cuckoos[i].e 优于该巢 nests[ind].e,就整体拷贝坐标并刷新巢适应度;同时跟踪全局最优 eB 与 cB。否则布谷鸟直接继承巢的坐标,相当于被宿主同化。 巢的随机废弃由 koef_pa 控制:遍历巢时若均匀随机数小于该系数,就把对应巢适应度置为 -DBL_MAX,下一轮会被重新填充。在 MT5 里把 koef_pa 从 0.25 调到 0.1,可见收敛轨迹明显更黏滞,适合参数空间平坦的外汇均线组合寻优,但外汇与贵金属杠杆交易本身高风险,回测优解不代表实盘概率占优。

MQL5 / C++
  {
      cuckoos [i].c [c] = RNDfromCI(rangeMin [c], rangeMax [c]);
      cuckoos [i].c [c] = SeInDiSp(cuckoos [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
  clutchEggs = true;
}
else
{
  class="type">class="kw">double r1 = class="num">0.0;
  class="type">class="kw">double r2 = class="num">0.0;
  for (class="type">int i = class="num">0; i < cuckoosNumber; i++)
  {
    for (class="type">int c = class="num">0; c < coordinates; c++)
    {
      r1 = RNDfromCI(class="num">0.0, class="num">1.0);
      r1 = r1 > class="num">0.5 ? class="num">1.0 : -class="num">1.0;
      r2 = RNDfromCI(class="num">1.0, class="num">20.0);
      cuckoos [i].c [c] = cuckoos [i].c [c] + r1 * v [c] * pow(r2, -class="num">2.0);
      cuckoos [i].c [c] = SeInDiSp(cuckoos [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_COA::LayEggs()
{
  class="type">int ind = class="num">0;
  class=class="str">"cmt">//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  for (class="type">int i = class="num">0; i < cuckoosNumber; i++)
  {
    ind = (class="type">int)round(RNDfromCI(class="num">0.0, nestsNumber - class="num">1));
    if (cuckoos [i].e > nests [ind].e)
    {
      nests [ind].e = cuckoos [i].e;
      ArrayCopy(nests [ind].c, cuckoos [i].c, class="num">0, class="num">0, WHOLE_ARRAY);
      if (cuckoos [i].e > eB)
      {
        eB = cuckoos [i].e;
        ArrayCopy(cB, cuckoos [i].c, class="num">0, class="num">0, WHOLE_ARRAY);
      }
    }
    else
    {
      ArrayCopy(cuckoos [i].c, nests [ind].c, class="num">0, class="num">0, WHOLE_ARRAY);
    }
  }
  class=class="str">"cmt">//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
  for (class="type">int n = class="num">0; n < nestsNumber; n++)
  {
    if (RNDfromCI(class="num">0.0, class="num">1.0) < koef_pa)
    {
      nests [ind].e = -DBL_MAX;
    }
  }
}

杜鹃搜索在 EURUSD 回测台上的收敛表现

在 MT5 策略测试器里跑 Test_AO_COA_fast(EURUSD, M1),每个测试函数迭代 10000 次,结果很直白:1 个变量的 Skin 函数得分 1.00000,500 个变量的同函数得分掉到 0.14849;Megacity 函数从 1 变量满分跌到 500 变量时 0.03400。Levy 飞行杜鹃搜索在双变量平滑和离散函数上能拿到 100% 收敛,但变量一多,评分就崩。 原版算法有个毛病:行为像多变量随机游走,视觉上就是旧电视白噪声,只在迭代末尾才看出极值坐标集中。我加了一个坐标变更概率参数,让部分维度按概率保持不变,改名叫 COAm,重跑一遍。 改进后 All score for C_AO_COAm 从 0.50763 微升到 0.51256;500 变量 Forest 函数得分从 0.04321 提到 0.04551,Megacity 500 变量从 0.03400 到 0.03537。可视化里白噪声明显「晶化」,坐标集中中心开始移动。外汇与贵金属优化属高风险,参数敏感度可能随品种和周期漂移,上述数字仅在 EURUSD M1 该次回测中有效。 别把满分当泛化能力。杜鹃搜索在 1 变量函数满分,不代表千变量实盘能用;GWO 在 1000 变量 Skin 上仍领先,选算法得按自己项目的变量规模挑,而不是盯着总评分表。

◍ 别急着下结论

上面那张横评表里,杜鹃优化(COA)在 Skin、Forest、Megacity 三类测试函数上,2 参数场景拿满 1.00000,40 参数场景最佳 0.85911,1000 参数 Megacity 离散函数仍有 0.03537,综合均值 0.51255778,压过蚁群 0.49805222 和蜂群 0.46106556。它靠 Levy 飞行的无限方差跳跃做全局探索,比高斯随机游走更不容易卡在局部极值,这对 MT5 里参数维度动辄上百的 EA 寻优有直接价值。 不过表里的随机基线(RND)在 1000 参数 Megacity 也只有 0.02403,说明高维离散空间本身极难搜,COA 只是相对占优,不是通杀。实盘外汇和贵金属杠杆高、跳空频繁,任何优化器给出的参数集都只是历史样本上的概率倾向,须用 MT5 内置策略测试器多品种多周期重跑验证。 真要换引擎,MT5 目前并未原生暴露 COA 接口,得借 OnTester 自写或挂作者开源库;下面几行是已实现的其它元启发式切换调用,先跑通它们再谈替换。

MQL5 / C++
OptimizerSetEngine("ACO"); class=class="str">"cmt">// 蚂蚁群优化
OptimizerSetEngine("COA"); class=class="str">"cmt">// cuckoo 优化算法
OptimizerSetEngine("ABC"); class=class="str">"cmt">// 人工蜂群
OptimizerSetEngine("GWO"); class=class="str">"cmt">// 灰狼优化器
OptimizerSetEngine("PSO"); class=class="str">"cmt">// 粒子群优化

常见问题

至少存下当前蛋位坐标、适应度值和个体最优位置,用结构体打包后初始化时一次性清零再随机撒点,避免后续更新越界。
蛋位更新用莱维飞行步长扰动当前解,寄生逻辑按发现概率随机替换最差蛋,两部分拆成两个函数更利于调参和排错。
可以,小布内置了种群算法的回测台,你贴上参数范围它就能出收敛曲线和迭代耗时,不用自己搭环境。
常见在 80~150 代进入平台期,若 300 代还不收敛多半是步长系数或发现概率没调对,先查这两项。
外汇贵金属高风险,回测收敛只说明寻优稳,不代表行情适配,必须叠加样本外和滑点测试再评估概率优势。