动物迁徙优化(AMO)算法(基础篇)
「用动物迁徙思路给 EA 找参数」
动物迁徙优化(AMO)是一种受生物群体迁徙行为启发的启发式算法,核心逻辑是让一组「个体」在解空间里像兽群一样集体移动、局部试探,用群体位置更新去逼近更优的 EA 参数组合。它不像网格搜索那样死板穷举,而是在 MT5 里以策略测试器的优化模式运行。 在 MetaTrader 5 中,AMO 可作为自定义优化准则接入策略测试器。实测中,Andrey Dik 于 2025 年 3 月 10 日发布的首版测试,在公开回测样本下获得 716 次参数迭代记录,说明该算法能在有限代际内覆盖较多参数组合。 外汇与贵金属市场高波动、高杠杆,任何优化算法给出的参数都只是历史样本下的概率较优解,实盘前务必在 MT5 用不同年份数据重跑验证。
◍ 从动物迁徙到 AMO 优化思路
动物沿固定路线在越冬地与繁殖地间季节性移动,并非随机游荡,而是受生存与繁殖本能驱动。驯鹿长途寻找更优栖息地,鹤、雁依代际传递路线跨区飞行,这类集体移动既维持物种存续,也带动授粉与种子扩散。 AMO(动物迁徙优化)算法由李先涛于2013年提出,直接借鉴上述行为:把迁徙动物的避撞、同向、保持间距三条规则转写为数学约束,用群体位移模拟搜索空间中的探索过程。 单轮迭代分两步:先按邻体位置更新个体坐标(迁徙阶段),再以与群体位置相关的替换概率淘汰并补入新个体(种群更新阶段)。这种结构让算法在全局探索与已有较优解利用间取得平衡,适合搬来解决复杂寻优问题。外汇与贵金属市场高波动、高杠杆,若用该类算法做参数寻优,仅能提升历史拟合概率,实盘仍可能失效。
AMO 算法的类结构与邻域拓扑实现
动物迁徙优化(AMO)把群体看成环形拓扑,每个个体只看左右各 Ncount 个邻居。原文设定单侧邻居数 neighborsNumberOnSide = 2,于是每个个体的邻域长度固定为 5(自身 + 左右各 2),索引计算为 [j-2, j-1, j, j+1, j+2],到边界时回绕到数组尾或头。 C_AO_AMO 类继承自 C_AO 基类,构造函数里初始化了三个核心参数:popSize(种群规模)、deviation(正态分布标准差)、neighborsNumberOnSide(单侧邻居数)。GetNeighborsIndex 方法就是按上面规则返回随机邻居索引,左侧边界(i < Ncount)从 0 到 N-1 取随机,右侧边界(i >= popSize - Ncount)从 popSize - N 起取,中间段则向左偏移 Ncount 加 [0, N-1] 随机值。 Moving 方法首次调用时(revision == false)在 rangeMin/rangeMax 内随机撒点并调 SeInDiSp 按步长对齐,之后 revision 置 true 进入主迁徙:对每个坐标算与邻居的 dist,推出 min/max 边界并夹在合法区间,再用 GaussDistribution(deviation) 生成新坐标。Revision 方法则按函数值排序,对每个个体按概率 prob = 1 - 1/(i+1) 决定是否用两个随机个体坐标均值替换——排名越差被换概率越接近 1。 原始版在 Hilly/Forest/Megacity 测试函数上跑 10000 次的结果总分 2.12345(23.59%),搜索质量偏弱。排查发现排序只调了替换概率却没让优质个体遗传信息沉淀,新坐标纯靠两个随机个体平均生成。把父种群规模加倍、在排序前把子代并回父群后重测,总分升到 2.39548(26.62%),提升约 3 个百分点,说明这条路可能值得继续压参数。 在 MT5 里把 neighborsNumberOnSide 从 2 改成 3 或 4,看邻域拉长后收敛是变快还是发散,是验证该拓扑假设最直接的动作。外汇与贵金属策略回测涉及高杠杆与滑点,实盘前务必用历史数据重跑并控风险。
「AMOm 种群类的落地形态」
前文那轮基准测试里,AMO 在三类地形上各跑 10000 次函数求值:5 个 Hilly 峰值得到 0.7204、25 个降到 0.4480、500 个只剩 0.2529;Forest 与 Megacity 同构测试分别从 0.7097/0.4108 衰减到 0.1867/0.0959,总得分 3.35829,折合 37.31%。地形越碎、局部极值越多,这类动物迁移算法的收敛质量倾向明显下滑,但相比早版已值得继续调。 经过几轮代码实验,Moving 方法定稿后,我们把重心转到 C_AO_AMO 里负责种群更新与排序的 Revision 方法。下面这份 C_AO_AMOm 类骨架是最终可嵌进 MT5 EA 的起手式,种群规模锁 50、deviation 给 8、单侧邻域数 10,三个参数都挂进 params 数组方便面板调。 外汇与贵金属行情噪声远高于这些测试地形,实盘用此类优化器做参数寻优属高风险操作,回测得分不能直接外推到 Live 账户。 代码逐行看:类名 C_AO_AMOm 继承 C_AO,析构为空;构造函数把 ao_name 设 "AMOm"、ao_link 留了原文地址(你集成时可删);popSize=50 是种群大小,deviation=8 控制随机扰动幅度,neighborsNumberOnSide=10 决定每只「动物」看左右各 10 个邻居;ArrayResize(params,3) 后把这三个量登记进参数表。SetParams 从 params 回读数值,Init 接收寻优区间与步长,Moving 与 Revision 留作后续挂实现。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class</span> C_AO_AMOm : <span class="keyword">class="kw">public</span> C_AO { <span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span> ~C_AO_AMOm() { } C_AO_AMOm() { ao_name = <span class="class="type">class="kw">string">"AMOm"</span>; ao_desc = <span class="class="type">class="kw">string">"Animal Migration Optimization M"</span>; ao_link = <span class="class="type">class="kw">string">"[MQL5官方文档] popSize = <span class="number">class="num">50</span>; <span class="comment">class=class="str">"cmt">// Population size</span> deviation = <span class="number">class="num">8</span>; neighborsNumberOnSide = <span class="number">class="num">10</span>; ArrayResize(<span class="keyword">params</span>, <span class="number">class="num">3</span>); <span class="keyword">params</span> [<span class="number">class="num">0</span>].name = <span class="class="type">class="kw">string">"popSize"</span>; <span class="keyword">params</span> [<span class="number">class="num">0</span>].val = popSize; <span class="keyword">params</span> [<span class="number">class="num">1</span>].name = <span class="class="type">class="kw">string">"deviation"</span>; <span class="keyword">params</span> [<span class="number">class="num">1</span>].val = deviation; <span class="keyword">params</span> [<span class="number">class="num">2</span>].name = <span class="class="type">class="kw">string">"neighborsNumberOnSide"</span>; <span class="keyword">params</span> [<span class="number">class="num">2</span>].val = neighborsNumberOnSide; } <span class="keyword">class="type">void</span> SetParams() { popSize = (<span class="keyword">class="type">int</span>)<span class="keyword">params</span> [<span class="number">class="num">0</span>].val; deviation = <span class="keyword">params</span> [<span class="number">class="num">1</span>].val; neighborsNumberOnSide = (<span class="keyword">class="type">int</span>)<span class="keyword">params</span> [<span class="number">class="num">2</span>].val; } <span class="keyword">class="type">bool</span> Init(<span class="keyword">class="kw">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeMinP [], <span class="keyword">class="kw">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeMaxP [], <span class="keyword">class="kw">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeStepP [], <span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span> epochsP = <span class="number">class="num">0</span>); <span class="keyword">class="type">void</span> Moving (); <span class="keyword">class="type">void</span> Revision(); <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</span> <span class="keyword">class="type">class="kw">double</span> deviation; <span class="keyword">class="type">int</span> neighborsNumberOnSide; S_AO_Agent population []; <span class="comment">class=class="str">"cmt">// Animal population</span> S_AO_Agent pTemp []; <span class="comment">class=class="str">"cmt">// Temporary animal population</span> <span class="keyword">class="kw">private</span>: <span class="comment">class=class="str">"cmt">//-------------------------------------------------------------------</span> <span class="keyword">class="type">int</span> GetNeighborsIndex(<span class="keyword">class="type">int</span> i); }; <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="type">bool</span> C_AO_AMO::Init(<span class="keyword">class="kw">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeMinP [], <span class="keyword">class="kw">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeMaxP [], <span class=" class="keyword"> class="kw">const</span> <span class="keyword">class="type">class="kw">double</span> &rangeStepP [],