种群优化算法:鲸鱼优化算法(WOA)(基础篇)
📘

种群优化算法:鲸鱼优化算法(WOA)(基础篇)

第 1/2 篇

「鲸鱼优化算法在MT5里的落地起点」

鲸鱼优化算法(WOA)属于种群优化类方法,核心是用模拟座头鲸包围、气泡网捕食的群体行为去搜解空间。在MT5里它可以脱离具体交易逻辑,先作为一个通用优化器跑起来。 原文示例发布于2024年11月4日14:29,作者Andrey Dik,页面记录显示已有649次查看、2条评论,说明这类算法模块在量化社群里已有基本关注度。 对交易者而言,第一步不是急着接信号,而是把WOA当黑盒优化器,用历史数据验证它收敛到参数的速度是否比网格搜索更省时。外汇与贵金属杠杆高,任何优化结果都只是历史拟合,实盘前须在小布盯盘里做样本外复核。

从座头鲸捕食到元启发式寻优

座头鲸每天可消耗多达 1.4 公吨食物,却在冬季几乎停食,靠丰期积累的脂肪熬过禁食期。这种「富时囤、缺时扛」的节奏,本质是海洋环境不确定下的生存优化,而非随机游荡。 Mirjalili 与 Lewis 在 2016 年提出的鲸鱼优化算法(WOA),直接把这种捕食逻辑抽象成了数学寻优框架。鲸鱼代表解空间里的一个候选解,「气泡网」和「螺旋穿透」两种行为,分别对应包围猎物与螺旋逼近猎物两类位置更新机制。 在 WOA 中,狩猎不再是生物动作,而是寻找最优解的过程:解向当前最优解收缩,或沿对数螺旋向目标绕行。对做外汇 / 贵金属参数寻优的交易者来说,这类元启发式算法的高风险在于——它只保证在给定回测窗口里「可能」收敛,绝不承诺实盘稳健。

◍ 鲸群寻优怎么搬进EA里

座头鲸优化算法(WOA)把一群随机初始化的「鲸鱼」当作候选解,每轮挑出目标函数最优的那头当领导者,其余鲸鱼按它的位置更新。更新分两种模式:探索时绕全局最优随机搜,开发时直接朝最优靠拢。原版 WOA 容易陷局部最优、收敛偏慢,因此在改进版 WOAm 里我硬加了第四阶段「迁移」——随机改变个体位置,模拟鲸鱼换猎场,抗卡滞性明显提升。

WOAm 的核心节奏由 aKo 控制:aKo = 2.0 - epochNow * (2.0 / epochs),伴随 A = 2.0*aKo*r - aKo(r 为 -1~1 均匀随机数)。A 绝对值大于 1 走探索,小于 1 走开发;再叠一个 spiralProb 概率触发螺旋运动,公式 X = Xprev +Xprev-X*exp(b*l)*cos(2*M_PI*l),对应「气泡网」包抄。

下面这段是 WOAm 最底层的智能体结构与类骨架,直接在 MT5 里建新文件就能编译跑通。struct 只存旧坐标和旧适应度,Init 里 fPrev 设成 -DBL_MAX 保证任何评价值都能覆盖;类 C_AO_WOAm 默认 popSize=100,参数数组长度 4,继承基类 C_AO。 别把正态当圣经:迁移阶段用的 PowerDistribution 不是高斯,是幂分布长尾,正是它让远离全局解的个体仍有非零概率被翻出来,避免早熟收敛。开 MT5 把 popSize 改成 30 对比 100,回测曲线会告诉你种群规模对收敛速度的真实影响。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="keyword">class="kw">struct</span> S_WOA_Agent
{
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> cPrev []; <span class="comment">class=class="str">"cmt">//previous coordinates</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> fPrev;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//previous fitness</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">void</span> Init(<span class="keyword">class="type">int</span> coords)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ArrayResize</span> (cPrev, coords);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fPrev = -<span class="macro">DBL_MAX</span>;
&nbsp;&nbsp;&nbsp;&nbsp;}
};
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="keyword">class</span> C_AO_WOAm : <span class="keyword">class="kw">public</span> C_AO
{
&nbsp;&nbsp;<span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span>
&nbsp;&nbsp;~C_AO_WOAm() { }
&nbsp;&nbsp;C_AO_WOAm()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ao_name = <span class="class="type">class="kw">string">"WOAm"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;ao_desc = <span class="class="type">class="kw">string">"Whale Optimization Algorithm M"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;popSize = <span class="number">class="num">100</span>;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">//population size</span>
&nbsp;&nbsp;&nbsp;&nbsp;ArrayResize(<span class="keyword">params</span>, <span class="number">class="num">4</span>);
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">0</span>].name = <span class="class="type">class="kw">string">"popSize"</span>;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">params</span> [<span class="number">class="num">0</span>].val = popSize;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">1</span>].name = <span class="class="type">class="kw">string">"refProb"</span>;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">params</span> [<span class="number">class="num">1</span>].val = refProb;

「把鲸鱼优化参数接进实盘初始化」

上面这段是鲸鱼优化算法(WOA)封装类的尾部定义与 Init 实现,核心是把四个外部参数落进代理群。params[0] 到 params[3] 分别承载种群规模、refinement 概率、螺旋系数与螺旋概率,SetParams 里直接按索引取值赋给类成员,改一个外部输入就能换一整套寻优行为。 Init 函数先跑 StandardInit 做边界与步长校验,失败直接返回 false;通过后把 epochsP 存进 epochs、epochNow 归零,再用 ArrayResize 按 popSize 扩出 agent 向量,循环里给每个代理调 Init(coords)。popSize 若为 50,这里就实打实建出 50 个搜索个体。 Moving 里 epochNow 自增,未进入 revision 分支时,对每个代理、每个坐标维度用 u.RNDfromCI 在 [rangeMin[c], rangeMax[c]] 内撒随机初值。外汇与贵金属波动剧烈,这类群体寻优若直接接 EA 跑实盘,参数区间设宽了可能收敛慢、设窄了易陷局部极值,属高风险用法,建议先 MT5 策略测试器回测。

MQL5 / C++
params [class="num">2].name = "spiralCoeff"; params [class="num">2].val = spiralCoeff;
params [class="num">3].name = "spiralProb";  params [class="num">3].val = spiralProb;
}
class="type">void SetParams()
{
   popSize      = (class="type">int)params [class="num">0].val;
   refProb      = params      [class="num">1].val;
   spiralCoeff = params      [class="num">2].val;
   spiralProb  = params      [class="num">3].val;
}
class="type">bool 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
class="type">void Moving();
class="type">void Revision();
class=class="str">"cmt">//----------------------------------------------------------------------------
class="type">class="kw">double refProb;     class=class="str">"cmt">//refinement probability
class="type">class="kw">double spiralCoeff; class=class="str">"cmt">//spiral coefficient
class="type">class="kw">double spiralProb;  class=class="str">"cmt">//spiral probability
S_WOA_Agent agent []; class=class="str">"cmt">//vector
class="kw">private: class=class="str">"cmt">//-------------------------------------------------------------------
class="type">int  epochs;
class="type">int  epochNow;
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">bool C_AO_WOAm::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 false;
   class=class="str">"cmt">//----------------------------------------------------------------------------
   epochs   = epochsP;
   epochNow = class="num">0;
   ArrayResize(agent, popSize);
   for (class="type">int i = class="num">0; i < popSize; i++) agent [i].Init(coords);
   class="kw">return true;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_WOAm::Moving()
{
   epochNow++;
   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]);

常见问题

它模拟座头鲸的包围捕食、气泡网捕食和随机搜索三种行为,用数学模型把位置更新写成迭代公式,从而在解空间里找最优参数。
先定义好要优化的参数边界和适应度函数,再把鲸群位置初始化逻辑写进EA的OnInit里,跑几代看看收敛情况再决定实盘用不用。
小布可以读取你品种页的回测与波动诊断,把算法给出的参数组合标记出过拟合风险和高风险时段,你再决定是否接入实盘。
鲸鱼优化靠随机搜索和气泡网机制跳出局部洼地的概率略高,但样本少时仍可能早熟,建议多跑几组种群规模对比。
种群小于15时容易早熟收敛,搜到的止损止盈组合可能只适配历史片段,换周期就失效,贵金属和外汇都属高风险需谨慎验证。