种群优化算法:灰狼优化器(GWO)(基础篇)
📘

种群优化算法:灰狼优化器(GWO)(基础篇)

第 1/2 篇

灰狼优化器在 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 控制。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="keyword">class="kw">struct</span> S_Wolf
{
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> c []; <span class="comment">class=class="str">"cmt">//coordinates</span>
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> p;&nbsp;&nbsp;&nbsp;&nbsp;<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>
{
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//============================================================================</span>
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> rangeMax&nbsp;&nbsp;[]; <span class="comment">class=class="str">"cmt">//maximum search range</span>
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> rangeMin&nbsp;&nbsp;[]; <span class="comment">class=class="str">"cmt">//manimum search range</span>
&nbsp;&nbsp;<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>
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: S_Wolf wolves&nbsp;&nbsp;&nbsp;&nbsp;[]; <span class="comment">class=class="str">"cmt">//wolves of the pack</span>
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> cB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[]; <span class="comment">class=class="str">"cmt">//best prey coordinates</span>
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> pB;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//best prey</span>
&nbsp;&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;coordinatesP,&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//number of opt. parameters</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;wolvesNumberP,&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//wolves number</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;alphaNumberP,&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//alpha beta delta number</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;epochCountP);&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//epochs number</span>
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="keyword">class="type">void</span> TasksForWolves&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<span class="keyword">class="type">int</span> epochNow);
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="keyword">class="type">void</span> RevisionAlphaStatus();
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//============================================================================</span>
&nbsp;&nbsp;<span class="keyword">class="kw">private</span>: <span class="keyword">class="type">void</span>&nbsp;&nbsp; ReturnToRange(S_Wolf &amp;wolf);
&nbsp;&nbsp;<span class="keyword">class="kw">private</span>: <span class="keyword">class="type">void</span>&nbsp;&nbsp; SortingWolves();
&nbsp;&nbsp;<span class="keyword">class="kw">private</span>: <span class="keyword">class="type">class="kw">double</span> SeInDiSp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(<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 只狼各自落在参数区间内的离散坐标上。

MQL5 / C++
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;
}

常见问题

它用狼群阶层和狩猎逻辑自动搜索最优参数组合,减少你手动穷举 EA 参数的时间,适合多变量寻优场景。
alpha、beta、delta 是头狼到三把手,负责引导包围猎物,omega 是普通狼只跟随前三只更新位置,不主导决策。
小布盯盘的 AIGC 已内置这类诊断与寻优辅助,打开对应品种页即可调用,把重复劳动交给小布你专注决策。
初始种群随机撒点只影响早期收敛速度,迭代中靠三阶段狩猎逻辑收拢,种群规模设大些可缓解漏优。
包围、追猎与骚扰、攻击收敛;前两步估算猎物方位,末步依距离收缩包围圈锁定最优解。