珊瑚礁优化算法(CRO)·进阶篇
🪸

珊瑚礁优化算法(CRO)·进阶篇

(2/3)·标准CRO只靠自然竞争太慢?引入逆幂律分布后邻域搜索效率明显不同

偏理论 第 2/3 篇
很多交易者把仿生优化当成黑箱,直接套用原始珊瑚礁算法跑参数,结果在多维品种上卡在局部最优。忽略繁殖与淘汰机制的细节,往往让回测漂亮、实盘拉胯。外汇贵金属高杠杆下,这类误差会被放大成实亏。

珊瑚礁优化器的初始化与布点逻辑

C_AO_CRO 类的 Init 先把搜索区间、步长和父类的 StandardInit 接好,随后算出礁体总规模 totalReefSize = reefRows * reefCols。初始珊瑚数由 rho0 * totalReefSize 四舍五入得到,若超过 popSize 则截断为 popSize,这一步直接决定种群起跑密度。 InitReef 负责在礁体内随机撒点:用 do-while 找未被占用的位置,pos 越界会被夹回 [0, totalReefSize-1]。每个新珊瑚的坐标由 u.RNDfromCI 在 [rangeMin, rangeMax] 内抽取,再经 u.SeInDiSp 对齐到离散步长——这意味着你调 rangeStep 会切实改变参数组合的解空间粒度。 Moving 里有个 revision 开关:当 revision 为 false 时,函数会对 occupied 为 true 的所有礁位做首次适应度评估。外汇与贵金属市场高波动,这类基于种群的优化仅用于历史参数搜索,实盘表现可能存在概率性偏差,开 MT5 把 rho0 从 0.3 改到 0.6 能直观看到初始珊瑚数变化。

MQL5 / C++
class="type">bool C_AO_CRO::Init(class="kw">const class="type">class="kw">double &rangeMinP   [],   class=class="str">"cmt">// minimum search range
                         class="kw">const class="type">class="kw">double &rangeMaxP   [],   class=class="str">"cmt">// maximum search range
                         class="kw">const class="type">class="kw">double &rangeStepP [],   class=class="str">"cmt">// search step
                         class="kw">const class="type">int     epochsP = class="num">0)     class=class="str">"cmt">// number of epochs
{
  class=class="str">"cmt">// Standard initialization of the parent class
  if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return class="kw">false;
  class=class="str">"cmt">//----------------------------------------------------------------------------
  class=class="str">"cmt">// Calculate the reef total size
  totalReefSize = reefRows * reefCols;
  class=class="str">"cmt">// The number of starting corals should not exceed popSize
  class="type">int initialPopSize = (class="type">int)MathRound(rho0 * totalReefSize);
  if (initialPopSize > popSize) initialPopSize = popSize;
  class=class="str">"cmt">// Initialize the occupancy array and indices
  ArrayResize(occupied, totalReefSize);
  ArrayResize(reefIndices, totalReefSize);
  class=class="str">"cmt">// Fill the arrays with initial values
  for (class="type">int i = class="num">0; i < totalReefSize; i++)
  {
    occupied    [i] = class="kw">false;
    reefIndices [i] = -class="num">1;
  }
  class=class="str">"cmt">// Reef initialization
  InitReef();
  class="kw">return true;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_CRO::InitReef()
{
  class=class="str">"cmt">// Number of starting corals in the reef(based on rho0)
  class="type">int initialCorals = (class="type">int)MathRound(rho0 * totalReefSize);
  class=class="str">"cmt">// The number of starting corals should not exceed the population size
  if (initialCorals > popSize) initialCorals = popSize;
  class=class="str">"cmt">// Initialize initialCorals random positions in the reef
  for (class="type">int i = class="num">0; i < initialCorals; i++)
  {
    class="type">int pos;
    class=class="str">"cmt">// Look for a free position
    do
    {
      pos = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, totalReefSize));
      class=class="str">"cmt">// Protection against exceeding the array size
      if (pos < class="num">0) pos = class="num">0;
      if (pos >= totalReefSize) pos = totalReefSize - class="num">1;
    }
    class="kw">while (occupied [pos]);
    class=class="str">"cmt">// Create a new coral at the found position
    occupied [pos] = true;
    reefIndices [pos] = i;
    class=class="str">"cmt">// Generate random coordinates for a new coral
    for (class="type">int c = class="num">0; c < coords; c++)
    {
      class="type">class="kw">double coordinate = u.RNDfromCI(rangeMin [c], rangeMax [c]);
      a [i].c [c] = u.SeInDiSp(coordinate, rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_CRO::Moving()
{
  if (!revision)
  {
    class=class="str">"cmt">// Initial assessment of all corals in the reef
    for (class="type">int i = class="num">0; i < totalReefSize; i++)
    {
      if (occupied [i])

◍ 珊瑚礁算法的幼虫定居与修订循环

这段 CRO(珊瑚礁优化)实现里,Revision() 是每一代的核心驱动器:先扫一遍 totalReefSize 个礁位,把适应度 a[reefIndices[i]].f 优于当前全局最优 fB 的个体写进 cB,再走广播产卵、育雏、定居、无性繁殖、捕食五阶段。注意 larvae 数组按 totalReefSize * 2 预留空间,实际数量由 larvaCount 控制,避免每代反复分配内存。 LarvaSettling() 给单个幼虫最多 attemptsNum 次随机落位机会。每次用 MathFloor(u.RNDfromCI(0, totalReefSize)) 抽礁位,越界就跳过;若 occupied[pos] 为空,再在 popSize 范围内找一个未被 reefIndices 引用的空闲索引 newIndex,成功才落定。这种双重循环查重(j 扫礁位、i 扫种群)在礁规模大时偏慢,MT5 里跑 EURUSD 十五分钟数据、totalReefSize=200 时单代定居耗时可观,倾向用哈希或标记数组替换。 适应度计算被刻意移出类内,留到外部 FuncTests 统一评测,所以类里只管拓扑演进不管目标函数。外汇与贵金属参数优化属高风险实验,回测优解实盘可能漂移,验证前先在小样本周期跑通再放大。

MQL5 / C++
{
      class="type">int idx = reefIndices [i];
      if (idx >= class="num">0 && idx < popSize)
      {
        class=class="str">"cmt">// Calculating fitness does not require class="kw">using GetFitness()
        class=class="str">"cmt">// since it will be evaluated in external code(in FuncTests)
      }
    }
  }
  revision = true;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_CRO::Revision()
{
  class=class="str">"cmt">// Update the global best solution
  for (class="type">int i = class="num">0; i < totalReefSize; i++)
  {
    if (occupied [i] && a [reefIndices [i]].f > fB)
    {
      fB = a [reefIndices [i]].f;
      ArrayCopy(cB, a [reefIndices [i]].c, class="num">0, class="num">0, WHOLE_ARRAY);
    }
  }
  class=class="str">"cmt">// Form an array to store larvae
  S_AO_Agent larvae [];
  ArrayResize(larvae, totalReefSize * class="num">2); class=class="str">"cmt">// Allocate with reserve
  class="type">int larvaCount = class="num">0;
  class=class="str">"cmt">// Stage class="num">1: Broadcast Spawning
  BroadcastSpawning(larvae, larvaCount);
  class=class="str">"cmt">// Stage class="num">2: Brooding
  Brooding(larvae, larvaCount);
  class=class="str">"cmt">// Calculate the fitness function for each larva
  class=class="str">"cmt">// (will be executed in external code in FuncTests)
  class=class="str">"cmt">// Stage class="num">3: Larval settlement
  for (class="type">int i = class="num">0; i < larvaCount; i++)
  {
    LarvaSettling(larvae [i]);
  }
  class=class="str">"cmt">// Stage class="num">4: Asexual reproduction
  AsexualReproduction();
  class=class="str">"cmt">// Stage class="num">5: Depredation
  Depredation();
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">int C_AO_CRO::LarvaSettling(S_AO_Agent &larva)
{
  class=class="str">"cmt">// Try to settle the larva attemptsNum times
  for (class="type">int attempt = class="num">0; attempt < attemptsNum; attempt++)
  {
    class=class="str">"cmt">// Select a random position in the reef
    class="type">int pos = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, totalReefSize));
    class=class="str">"cmt">// Check that the position is within the array
    if (pos < class="num">0 || pos >= totalReefSize) class="kw">continue;
    class=class="str">"cmt">// If the position is free, populate the larva
    if (!occupied [pos])
    {
      class=class="str">"cmt">// Search for a free index in the agents array
      class="type">int newIndex = -class="num">1;
      for (class="type">int i = class="num">0; i < popSize; i++)
      {
        class="type">bool used = class="kw">false;
        for (class="type">int j = class="num">0; j < totalReefSize; j++)
        {
          if (reefIndices [j] == i)
          {
            used = true;
            class="kw">break;
          }
        }
        if (!used)
        {
          newIndex = i;
          class="kw">break;
        }
      }
      if (newIndex != -class="num">1)

「珊瑚虫占据与广播产卵的代码落点」

珊瑚礁优化(CRO)里,幼虫尝试定居的逻辑分两条路:位置空着就直接拷贝解并标记占用;已被占但幼虫适应度更高,则替换掉原珊瑚。两段都靠 ArrayCopy 搬移候选解向量,返回位置索引,失败统一回 -1。 广播产卵阶段先扫一遍 totalReefSize 收集 occupied 为 true 的索引,occupiedCount 为 0 就直接退出。Fb 参数决定参与广播的珊瑚比例:broadcastCount = MathRound(Fb * occupiedCount),下限钳到 1、上限不超 occupiedCount,避免空转或越界。 索引洗牌用 u.RNDfromCI(0, occupiedCount) 取随机位做交换,但每次都补了 j 与 i 的边界判断,这是原实现修掉数组越界隐患的地方。之后以步长 2 抽取相邻两只珊瑚当父母,idx1/idx2 再经 popSize 双重校验才进交叉,少一步就可能写烂种群数组。 在 MT5 里把 Fb 从默认 0.2 调到 0.8,同品种 EURUSD 回测中广播幼虫数会明显变多,种群多样性倾向上升,但单代计算耗时也可能翻倍——外汇与贵金属属高风险品种,参数改动须先在策略测试器验证过拟合。

MQL5 / C++
  {
      class=class="str">"cmt">// Copy the larva&class="macro">#x27;s solution
      ArrayCopy(a [newIndex].c, larva.c, class="num">0, class="num">0, WHOLE_ARRAY);
      a [newIndex].f = larva.f;
      class=class="str">"cmt">// Update information about the reef
      occupied [pos] = true;
      reefIndices [pos] = newIndex;
      class="kw">return pos;
    }
  }
  class=class="str">"cmt">// If the position is occupied, check if the larva is better than the current coral
  else
    if (occupied [pos] && reefIndices [pos] >= class="num">0 && reefIndices [pos] < popSize && larva.f > a [reefIndices [pos]].f)
    {
      class=class="str">"cmt">// The larva displaces the existing coral
      ArrayCopy(a [reefIndices [pos]].c, larva.c, class="num">0, class="num">0, WHOLE_ARRAY);
      a [reefIndices [pos]].f = larva.f;
      class="kw">return pos;
    }
  }
  class=class="str">"cmt">// If the larva failed to settle, class="kw">return -class="num">1
  class="kw">return -class="num">1;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_CRO::BroadcastSpawning(S_AO_Agent &larvae [], class="type">int &larvaCount)
{
  class=class="str">"cmt">// Find all occupied positions
  class="type">int occupiedIndices [];
  class="type">int occupiedCount = class="num">0;
  for (class="type">int i = class="num">0; i < totalReefSize; i++)
  {
    if (occupied [i])
    {
      ArrayResize(occupiedIndices, occupiedCount + class="num">1);
      occupiedIndices [occupiedCount] = i;
      occupiedCount++;
    }
  }
  class=class="str">"cmt">// Check if there are no occupied positions
  if (occupiedCount == class="num">0) class="kw">return;
  class=class="str">"cmt">// Select the Fb share for broadcast spawning
  class="type">int broadcastCount = (class="type">int)MathRound(Fb * occupiedCount);
  if (broadcastCount <= class="num">0) broadcastCount = class="num">1; class=class="str">"cmt">// At least one coral
  if (broadcastCount > occupiedCount) broadcastCount = occupiedCount;
  class=class="str">"cmt">// Shuffle the indices
  for (class="type">int i = class="num">0; i < occupiedCount; i++)
  {
    class=class="str">"cmt">// Register the array out-of-bounds problem
    class="type">int j = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, occupiedCount));
    class=class="str">"cmt">// Ensure that j is within the array bounds
    if (j >= class="num">0 && j < occupiedCount && i < occupiedCount)
    {
      class="type">int temp = occupiedIndices [i];
      occupiedIndices [i] = occupiedIndices [j];
      occupiedIndices [j] = temp;
    }
  }
  class=class="str">"cmt">// Form pairs and create offspring
  for (class="type">int i = class="num">0; i < broadcastCount - class="num">1; i += class="num">2)
  {
    if (i + class="num">1 < broadcastCount) class=class="str">"cmt">// Make sure there is a second parent 
    {
      class="type">int idx1 = reefIndices [occupiedIndices [i]];
      class="type">int idx2 = reefIndices [occupiedIndices [i + class="num">1]];
      if (idx1 >= class="num">0 && idx1 < popSize && idx2 >= class="num">0 && idx2 < popSize)
      {
        class=class="str">"cmt">// Initialize the larva

珊瑚礁算法的交叉与孵育实现

人工珊瑚礁优化(CRO)里,子代生成靠两条路径:双亲交叉与 occupied 位点的自繁殖。下面这段交叉逻辑取两个父代坐标均值,再叠加 ±0.1 倍参数区间的随机扰动,最后用 SeInDiSp 夹回离散合法网格。

MQL5 / C++
larvae [larvaCount].Init(coords);
class=class="str">"cmt">// Create a new larva as a result of crossover
for (class="type">int c = class="num">0; c < coords; c++)
{
  class=class="str">"cmt">// Simple crossover method: average of parents&class="macro">#x27; coordinates with a small mutation
  class="type">class="kw">double value = (a [idx1].c [c] + a [idx2].c [c]) / class="num">2.0 + u.RNDfromCI(-class="num">0.1, class="num">0.1) * (rangeMax [c] - rangeMin [c]);
  larvae [larvaCount].c [c] = u.SeInDiSp(value, rangeMin [c], rangeMax [c], rangeStep [c]);
}
class=class="str">"cmt">// Increase the larvae counter
larvaCount++;
逐行拆解:Init 先按维度 coords 分配幼虫内存;循环对每个维度取两父代 c[c] 平均,扰动幅度由该维区间长度缩放,避免越界;SeInDiSp 把连续值吸附到最小步长 grid 上,保证后续评估不踩非法参数。 Brooding 函数处理无性孵育:先扫 totalReefSize 收集 occupied 下标,数量为零直接 return。繁殖数按 (1-Fb)*occupiedCount 四舍五入,Fb 是不育率,下限锁 1、上限锁 occupiedCount,防止空转或超发。
MQL5 / C++
class="type">void C_AO_CRO::Brooding(S_AO_Agent &larvae [], class="type">int &larvaCount)
{
  class="type">int occupiedIndices [];
  class="type">int occupiedCount = class="num">0;
  for (class="type">int i = class="num">0; i < totalReefSize; i++)
  {
    if (occupied [i])
    {
      ArrayResize(occupiedIndices, occupiedCount + class="num">1);
      occupiedIndices [occupiedCount] = i;
      occupiedCount++;
    }
  }
  if (occupiedCount == class="num">0) class="kw">return;
  class="type">int broodingCount = (class="type">int)MathRound((class="num">1.0 - Fb) * occupiedCount);
  if (broodingCount <= class="num">0) broodingCount = class="num">1;
  if (broodingCount > occupiedCount) broodingCount = occupiedCount;
  for (class="type">int i = class="num">0; i < occupiedCount; i++)
  {
    class="type">int j = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, occupiedCount));
    if (j >= class="num">0 && j < occupiedCount && i < occupiedCount)
    {
      class="type">int temp = occupiedIndices [i];
      occupiedIndices [i] = occupiedIndices [j];
      occupiedIndices [j] = temp;
    }
  }
  for (class="type">int i = class="num">0; i < broodingCount; i++)
  {
    if (i < occupiedCount)
    {
      class="type">int idx = reefIndices [occupiedIndices [i]];
      if (idx >= class="num">0 && idx < popSize)
      {
        larvae [larvaCount].Init(coords);
        for (class="type">int c = class="num">0; c < coords; c++)
        {
          class="type">class="kw">double value = a [idx].c [c] + u.RNDfromCI(-class="num">0.2, class="num">0.2) * (rangeMax [c] - rangeMin [c]);
自繁殖变异幅度放到 ±0.2 倍区间,比交叉更激进,倾向在局部已占领点附近探得更远偏移。MT5 里把 Fb 从 0.3 调到 0.6,可观察幼虫池里交叉与孵育占比变化,外汇参数寻优属高风险实验,结果仅具概率意义。

MQL5 / C++
larvae [larvaCount].Init(coords);
class=class="str">"cmt">// Create a new larva as a result of crossover
for (class="type">int c = class="num">0; c < coords; c++)
{
  class=class="str">"cmt">// Simple crossover method: average of parents&class="macro">#x27; coordinates with a small mutation
  class="type">class="kw">double value = (a [idx1].c [c] + a [idx2].c [c]) / class="num">2.0 + u.RNDfromCI(-class="num">0.1, class="num">0.1) * (rangeMax [c] - rangeMin [c]);
  larvae [larvaCount].c [c] = u.SeInDiSp(value, rangeMin [c], rangeMax [c], rangeStep [c]);
}
class=class="str">"cmt">// Increase the larvae counter
larvaCount++;

class="type">void C_AO_CRO::Brooding(S_AO_Agent &larvae [], class="type">int &larvaCount)
{
  class="type">int occupiedIndices [];
  class="type">int occupiedCount = class="num">0;
  for (class="type">int i = class="num">0; i < totalReefSize; i++)
  {
    if (occupied [i])
    {
      ArrayResize(occupiedIndices, occupiedCount + class="num">1);
      occupiedIndices [occupiedCount] = i;
      occupiedCount++;
    }
  }
  if (occupiedCount == class="num">0) class="kw">return;
  class="type">int broodingCount = (class="type">int)MathRound((class="num">1.0 - Fb) * occupiedCount);
  if (broodingCount <= class="num">0) broodingCount = class="num">1;
  if (broodingCount > occupiedCount) broodingCount = occupiedCount;
  for (class="type">int i = class="num">0; i < occupiedCount; i++)
  {
    class="type">int j = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, occupiedCount));
    if (j >= class="num">0 && j < occupiedCount && i < occupiedCount)
    {
      class="type">int temp = occupiedIndices [i];
      occupiedIndices [i] = occupiedIndices [j];
      occupiedIndices [j] = temp;
    }
  }
  for (class="type">int i = class="num">0; i < broodingCount; i++)
  {
    if (i < occupiedCount)
    {
      class="type">int idx = reefIndices [occupiedIndices [i]];
      if (idx >= class="num">0 && idx < popSize)
      {
        larvae [larvaCount].Init(coords);
        for (class="type">int c = class="num">0; c < coords; c++)
        {
          class="type">class="kw">double value = a [idx].c [c] + u.RNDfromCI(-class="num">0.2, class="num">0.2) * (rangeMax [c] - rangeMin [c]);

◍ 无性繁殖与捕食的礁体更新逻辑

珊瑚礁优化(CRO)在 MT5 里落地时,无性繁殖阶段先扫一遍 totalReefSize 个网格位置,把 occupied[i] 为真的下标收进 occupiedIndices 数组;若 occupiedCount 为 0 直接 return,避免空礁做无用计算。 接着按适应度排序,取 budCount = MathRound(Fa * occupiedCount) 个最优珊瑚克隆,Fa 为繁殖比例参数,budCount 下限锁死为 1、上限不超 occupiedCount,保证至少出一株克隆且不会越界。 克隆体用 ArrayCopy 原样拷贝父代坐标参数 c 与适应度 f,再丢进 LarvaSettling 尝试占坑,这一步决定了优解在种群里的扩散速度,外汇与贵金属参数寻优时可能明显影响收敛节奏,但杠杆品类高风险,回测不过别硬上。 捕食函数 Depredation 以 Pd 概率触发,同样先捞 occupiedIndices,排序后把数组前后对调,让适应度最差的先面对淘汰——和前面繁殖形成「留优去劣」的闭环,开 MT5 把 Fa、Pd 调到 0.1~0.3 区间跑一遍就能看出礁体置换频率的差异。

MQL5 / C++
class="type">void C_AO_CRO::AsexualReproduction()
{
  class="type">int occupiedIndices [];
  class="type">int occupiedCount = class="num">0;
  for (class="type">int i = class="num">0; i < totalReefSize; i++)
  {
    if (occupied [i])
    {
      ArrayResize(occupiedIndices, occupiedCount + class="num">1);
      occupiedIndices [occupiedCount] = i;
      occupiedCount++;
    }
  }
  if (occupiedCount == class="num">0) class="kw">return;
  SortAgentsByFitness(occupiedIndices, occupiedCount);
  class="type">int budCount = (class="type">int)MathRound(Fa * occupiedCount);
  if (budCount <= class="num">0) budCount = class="num">1;
  if (budCount > occupiedCount) budCount = occupiedCount;
  for (class="type">int i = class="num">0; i < budCount; i++)
  {
    if (i < occupiedCount)
    {
      class="type">int idx = reefIndices [occupiedIndices [i]];
      if (idx >= class="num">0 && idx < popSize)
      {
        S_AO_Agent clone;
        clone.Init(coords);
        ArrayCopy(clone.c, a [idx].c, class="num">0, class="num">0, WHOLE_ARRAY);
        clone.f = a [idx].f;
        LarvaSettling(clone);
      }
    }
  }
}

oid C_AO_CRO::Depredation()
{
  if (u.RNDfromCI(class="num">0, class="num">1) < Pd)
  {
    class="type">int occupiedIndices[];
    class="type">int occupiedCount = class="num">0;
    for (class="type">int i = class="num">0; i < totalReefSize; i++)
    {
      if (occupied[i])
      {
        ArrayResize(occupiedIndices, occupiedCount + class="num">1);
        occupiedIndices[occupiedCount] = i;
        occupiedCount++;
      }
    }
    if (occupiedCount == class="num">0) class="kw">return;
    SortAgentsByFitness(occupiedIndices, occupiedCount);
    for (class="type">int i = class="num">0; i < occupiedCount / class="num">2; i++)
把多模态诊断交给小布盯盘
这些关于搜索空间结构与收敛行为的判断,小布盯盘的 AIGC 已内置在品种页的诊断模块里,打开对应页就能看到当前拟合倾向,你只管做决策。

常见问题

ρ₀过大意味着礁石早期被占满,幼虫可附着空位少,算法探索多样性下降,可能过早收敛到次优解。
群体产卵用交叉配对生成后代,偏向全局探索;体内受精用变异生成微变体,偏向局部开发,两者比例影响平衡。
小布盯盘内置了针对多模态目标的诊断视图,可呈现类似逆幂律邻域搜索的收敛特征,但策略执行仍需你确认参数边界。
逆幂律让少数高适应度珊瑚周边更频繁产生新解,概率上更聚焦有前景区域,复杂搜索空间里收敛更快。
多模态存在大量局部峰,传统CRO易分散,CROm的邻域机制强化了对已发现峰周围的精细开发。