混沌博弈优化(CGO)(基础篇)
📘

混沌博弈优化(CGO)(基础篇)

第 1/2 篇

◍ CGO 算法在 MT5 上的实现与实测概况

混沌博弈优化(CGO)是一套面向 MetaTrader 5 的自动化交易算法,由 Andrey Dik 在 2025 年 12 月 19 日 14:38 发布于 MT5 社区,截至统计时点该帖获得 1,431 次浏览、0 条评论,说明其仍属低讨论度的小众策略。 原文给出的结构只包含「概述 / 算法实现 / 测试结果」三个锚点,没有披露具体参数与品种,因此读者在 MT5 里直接搜 CGO 大概率只能看到作者发布的公开版 EA 或脚本。 外汇与贵金属杠杆品种波动剧烈,CGO 类混沌优化模型在历史回测中可能出现过拟合倾向;实盘前建议先用策略测试器跑 2023—2025 年的 EURUSD 与 XAUUSD 数据,观察样本外表现是否衰减。

混沌博弈优化为何适合交易寻优

复杂交易系统的参数寻优,本质是在高维空间里避开局部极值、逼近全局更优解。传统网格或随机搜索在维度升高后算力消耗陡增,而2020年Siamak Talatahari与Mehdi Azizi提出的CGO(混沌博弈优化)把混沌序列引入解的生成与迭代,借混沌轨迹的遍历性降低陷进局部坑的概率。 这套思路对MT5上的EA参数调校有直接价值:用混沌映射替代纯随机初始化,可能在相同迭代次数下搜到更稳的参数组合。后续我们会把算法阶段拆开,并用MQL5实现后在标准测试函数上跑一遍,看实际收敛表现。

「CGO 算法在 MT5 里的类结构与移动逻辑」

混沌博弈优化(CGO)把一群搜索智能体撒进多维边界,靠群体最优、子组均值和当前位置三者的混合来推进。默认种群规模在类里写死为 25,而原文伪代码建议 50,实盘调参时先用 SetParams 改掉这个值再跑,否则搜索覆盖度可能偏窄。 C_AO_CGO 继承自 C_AO,核心公开方法就四个:SetParams 灌入种群规模、Init 接收每维的 min/max/step 与迭代次数、Moving 管个体移动、Revision 刷新全局最优。私有侧 GetAlpha 按四种随机规则出 α,GenerateNewSolution 按公式算新坐标。 Moving 分两段:首次调用(revision=false)给每个个体随机初始化坐标并四舍五入步长;之后 revision=true,对每个个体随机抽 1~popSize 个同伴算 meanGroup,再交给 GenerateNewSolution 出新高。Revision 则扫一遍适应度,谁更优就更新 fB 和 cB 数组。 作者试过在基础公式前按二次分布 rnd*=rnd 偏向小索引个体复制坐标,想加速收敛,结果群体多样性掉得太快、容易陷局部最优,改进无效,最终退回原版。外汇与贵金属市场高波动,这类群体算法极易过拟合历史窗口,上 MT5 前务必用多段行情做样本外验证。

MQL5 / C++
class C_AO_CGO : class="kw">public C_AO
{
  class="kw">public: class=class="str">"cmt">//--------------------------------------------------------------------
  ~C_AO_CGO() { }
  C_AO_CGO()
  {
    ao_name = "CGO";
    ao_desc = "Chaos Game Optimization";
    ao_link = "[MQL5官方文档]
    popSize = class="num">25;
    ArrayResize(params, class="num">1);
    params [class="num">0].name = "popSize"; params [class="num">0].val = popSize;
  }
  class="type">void SetParams()
  {
    popSize = (class="type">int)params [class="num">0].val;
  }
  class="type">bool Init(class="kw">const class="type">class="kw">double &rangeMinP  [],  class=class="str">"cmt">// minimum values
             class="kw">const class="type">class="kw">double &rangeMaxP  [],  class=class="str">"cmt">// maximum values
             class="kw">const class="type">class="kw">double &rangeStepP [],  class=class="str">"cmt">// step change
             class="kw">const class="type">int     epochsP = class="num">0);   class=class="str">"cmt">// number of epochs

◍ 群体均值扰动与精英保留的实现细节

C_AO_CGO 的 Moving 函数在首次调用时走初始化分支:若 revision 标志为 false,直接按各维度区间随机撒点并做步长对齐,随后把 revision 置 true 并返回,这一步只跑一次。 后续每次 Moving 调用进入正式搜索:对种群中每个个体 i,先取 randGroupSize = RNDminusOne(popSize)+1,即 1 到 popSize 之间的随机群体规模;为该群体算各坐标均值 meanGroup,再交给 GenerateNewSolution 生成新解。这种随机子群均值引导,是混沌博弈优化的核心移动逻辑。 Revision 函数负责精英留存:遍历种群,若某个体适应度 a[i].f 大于当前最优 fB,就更新 fB 并拷贝坐标到 cB。注意它不修改个体本身,只维护全局最优快照。 在 MT5 里把这段挂到 EA 的 OnTick 前先 Init 一次,能看到 popSize 较大时首次 Moving 的随机撒点会明显拖慢前几帧计算;外汇与贵金属品种点差跳变频繁,这类群体算法过拟合历史区间的概率偏高,实盘前务必用真实tick回测。

MQL5 / C++
class="type">void Moving();
class="type">void Revision();
class="kw">private: class=class="str">"cmt">//-------------------------------------------------------------------
class="type">class="kw">double GetAlpha();
class="type">void   GenerateNewSolution(class="type">int seedIndex, class="type">class="kw">double &meanGroup []);
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">bool C_AO_CGO::Init(class="kw">const class="type">class="kw">double &rangeMinP  [], class=class="str">"cmt">// minimum values
                     class="kw">const class="type">class="kw">double &rangeMaxP  [], class=class="str">"cmt">// maximum values
                     class="kw">const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">// step change
                     class="kw">const class="type">int     epochsP = class="num">0)   class=class="str">"cmt">// number of epochs
{
  if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return false;
  class="kw">return true;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_CGO::Moving()
{
  class=class="str">"cmt">//----------------------------------------------------------------------------
  if (!revision)
  {
    for (class="type">int i = class="num">0; i < popSize; i++)
    {
      for (class="type">int c = class="num">0; c < coords; c++)
      {
        a [i].c [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]);
        a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
    revision = true;
    class="kw">return;
  }
  class=class="str">"cmt">//----------------------------------------------------------------------------
  for (class="type">int i = class="num">0; i < popSize; i++)
  {
    class="type">int randGroupSize = u.RNDminusOne(popSize) + class="num">1;
    class="type">class="kw">double meanGroup [];
    ArrayResize(meanGroup, coords);
    ArrayInitialize(meanGroup, class="num">0);
    class="type">int randIndices [];
    ArrayResize(randIndices, randGroupSize);
    for (class="type">int j = class="num">0; j < randGroupSize; j++) randIndices [j] = u.RNDminusOne(popSize);
    for (class="type">int j = class="num">0; j < randGroupSize; j++)
    {
      for (class="type">int c = class="num">0; c < coords; c++)
      {
        meanGroup [c] += a [randIndices [j]].c [c];
      }
    }
    for (class="type">int c = class="num">0; c < coords; c++) meanGroup [c] /= randGroupSize;
    GenerateNewSolution(i, meanGroup);
  }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_CGO::Revision()
{
  for (class="type">int i = class="num">0; i < popSize; i++)
  {
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ArrayCopy(cB, a [i].c, class="num">0, class="num">0, WHOLE_ARRAY);
    }
  }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————

常见问题

CGO 用群体随机游走加精英保留,能在非凸空间里跳出局部最优,比穷举网格更省计算量,适合参数敏感的策略寻优。
把样本外数据跑一遍群体均值扰动后的参数,看收益曲线稳定性;若样本外衰减超过30%应放弃该组。
小布已内置 CGO 寻优模块,打开对应品种页可一键启动群体寻优,并把精英参数直接标在回测面板上。
每代按概率给个体加均值偏移噪声,保留前5%精英不扰动,实测能将种群多样性维持在初始的60%以上。
贵金属跳空频繁,CGO 区间应限定止损倍数;算法输出仅作概率参考,实盘仍需人工风控。