辩证搜索(DA)·进阶篇
📘

辩证搜索(DA)·进阶篇

第 2/2 篇

◍ 代理寻优里的距离与坐标工具

Dragonfly Algorithm 在 MT5 里跑群体寻优,先得有一组底层辅助函数撑住几何运算。EuclideanDistance 算多维空间两点的欧氏距离,u.RNDfromCI 从给定区间抽随机数,u.SeInDiSp 把连续值按步长对齐到合法网格,u.RNDprobab 则返回均匀概率分布的随机量。 Revision 方法负责把每个代理找到的解往更优方向推。它用 for 循环扫一遍种群:若某代理适应度 a[i].f 大于全局最优 fB,就刷新全局解并记录代理索引 ind;同时每个代理的 f 若优于自身历史最佳,也把当前坐标 c 拷进个体最优 cB。 当 ind 不为 -1,说明出现了新全局最优,对应代理坐标被写进全局 cB。方法末尾建 aT 数组并按种群规模 resize,再调 u.Sorting_fB 依据 fB 对代理排座次,方便下一轮迭代直接挑头部个体。 下面这段 Moving 是代理在搜索空间里挪位置的核心。首次进入(revision 为 false)时随机撒点:每个代理各维先用 RNDfromCI 取区间随机值,再经 SeInDiSp 吸附到步长网格,之后置 revision=true 并返回。 后续调用则先更新‘最优思考者’(索引0):其新位置 = 自身历史最优 + 均匀随机数乘邻代理偏移,同样过一遍步长对齐。接着对投机思考者(k2 到 k1 区间)分别向前 k2 个、向后 k2 个邻居算欧氏距离,抓出最远的前邻与后邻,谁更远就选谁来驱动位移更新。外汇与贵金属参数寻优高风险,回测过拟合概率不低,建议开 MT5 用自己品种数据跑一遍验证。

MQL5 / C++
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">// Implement agent movement in the search space
class="type">void C_AO_DA::Moving()
{
  class=class="str">"cmt">//----------------------------------------------------------------------------
  class=class="str">"cmt">// Initialize the agents&class="macro">#x27; positions randomly
  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]);
        a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
    revision = true;
    class="kw">return;
  }
  class=class="str">"cmt">//----------------------------------------------------------------------------
  class=class="str">"cmt">//  Update the best thinker&class="macro">#x27;s position
  for (class="type">int c = class="num">0; c < coords; c++)
  {
    a [class="num">0].c [c] = a [class="num">0].cB [c] + u.RNDprobab() * (a [class="num">1].c [c] - a [class="num">0].c [c]);
    a [class="num">0].c [c] = u.SeInDiSp(a [class="num">0].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
  }
  class=class="str">"cmt">//----------------------------------------------------------------------------
  class="type">class="kw">double dist_next   = -DBL_MAX;   class=class="str">"cmt">// maximum distance to the next neighbor
  class="type">class="kw">double dist_prev   = -DBL_MAX;   class=class="str">"cmt">// maximum distance to the previous neighbor
  class="type">class="kw">double dist        = class="num">0.0;        class=class="str">"cmt">// current distance
  class="type">int    antiNextIND = class="num">0;          class=class="str">"cmt">// index of the most distant next neighbor
  class="type">int    antiPrevIND = class="num">0;          class=class="str">"cmt">// index of the most distant previous neighbor
  class="type">int    antiIND     = class="num">0;          class=class="str">"cmt">// selected index to update position
  class=class="str">"cmt">// Update the positions of speculative thinkers -------------------------------
  for (class="type">int i = k2; i < k1; i++)
  {
    class=class="str">"cmt">// Find the most distant previous neighbor
    for (class="type">int j = class="num">1; j <= k2; j++)
    {
      dist = EuclideanDistance(a [i].cB, a [i - j].cB, coords);
      if (dist > dist_prev)
      {
        dist_prev   = dist;
        antiPrevIND = i - j;
      }
    }
    class=class="str">"cmt">// Find the farthest next neighbor
    for (class="type">int j = class="num">1; j <= k2; j++)
    {
      dist = EuclideanDistance(a [i].cB, a [i + j].cB, coords);
      if (dist > dist_next)
      {
        dist_next = dist;
        antiNextIND  = i + j;
      }
    }
    class=class="str">"cmt">// Select the most distant neighbor to update position
    if (dist_prev > dist_next) antiIND = antiPrevIND;

「两类思考者的位置更新与最优解留存」

这段逻辑把群体拆成「投机型」和「务实型」两类 agent,各自用不同随机机制刷新坐标。投机型在循环里走 u.RNDbool(),意味着以 0/1 二值概率直接跳向反例个体的维度差值;被注释掉的 u.RNDprobab() 版本才是连续比例混合,实盘切换这两行能明显改变收敛抖动。 务实型从 k1 到 popSize 遍历,先随机抽两个投机型个体,用欧几里得距离比较远近,只朝更近的那个更新。它的坐标步进固定用 u.RNDprobab() 乘差值,比投机型更平滑,回测中这类写法在 30 维参数空间里通常收敛更稳定。 Revision() 负责把每次迭代的成果落袋:逐个 agent 比 f 和 fB,刷新全局最优 fB 与个人最优 cB,并用 ArrayCopy 把坐标存进 cB。最后按 fB 排序,静态数组 aT 承接排序结果,方便下一轮直接切分 k1。外汇与贵金属参数优化属高风险,过拟合概率偏高,建议 MT5 策略测试器里先跑 3 个月 Tick 数据验证。

MQL5 / C++
      else                    antiIND = antiNextIND;
      class=class="str">"cmt">// Update the speculative thinker&class="macro">#x27;s coordinates
      for (class="type">int c = class="num">0; c < coords; c++)
      {
        a [i].c [c] = a [i].cB [c] + u.RNDbool() * (a [antiIND].c [c] - a [i].c [c]);
        class=class="str">"cmt">//a [i].c [c] = a [i].cB [c] + u.RNDprobab() * (a [antiIND].c [c] - a [i].c [c]);
        a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
    class=class="str">"cmt">// Update the positions of practical thinkers --------------------------------
    for (class="type">int i = k1; i < popSize; i++)
    {
      class=class="str">"cmt">// Random selection of two speculative thinkers
      antiNextIND = u.RNDintInRange(class="num">0, k1 - class="num">1);
      antiPrevIND = u.RNDintInRange(class="num">0, k1 - class="num">1);
      if (antiNextIND == antiPrevIND) antiNextIND = antiPrevIND + class="num">1;
      class=class="str">"cmt">// Calculate distances to selected thinkers
      dist_next = EuclideanDistance(a [i].cB, a [antiNextIND].cB, coords);
      dist_prev = EuclideanDistance(a [i].cB, a [antiPrevIND].cB, coords);
      class=class="str">"cmt">// Select the closest thinker to update the position
      if (dist_prev < dist_next) antiIND = antiPrevIND;
      else                    antiIND = antiNextIND;
      class=class="str">"cmt">// Update the coordinates of the practical thinker
      for (class="type">int c = class="num">0; c < coords; c++)
      {
        a [i].c [c] = a [i].cB [c] + u.RNDprobab() * (a [antiIND].c [c] - a [i].c [c]);
        a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
  }
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">// Review and update the best solutions found
class="type">void C_AO_DA::Revision()
{
  class="type">int ind = -class="num">1;
  class=class="str">"cmt">// Update the best solutions found for each agent
  for (class="type">int i = class="num">0; i < popSize; i++)
  {
    class=class="str">"cmt">// Update the global best solution
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ind = i;
    }
    class=class="str">"cmt">// Update the agent&class="macro">#x27;s personal best solution
    if (a [i].f > a [i].fB)
    {
      a [i].fB = a [i].f;
      ArrayCopy(a [i].cB, a [i].c, class="num">0, class="num">0, WHOLE_ARRAY);
    }
  }
  class=class="str">"cmt">// Update the global best solution coordinates
  if (ind != -class="num">1) ArrayCopy(cB, a [ind].c, class="num">0, class="num">0, WHOLE_ARRAY);
  class=class="str">"cmt">// Sort agents by their best found solutions
  class="kw">static S_AO_Agent aT []; ArrayResize(aT, popSize);
  u.Sorting_fB(a, aT, popSize);
}

DA 跑分:布尔扰动带来的意外跃升

把辩证算法(DA)丢进 Hilly、Forest、Megacity 三组标准测试函数里各跑 1 万次,初版用 [0.0;1.0] 均匀随机数,总分只有 3.36185(折算 37.35%),在榜单里排不上号。 问题出在随机源:原实现误用了随机布尔值,坐标有 50% 概率保持原状、50% 概率被对立面坐标替换。这种非连续扰动反而更贴近「正题—反题」对立的设定,实测总分跳到 5.21560(57.95%),整体有效率提升约 20.6 个百分点。 细分来看,Forest 5 函数结果从 0.7626 升到 0.9816,Megacity 5 函数从 0.36 升到 0.7031;500 维高维场景下收益递减但仍优于初版。该版在 27 个算法横向对比中列第 12 位,特征是在全局探索与局部挖掘间靠「推测型 / 实践型思考者」分工维持平衡,收敛快但精度不算顶尖。 外汇与贵金属行情受流动性突变影响,这类优化器直接套用存在过拟合高风险;MT5 上验证时建议先用历史 tick 重跑上述 1 万次 func runs,确认随机布尔扰动逻辑后再考虑接入实盘信号过滤。

◍ 低分优化器的实测排序与随机游走基线

上面这张横评表继续列了第 28 到 45 号算法以及 RW 随机游走的指标表现,编号越靠后整体得分越低。AEFA 人工电场算法拿了 4.459 分(百分制 49.55),是这一区段里唯一仍接近 50 分的;往下 ABHA、ACMO、ADAMm 依次落在 44~46 分区间,衰减并不陡。 到了 36 号 ATAm 人工部落算法 M,总分掉到 3.832(42.58),再往后的 ASHA、ASBO 都已不足 41 分。MEC 思维进化计算 3.470 分(38.55),IWO 入侵性杂草优化 3.403 分(37.81),Micro-AIS 微型人工免疫系统 3.379 分(37.54),COAm、SDOm、NMm、BBBC 则依次滑到 35 分上下。 RW 随机游走作为无脑基线,三项主指标仅 0.48754 / 0.32159 / 0.25781,总分 2.348(26.09),比最差的 BBBC 还低近 9 分。做 MT5 EA 参数寻优时,若自写算法跑分掉到 RW 的两倍以下,基本可判定该优化器在样本外已无实用价值,外汇与贵金属品种波动结构复杂,这类低分寻优结果实盘失效概率偏高,建议直接换算法或缩参数域。

「把这条线请下神坛」

辩证算法(DA)把种群拆成推测型与实践型两类思考者,前者广撒网做全局探索,后者在优区深耕做局部利用,靠这种对立统一来平衡搜索与开发。它只有两个外部参数(不含种群规模),实现简单且跑得相当快,在小问题和大问题上都给出了不偏科的均匀表现。 测试台用 0–100 的评分梯度衡量算法,100 为理论上限;DA 在对比表里颜色渐变平稳,没显出明显优于或劣于同类方法的偏向。附带的 DA.zip(158.45 KB)里含 Test_AO_DA.mq5 等脚本,在 MT5 里直接跑就能复现那张直方图。 这套方法毕竟带着作者为贴近辩证逻辑而故意掺入的随机扰动,结果分散是硬伤,别把它当确定性圣杯。外汇与贵金属市场高波动、高杠杆,任何优化器都只是辅助筛参工具,真要上实盘前请在策略测试器里用自己的品种数据再验一遍。

常见问题

先用代理寻优里的距离函数确认变量空间是否归一化;未归一化时优先用标准化坐标,避免量纲差异压垮更新步长。
检查位置更新公式是否只留了高分代理;应同时保留历史最优与当前代最优,并加小概率随机扰动打破对称。
小布可替你批量跑代理寻优的诊断,直接标出距离工具误用和低分优化器拖后腿的环节,你只看结论调参。
实测中布尔扰动常带来意外跃升,低分优化器排序里随机游走基线最弱,加扰动后收敛评分明显优于纯游走。
高维稀疏且评测极慢的问题里,代理寻优开销大、两类思考者易同化,此时不如简化随机基线先探路。