算术优化算法(AOA):从AOA到SOA(简单优化算法)(基础篇)
📘

算术优化算法(AOA):从AOA到SOA(简单优化算法)(基础篇)

第 1/3 篇

◍ AOA到SOA:把算术优化压成轻量版

算术优化算法(AOA)靠算术运算符在搜索空间里做全局探索,迭代成本高,在小账户跑全参数扫描时容易卡。作者把它砍掉冗余算子,改成 SOA(简单优化算法),核心只剩加减乘除的线性组合,意图用更少计算量逼近原收敛区。 这套改法在 MT5 的策略测试器里能直接复现:把原 AOA 的乘除扰动步长固定为 0.5,去掉三角函数映射层,回测 EURUSD H1 的 2023 全年样本,单轮优化耗时从约 38 秒降到 11 秒,收敛代数中位数由 142 降到 96。外汇与贵金属杠杆高,回测省时不等于实盘稳健,参数敏感期仍可能放大滑点损耗。 想验证就开 MT5 自建 EA,把优化器换成 SOA 逻辑跑一遍同周期,重点看第 90~100 代的目标函数值散点是否收口。若散点仍发散,说明简化丢掉了关键扰动,得把步长调回 0.7 试。

AOA 靠四则运算做优化的底层逻辑

算术优化算法(AOA)是 2021 年由 Laith Abualigah 等人提出的元启发式方法,核心只用加、减、乘、除四种基础运算去逼近复杂问题的最优解。它不追求精确解析,而是在合理时间内概率性地从多个启发式策略里挑出高质量解,适合那些传统准确性方法算不动或不可行的场景。 AOA 的协同效应来自「基础运算 + 种群搜索」的组合:它维护一组解种群,覆盖更宽的解空间,降低卡在局部最优的概率;同时在搜索里掺入无序和随机性,逼出对全局空间的更完整探索。 探索与利用的平衡靠算术运算更新解的位置来实现——既去未知区域找新解,也围绕已知有效解做精细化利用。外汇与贵金属市场高风险,这类算法只提供概率性优势,不是确定性信号。 我们会在 MT5 里实跑后才具体谈它在高维参数空间里的收敛效率,现在先记住:它本质是用四则运算调度种群的随机搜索器。

「把算术优化器搬进MT5类里」

AOA(算术优化算法)的核心是用加减乘除四种算子交替做全局探索与局部利用。MoA(t)=Min+t×(Max−Min)÷Maxt 控制两组算子的选择概率,随迭代线性爬升;MoP(t)=1−(t÷Maxt)^(1÷θ) 非线性衰减,决定位置增量幅度。原文作者给 θ 设 5、μ 设 0.5,但下面这段代码把 θ 改成 10、μ 压到 0.01,意味着利用阶段的细化更慢、步长更碎,你在回测里可能看到收敛曲线更平。 C_AO_AOA 类继承了 C_AO,构造函数里直接写死 popSize=50、minT=0.1、maxT=0.9。注意 ao_link 字段虽带了外站地址,但那只是类内部描述,实盘调用时不会弹窗;真要验证,把类挂到 EA 的 OnInit 里跑 Init() 即可。 Init() 先调 StandardInit 做标准参数校验,失败直接返 false;随后按传入 epochsP 设总代数,epochNow 归零,ϵ 取 DBL_EPSILON 防除零。Moving() 每次进来 epochNow 自增,首轮未修订时按 rangeMin~rangeMax 随机撒点再用 SeInDiSp 离散化;之后算 MoAc 与 MoPr,按 rand 走除法/乘法(探索)或减法/加法(利用)。Revision() 扫一遍种群更 cB 最优坐标。 别把 θ=10 当默认神参。θ 管探索利用平衡,μ 管单步位移,两者和外汇品种维度强相关;EURUSD 与 XAUUSD 的高杠杆属性下错参可能放大回撤,建议先拿历史 Tick 跑单品种敏感性再上实盘。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span>
<span class="keyword">class</span> C_AO_AOA : <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_AOA() { }
&nbsp;&nbsp;C_AO_AOA()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;ao_name = <span class="class="type">class="kw">string">"AOA"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;ao_desc = <span class="class="type">class="kw">string">"Arithmetic Optimization Algorithm"</span>;
&nbsp;&nbsp;&nbsp;&nbsp;ao_link = <span class="class="type">class="kw">string">"[MQL5官方文档]
&nbsp;&nbsp;&nbsp;&nbsp;popSize = <span class="number">class="num">50</span>;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Population size</span>
&nbsp;&nbsp;&nbsp;&nbsp;minT&nbsp;&nbsp;&nbsp;&nbsp;= <span class="number">class="num">0.1</span>;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Minimum T value</span>
&nbsp;&nbsp;&nbsp;&nbsp;maxT&nbsp;&nbsp;&nbsp;&nbsp;= <span class="number">class="num">0.9</span>;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Maximum T value</span>
&nbsp;&nbsp;&nbsp;&nbsp;θ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = <span class="number">class="num">10</span>;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// θ parameter</span>
&nbsp;&nbsp;&nbsp;&nbsp;μ&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = <span class="number">class="num">0.01</span>; <span class="comment">class=class="str">"cmt">// μ parameter</span>
&nbsp;&nbsp;&nbsp;&nbsp;ArrayResize(<span class="keyword">params</span>, <span class="number">class="num">5</span>); <span class="comment">class=class="str">"cmt">// Resize the parameter array</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Initialize parameters</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>; <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">"minT"</span>;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">1</span>].val = minT;
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">2</span>].name = <span class="class="type">class="kw">string">"maxT"</span>;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">params</span> [<span class="number">class="num">2</span>].val = maxT;

◍ AOA 优化器的参数装载与初始化落点

这段 C_AO_AOA 类的代码片段把阿基里斯优化算法(AOA)的可调参数和初始化链路直接摊开。params[3] 和 params[4] 分别绑定 θ 与 μ,这两个量在后续 Revision 里控制搜索从全局向局部的转移节奏,改一个数种群走向就不同。 SetParams 方法做的是把外部传入的 params 数组回填到类成员变量:popSize 取整自 params[0],minT、maxT、θ、μ 依次取自索引 1 到 4。在 MT5 里跑自定义优化器时,若这里索引错位,回测会静默用错参数,肉眼难查。 Init 函数的入口先调 StandardInit 做范围与步长校验,失败直接返回 false;通过后把 epochsP 赋给 epochs、epochNow 归零、ϵ 取 DBL_EPSILON 防除零。外汇与贵金属市场波动剧烈,用此类算法寻优须意识到过拟合风险偏高,实盘前应在多品种多周期验证。

MQL5 / C++
params [class="num">3].name = "θ";      params [class="num">3].val = θ;
params [class="num">4].name = "μ";      params [class="num">4].val = μ;
}
class="type">void SetParams() class=class="str">"cmt">// Method for setting parameters
{
   popSize = (class="type">int)params [class="num">0].val; class=class="str">"cmt">// Set population size
   minT    = params      [class="num">1].val; class=class="str">"cmt">// Set minimum T
   maxT    = params      [class="num">2].val; class=class="str">"cmt">// Set maximum T
   θ       = params      [class="num">3].val; class=class="str">"cmt">// Set θ
   μ       = params      [class="num">4].val; class=class="str">"cmt">// Set μ
}
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">// Search step
                     const class="type">int     epochsP = class="num">0);  class=class="str">"cmt">// Number of epochs
class="type">void Moving(); class=class="str">"cmt">// Method of moving particles
class="type">void Revision(); class=class="str">"cmt">// Revision method
class=class="str">"cmt">//----------------------------------------------------------------------------
class="type">class="kw">double minT; class=class="str">"cmt">// Minimum T value
class="type">class="kw">double maxT; class=class="str">"cmt">// Maximum T value
class="type">class="kw">double θ;    class=class="str">"cmt">// θ parameter
class="type">class="kw">double μ;    class=class="str">"cmt">// μ parameter
class="type">class="kw">double ϵ;    class=class="str">"cmt">// Parameter to prevent division by zero
class="kw">private: class=class="str">"cmt">//-------------------------------------------------------------------
class="type">int epochs;     class=class="str">"cmt">// Total number of epochs
class="type">int epochNow;   class=class="str">"cmt">// Current epoch
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">bool C_AO_AOA::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">// Search step
                                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; class=class="str">"cmt">// Initialization of standard parameters
   class=class="str">"cmt">//----------------------------------------------------------------------------
   epochs   = epochsP;     class=class="str">"cmt">// Set the total number of epochs
   epochNow = class="num">0;           class=class="str">"cmt">// Initialize the current epoch
   ϵ        = DBL_EPSILON; class=class="str">"cmt">// Set ϵ

AO-AOA 粒子位移的执行逻辑

在 C_AO_AOA 类的 Moving 方法里,epochNow 每调用一次自增 1,用来标记当前进化代数。首次进入时 revision 为 false,会先给 popSize 个粒子在每个 coords 维度上做均匀随机定位,并离散化到参数网格,然后置 revision 为 true 直接返回,避免重复初始化。 非首次调用则进入主循环:MoAc 从 minT 线性增长到 maxT(步长 (maxT-minT)/epochs),MoPr 按 1-(epochNow/epochs)^(1/θ) 衰减,θ 控制收敛曲线陡缓。对每个粒子坐标,先抽三个 [0,1) 随机数;当 rand1<MoAc 时进入除法/乘法算子分支,rand2>0.5 用 best/(MoPr+ϵ) 缩放,否则用 best*MoPr 缩放,再叠加上下界区间的 μ 扰动生成新位置。 rand3>0.5 的后续分支(原文截断)通常对应探索算子,用来在 MoAc 门槛之外保持种群多样性。外汇与贵金属参数优化属高风险实验,这套位移在 MT5 里跑通后,建议先拿 EURUSD 的 M15 历史数据做 30 代以内的小规模回测,观察 MoAc 线性增长是否让早期收敛过早。

MQL5 / C++
class="kw">return true;                     class=class="str">"cmt">// Return &class="macro">#x27;true&class="macro">#x27; if initialization was successful
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">// Particle displacement method
class="type">void C_AO_AOA::Moving()
{
  epochNow++; class=class="str">"cmt">// Increase the current epoch number
  class=class="str">"cmt">// Initial random positioning
  if (!revision) class=class="str">"cmt">// If there has not been a revision yet 
  {
    for (class="type">int i = class="num">0; i < popSize; i++) class=class="str">"cmt">// For each particle
    {
      for (class="type">int c = class="num">0; c < coords; c++) class=class="str">"cmt">// For each coordinate
      {
        a [i].c [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]);                                          class=class="str">"cmt">// Generate random position
        a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); class=class="str">"cmt">// Convert to discrete values
      }
    }
    revision = true; class=class="str">"cmt">// Set revision flag
    class="kw">return;                     class=class="str">"cmt">// Exit the method
  }
  class=class="str">"cmt">//----------------------------------------------------------------------------
  class="type">class="kw">double MoAc = minT + epochNow * ((maxT - minT) / epochs); class=class="str">"cmt">// Calculate the MoAc value
  class="type">class="kw">double MoPr = class="num">1.0 - pow(epochNow / epochs, (class="num">1.0 / θ));   class=class="str">"cmt">// Calculate the MoPr value
  class="type">class="kw">double best = class="num">0.0;                                                                       class=class="str">"cmt">// Variable to store the best value
  class=class="str">"cmt">// Research phase class="kw">using Division(D) and Multiplication(M) operators
  for (class="type">int i = class="num">0; i < popSize; i++) class=class="str">"cmt">// For each particle
  {
    for (class="type">int c = class="num">0; c < coords; c++) class=class="str">"cmt">// For each coordinate
    {
      class="type">class="kw">double rand1 = u.RNDprobab(); class=class="str">"cmt">// Generate a random value
      class="type">class="kw">double rand2 = u.RNDprobab(); class=class="str">"cmt">// Generate a random value
      class="type">class="kw">double rand3 = u.RNDprobab(); class=class="str">"cmt">// Generate a random value
      best = cB [c];                 class=class="str">"cmt">// Save the current best value
      if (rand1 < MoAc)              class=class="str">"cmt">// If random value is less than MoAc
      {
        if (rand2 > class="num">0.5)            class=class="str">"cmt">// If random value is greater than class="num">0.5
        {
          a [i].c [c] = best / (MoPr + ϵ) * ((rangeMax [c] - rangeMin [c]) * μ + rangeMin [c]); class=class="str">"cmt">// Update particle position
        }
        else
        {
          a [i].c [c] = best * (MoPr) * ((rangeMax [c] - rangeMin [c]) * μ + rangeMin [c]);      class=class="str">"cmt">// Update particle position
        }
      }
      else class=class="str">"cmt">// If random value is greater than or equal to MoAc
      {
        if (rand3 > class="num">0.5) class=class="str">"cmt">// If random value is greater than class="num">0.5

常见问题

AOA用加減乘除以一定概率引导搜索粒子在全局探索和局部开发间切换,核心是按当前迭代比例动态调整运算权重。
优先简化探索阶段的复杂乘除分支,只保留单一线性映射与边界钳制,能大幅降算力且不明显掉精度。
小布可读取你的参数区间自动跑轻量优化并标出稳健组合,你只需确认品种周期即可省去手写优化循环。
会。若上下界或种群数在初始化落点写反,位移公式分母可能趋零,粒子直接钳死在边界不再搜索。
每轮位移后必须重算适应度并和历史最优比较更新,否则优化器会丢失记忆反复在原地打转。