种群优化算法:灰狼优化器(GWO)(基础篇)
灰狼优化器在 MT5 里的定位
种群优化算法里,灰狼优化器(GWO)用狼群等级与围猎机制做参数搜索,相比网格遍历更省算力,适合 EA 里多参数寻优。 MT5 端已有作者 Andrey Dik 在 2023 年 4 月 11 日发布示例,原文记录浏览量 2071、评论 3 条,说明这类算法在量化社群有真实落地需求。 外汇与贵金属波动受杠杆与跳空影响,任何优化结果都只是历史样本拟合,实盘存在显著高风险,参数迁移前务必在策略测试器重跑。
「灰狼算法的阶层狩猎映射」
灰狼优化算法(GWO)由 Mirjalili 等人在 2014 年提出,本质是一类元启发式随机群体智能方法,其核心是把狼群狩猎行为转写成数学搜索流程。狼群被硬性划分为四个等级:阿尔法、贝塔、德尔塔、欧米茄,权重依次递减,欧米茄在群体里只配最后进食,对应解空间里最无关紧要的残差。 算法把当前最优解直接等同于阿尔法狼,第二、第三优解分别映射为贝塔和德尔塔,剩下所有候选解都是欧米茄。每完成一次位置更新,就重新评估谁离“猎物”(即目标函数值更优)最近,狼群等级当场重排,这种动态编队会一直跑,直到整群收敛到最小包围距离再发起攻击。 一次完整迭代拆成三步:搜索、包围、攻击。搜索阶段先辨认出阿尔法/贝塔/德尔塔这三匹离猎物最近的狼;其余个体要么跟着头狼包围,要么继续随机游走找更优位置。对外汇或贵金属参数寻优来说,这种机制大概率能跳出局部极值,但群体智能本身不保证收敛,实盘调参仍是高风险动作。
◍ 灰狼算法的阶层与三阶段狩猎逻辑
灰狼优化把解按质量分四层:最优的 α(阿尔法)、次优的 β(贝塔)、第三的 δ(德尔塔),其余皆归为 ω(欧米茄)。每层狼不是固定身份,而是每代按适应度重排后由数组位置决定,前段即主导者。 算法每代跑三个阶段:搜索、包围、攻击。核心是一个叫 a 的比率,它随迭代非线性递减直至 0。a>1 时猎物位置未知,狼群随机散布做全局搜索;a=1 切入攻击,a→0 时狼群收拢到猎物坐标附近,代表可能已逼近最优。 ω 狼的行为在所有世代一致:跟随当前 α/β/δ 位置的几何中心移动,但带一定随机偏离概率,偏离半径正比于 a。图例显示 α/β/δ 会按系数在随机方向偏离旧位,ω 则向它们中心靠。 我改写的版本比规范版多一个实用点:可设主导狼数量,上限为整个狼群规模。这对某些窄参数空间的优化任务可能更顺手。 代码里狼就是个轻量结构,只存坐标数组 c[] 和猎物值 p。狼群用 C_AO_GWO 类封装,声明了参数范围、步长、最佳坐标 cB 与最佳值 pB,以及每代调用的 TasksForWolves 和 RevisionAlphaStatus。InitPack 接收坐标数、狼数、主导狼数、总代数四个参数。 公开方法 TasksForWolves 是体量最大的主逻辑:首代 a>1 时依参数最大最小值随机发狼;后续代按 α/β/δ 算 ω 新位,表达式 Xn += wolves[abd].c[c] - Ai*(Ci*wolves[abd].c[c] - wolves[w].c[c]),其中 Ai=2.0*a*r1-a、Ci=2.0*r2,r1/r2 为 [0,1] 随机。主导者则在循环中围猎物转圈,远近似由 a 控制。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="kw">struct</span> S_Wolf { <span class="keyword">class="type">class="kw">double</span> c []; <span class="comment">class=class="str">"cmt">//coordinates</span> <span class="keyword">class="type">class="kw">double</span> p; <span class="comment">class=class="str">"cmt">//prey</span> }; <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class</span> C_AO_GWO <span class="comment">class=class="str">"cmt">//wolfpack</span> { <span class="comment">class=class="str">"cmt">//============================================================================</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> rangeMax []; <span class="comment">class=class="str">"cmt">//maximum search range</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> rangeMin []; <span class="comment">class=class="str">"cmt">//manimum search range</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> rangeStep []; <span class="comment">class=class="str">"cmt">//step search</span> <span class="keyword">class="kw">public</span>: S_Wolf wolves []; <span class="comment">class=class="str">"cmt">//wolves of the pack</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> cB []; <span class="comment">class=class="str">"cmt">//best prey coordinates</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> pB; <span class="comment">class=class="str">"cmt">//best prey</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">void</span> InitPack(<span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span> coordinatesP, <span class="comment">class=class="str">"cmt">//number of opt. parameters</span> <span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span> wolvesNumberP, <span class="comment">class=class="str">"cmt">//wolves number</span> <span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span> alphaNumberP, <span class="comment">class=class="str">"cmt">//alpha beta delta number</span> <span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span> epochCountP); <span class="comment">class=class="str">"cmt">//epochs number</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">void</span> TasksForWolves (<span class="keyword">class="type">int</span> epochNow); <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">void</span> RevisionAlphaStatus(); <span class="comment">class=class="str">"cmt">//============================================================================</span> <span class="keyword">class="kw">private</span>: <span class="keyword">class="type">void</span> ReturnToRange(S_Wolf &wolf); <span class="keyword">class="kw">private</span>: <span class="keyword">class="type">void</span> SortingWolves(); <span class="keyword">class="kw">private</span>: <span class="keyword">class="type">class="kw">double</span> SeInDiSp (<span class="keyword">class="type">class="kw">double</span> In, <span class="keyword">class="type">class="kw">double</span> InMin, <span class="keyword">class="type">class="kw">double</span> InMax, <span class="keyword">class="type">class="kw">double</span> Step);
灰狼优化器的成员结构与冷启动撒点
类里把狼群算法的关键状态都收在 private 段:coordinates 记优化参数维度,wolvesNumber 是种群规模,alphaNumber 控制头狼(α/β/δ)保留数量,epochCount 决定迭代轮数。临时数组 wolvesT、ind、val 专门给排序换狼用,searching 标志位区分「还没探过空间」和「迭代中」两种阶段。 InitPack 负责把外部传进来的维度、狼数、头狼数、迭代轮数落进成员变量,并用 ArrayResize 给 rangeMax/rangeMin/rangeStep/cB 以及每只狼的位置数组 c 按 coordinates 维度开好内存。注意 pB 和每只狼的 p 都先置为 -DBL_MAX,意味着初始最优适应度是负无穷,任何一次评估都会刷新它。 冷启动逻辑看 searching 标志:若为 false,就对所有狼的所有坐标调用 RNDfromCI 在 [rangeMin, rangeMax] 内随机撒点,再经 SeInDiSp 对齐到离散网格步长。撒完把 searching 置 true 并 return,此后才进入正式围猎迭代。外汇与贵金属参数优化属高风险场景,回测过拟合概率偏高,实盘前务必做样本外验证。 想验证这套初始化,把 wolvesNumber 设 30、coordinates 设 5,在 MT5 里跑 InitPack 后打印 wolves[w].c 数组,能看到 30 只狼各自落在参数区间内的离散坐标上。
class="kw">private: class="type">class="kw">double RNDfromCI(class="type">class="kw">double Min, class="type">class="kw">double Max); class="kw">private: class="type">int coordinates; class=class="str">"cmt">//coordinates number class="kw">private: class="type">int wolvesNumber; class=class="str">"cmt">//the number of all wolves class="kw">private: class="type">int alphaNumber; class=class="str">"cmt">//Alpha beta delta number of all wolves class="kw">private: class="type">int epochCount; class="kw">private: S_Wolf wolvesT []; class=class="str">"cmt">//temporary, for sorting class="kw">private: class="type">int ind []; class=class="str">"cmt">//array for indexes when sorting class="kw">private: class="type">class="kw">double val []; class=class="str">"cmt">//array for sorting class="kw">private: class="type">bool searching; class=class="str">"cmt">//searching flag }; class="type">void C_AO_GWO::InitPack(class="kw">const class="type">int coordinatesP, class=class="str">"cmt">//number of opt. parameters class="kw">const class="type">int wolvesNumberP, class=class="str">"cmt">//wolves number class="kw">const class="type">int alphaNumberP, class=class="str">"cmt">//alpha beta delta number class="kw">const class="type">int epochCountP) class=class="str">"cmt">//epochs number { MathSrand(GetTickCount()); searching = class="kw">false; pB = -DBL_MAX; coordinates = coordinatesP; wolvesNumber = wolvesNumberP; alphaNumber = alphaNumberP; epochCount = epochCountP; ArrayResize(rangeMax, coordinates); ArrayResize(rangeMin, coordinates); ArrayResize(rangeStep, coordinates); ArrayResize(cB, coordinates); ArrayResize(ind, wolvesNumber); ArrayResize(val, wolvesNumber); ArrayResize(wolves, wolvesNumber); ArrayResize(wolvesT, wolvesNumber); for (class="type">int i = class="num">0; i < wolvesNumber; i++) { ArrayResize(wolves [i].c, coordinates); ArrayResize(wolvesT [i].c, coordinates); wolves [i].p = -DBL_MAX; wolvesT [i].p = -DBL_MAX; } } class=class="str">"cmt">//space has not been explored yet, then send the wolf in a random direction if (!searching) { for (class="type">int w = class="num">0; w < wolvesNumber; w++) { for (class="type">int c = class="num">0; c < coordinates; c++) { wolves [w].c [c] = RNDfromCI(rangeMin [c], rangeMax [c]); wolves [w].c [c] = SeInDiSp(wolves [w].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } searching = true; class="kw">return; }