群体算法的混合 -顺序结构和并行结构(基础篇)
把灰狼与臭虫算法塞进同一优化回路
群体智能算法在 MT5 策略优化里通常不是单打独斗。把两种元启发式算法做混合,常见有顺序结构与并行结构两类:顺序是一条跑完把结果喂给另一条,并行是各自独立搜索再合并候选解。 原文给出的示例发布于 2024 年 10 月 22 日,作者 Andrey Dik,截至发布页统计有 510 次浏览、0 条评论,属于低互动但偏底层的工程向内容。 对交易者而言,这种混合思路的价值不在「更玄」,而在用 GWO(灰狼)的收敛性与 COA(臭虫)的全局跳出能力互补,降低单算法早熟的概率。开 MT5 用自定义优化器接 EA 时,可先试顺序结构——实现成本低,出问题时容易定位是哪一段算法拖了后腿。
◍ 混合优化的三条技术路线
在 MT5 的种群优化框架里,把多种算法揉到一起通常只有三种落地方式。第一种是逻辑层融合:把不同算法的搜索策略嵌进同一套流程,比如前一篇里细菌觅食优化和遗传算法的结合,各自的能力互补,相当于一支风格混杂但步调统一的舞。 第二种是迭代接力:前期让一种算法跑,后期把棒交给另一种,总迭代次数不变,但知识和中间成果在阶段间平滑转移,像运动队分时段专注比赛。 第三种是并行作画:每种算法同时跑,每次迭代后合并各自的最佳结果,逐步拼出对问题空间更完整的理解。 我们之前的实验有个硬数据:在种群优化里强制把算法初始点放在全局最小点、目标却是全局最大点时,部分算法卡在最小点出不来、效率归零,而少数算法对起始位置依赖极小,早期就探出了空间,另一些则在后期 refinement。标准化后所有群体算法归到一个类,混合成本骤降,这给 MT5 策略优化留下了实打实的折腾空间。
「灰狼与布谷鸟的顺序及并行混跑实测」
把灰狼优化(GWO)和改良布谷鸟优化(COAm)拼到一起,思路是用 GWO 在前半段快速铺开搜索、靠 COAm 后半段用列维飞行细化极值。GWO 模拟狼群层级(α/β/δ/ω),收敛快但容易早熟;COAm 借寄生产卵逻辑不断替换劣解,适应性强但终点分散。实验给的总迭代资源是 10000 次,群体规模固定 50,ratio 设 0.5 表示两种算法各管一半 epoch。 顺序结构下,0–5000 代跑 GWO,到 5000 代把狼位置映射成杜鹃蛋交给 COAm 接着跑。测试台在 Hilly / Forest / Megacity 三类函数上各跑 10000 次:500 维 Megacity 得分仅 0.1072,全部函数总评分 3.55917(占满分 39.55%)。这数字和 COAm 单跑接近,改善微弱,说明狼群陷入局部极值后,杜鹃没接住接力棒。 并行结构不改类声明,只在 Moving 里同时驱动 AO1、AO2,再把两边最优代理互换。结果总评分降到 3.34807(37.20%),500 维 Forest 得 0.1686,反而略逊于顺序版。两种拼法都没打出 1+1>2,外汇或贵金属参数寻优若直接套用该混合,须警惕过拟合与局部收敛风险,MT5 上建议先单跑 COAm baseline 再决定是否混编。 下面这段是混合类构造声明,定下了 popSize=50、ratio=0.5 的默认槽位,继承 C_AO 后测试台无需改动即可调度: // 类声明继承自 C_AO class C_AO_GWO_COAm : public C_AO { public: //---- ~C_AO_GWO_COAm () { } C_AO_GWO_COAm () { ao_name = "GWO_COAm"; // 算法短名 ao_desc = "Grey Wolf Optimizer and Cuckoo Optimization Algorithm M"; // 描述 popSize = 50; // 群体大小 ratio = 0.5; // GWO 与 COAm 的 epoch 占比 ArrayResize (params, 2); // 参数数组扩到 2 个 params [0].name = "popSize"; params [0].val = popSize; // 注册 popSize }
class C_AO_GWO_COAm : class="kw">public C_AO { class="kw">public: ~C_AO_GWO_COAm() { } C_AO_GWO_COAm() { ao_name = "GWO_COAm"; ao_desc = "Grey Wolf Optimizer and Cuckoo Optimization Algorithm M"; popSize = class="num">50; class=class="str">"cmt">//population size ratio = class="num">0.5; class=class="str">"cmt">//the ratio of GWO and COAm ArrayResize(params, class="num">2); params [class="num">0].name = "popSize"; params [class="num">0].val = popSize; }
混合优化器的 epochs 切分逻辑
把 GWO 与 COAm 两套优化器塞进同一个 C_AO_GWO_COAm 类,核心靠一个 ratio 参数决定 epochs 怎么分。Init 里先按 epochGWO = int(epochCount * ratio) 算出灰狼优化占用的轮次,剩下 epochCOAm = epochCount - epochGWO 全给 COAm,两个子优化器各自用相同的 popSize 和搜索区间初始化。 Moving 函数按 epochNow 递增来切换:只要 epochNow 还小于 epochGWO,就只跑 AO1.Moving() 并把 AO1 的种群 a[i] 同步到外层,等于前段纯灰狼搜索;一旦越过 epochGWO 阈值,控制权交给 AO2,进入 COAm 阶段。 实盘调参时 ratio 设 0.5 意味着 100 轮 epochs 里前 50 轮 GWO、后 50 轮 COAm;外汇与贵金属市场波动结构多变,这种分段混合可能比单算法更快跳出局部最优,但参数敏感度高,建议在 MT5 策略测试器里用不同 ratio 做对比回测验证。
class="type">bool C_AO_GWO_COAm::Init(const class="type">class="kw">double &rangeMinP [], class=class="str">"cmt">//minimum search range const class="type">class="kw">double &rangeMaxP [], class=class="str">"cmt">//maximum search range const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">//step search const class="type">int epochsP = class="num">0) class=class="str">"cmt">//number of epochs { if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return class="kw">false; epochCount = epochsP; epochNow = class="num">0; epochGWO = class="type">int(epochCount *ratio); epochCOAm = epochCount - epochGWO; AO1.params [class="num">0].val = popSize; AO2.params [class="num">0].val = popSize; AO1.SetParams(); AO2.SetParams(); AO1.Init(rangeMinP, rangeMaxP, rangeStepP, epochGWO); AO2.Init(rangeMinP, rangeMaxP, rangeStepP, epochCOAm); class="kw">return true; } class="type">void C_AO_GWO_COAm::Moving() { epochNow++; if (epochNow < epochGWO) { AO1.Moving(); for (class="type">int i = class="num">0; i < popSize; i++) { a [i] = AO1.a [i]; } class="kw">return; } AO2.Moving(); if (epochNow == epochGWO) {