大气云模型优化(ACMO):实战·进阶篇
🌩️

大气云模型优化(ACMO):实战·进阶篇

(2/3)· 上篇搭好云类骨架,本篇补完水滴随机分布与降雨更新全局解,并用测试函数看收敛表现

含代码示例 第 2/3 篇
很多人把 ACMO 的云移动写完就直接拿去优化,忽略了水滴分布与降雨对全局解的更新,导致种群信息交换不充分、早熟收敛。本篇接着把这套气象过程补完,别让前半截代码空转。

「云滴降水的区域映射与高斯采样」

这段逻辑把「云」的熵和超熵推进后,开始做降水(RainProcess):非反向时所有云权重初始化为 1.0,反向时则按各坐标、各区域的湿度累加,缺失湿度用 minGp 兜底。 GetRegionIndex 用 floor 把价格点落到区间索引:regPos = (point - rangeMin[ind]) / ((rangeMax[ind] - rangeMin[ind]) / regionsNumber),越界则钳到 regionsNumber-1。这决定了云中心归属于哪个离散区域。 降水循环里,每颗云按 drops[i] 次数生成 droplet:以 clouds[i].entropy[c] 为半径,centre±dist 为边界,用 GaussDistribution(centre, xMin, xMax, hyperEntropy) 采样;若越出 [rangeMin, rangeMax] 就用 RNDfromCI 在边界与中心间回拉。外汇与贵金属行情下,这种采样对参数敏感,实盘前务必在 MT5 策略测试器跑一轮看分布偏移。 超熵上限被硬钳在 8(if hyperEntropy > 8 则置 8),避免高斯方差失控。想验证,可直接把 clouds[i].hyperEntropy 打印到注释框观察其随 α 的膨胀速度。

MQL5 / C++
clouds [i].center       [c] = u.SeInDiSp(clouds [i].center [c], rangeMin [c], rangeMax [c], rangeStep [c]);
clouds [i].regionIndex [c] = GetRegionIndex(clouds [i].center [c], c);
α = β;
clouds [i].entropy [c] *=(class="num">1 + α);
}
clouds [i].droplets     *=(class="num">1 - γ);
clouds [i].hyperEntropy *=(class="num">1 + α);
if (clouds [i].hyperEntropy > class="num">8) clouds [i].hyperEntropy = class="num">8;
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">int C_AO_ACMO::GetRegionIndex(class="type">class="kw">double point, class="type">int ind)
{
  class="type">int regPos = (class="type">int)floor((point - rangeMin [ind]) / ((rangeMax [ind] - rangeMin [ind]) / regionsNumber));
  if (regPos >= regionsNumber) regPos = regionsNumber - class="num">1;
  class="kw">return regPos;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_ACMO::RainProcess(class="type">bool &rev)
{
  class=class="str">"cmt">//to shed drops from every cloud----------------------------------------------
  class="type">class="kw">double cloud [];
  class="type">int    drops [];
  ArrayResize(cloud, cloudsNumber);
  ArrayResize(drops, cloudsNumber);
  if (!rev)
  {
    ArrayInitialize(cloud, class="num">1.0);
  }
  else
  {
    ArrayInitialize(cloud, class="num">0.0);
    class="type">class="kw">double humidity;
    for (class="type">int i = class="num">0; i < cloudsNumber; i++)
    {
      for (class="type">int c = class="num">0; c < coords; c++)
      {
        for (class="type">int r = class="num">0; r < regionsNumber; r++)
        {
          humidity = areas [c].regions [clouds [i].regionIndex [r]].humidity;
          if (humidity != -DBL_MAX) cloud [i] += humidity;
          else                      cloud [i] += minGp;
        }
      }
    }
  }
  DropletsDistribution(cloud, drops);
  class="type">class="kw">double dist   = class="num">0.0;
  class="type">class="kw">double centre = class="num">0.0;
  class="type">class="kw">double xMin   = class="num">0.0;
  class="type">class="kw">double xMax   = class="num">0.0;
  class="type">class="kw">double x      = class="num">0.0;
  class="type">int    dCNT   = class="num">0;
  for (class="type">int i = class="num">0; i < cloudsNumber; i++)
  {
    for (class="type">int dr = class="num">0; dr < drops [i]; dr++)
    {
      for (class="type">int c = class="num">0; c < coords; c++)
      {
        dist   = clouds [i].entropy [c];
        centre = clouds [i].center  [c];
        xMin   = centre - dist;
        xMax   = centre + dist;
        x = u.GaussDistribution(centre, xMin, xMax, clouds [i].hyperEntropy);
        if (x < rangeMin [c]) x = u.RNDfromCI(rangeMin [c], centre);
        if (x > rangeMax [c]) x = u.RNDfromCI(centre, rangeMax [c]);

◍ 云滴分配与代际修订的实现细节

这段逻辑把“湿度”映射成种群里的云滴数量,核心在 DropletsDistribution:先累加所有云的湿度得到 totalHumidity,再按 cloud[i]/totalHumidity*popSize 的比例把 popSize 个云滴分到各云。若除法取整后有剩余(remainingDrops = popSize - totalDrops),就全部塞进湿度最低的那朵云(indMinHumidity),保证总数恒定。 Revision 函数负责每代收尾:遍历 popSize 个个体,刷新全局最优 fB 与最差 minGp,若找到更优解就用 ArrayCopy 把其坐标写进 cB。随后调用 UpdateRegionProperties 重算区域湿度/气压,GenerateClouds 淘汰旧云并生新云,epochNow 自增。 GenerateClouds 开头用 CalculateHumidityThreshold 算湿度阈值 Ht,并用动态结构体数组 S_Areas 收集可成云的区域索引。coords 决定 ar 的大小,双层循环扫描 regionsNumber 个区域,为后续成云做统计铺垫。 在 MT5 里把 popSize 调到 200 以上时,比例分配的取整残差通常落在 0~regionsNumber 之间,直接观察 droplets[indMinHumidity] 的增量就能验证“残差归最低湿度云”不是空话。外汇与贵金属市场波动剧烈,此类算法仅作策略原型参考,实盘有较高风险。

MQL5 / C++
class="type">void C_AO_ACMO::DropletsDistribution(class="type">class="kw">double &cloud [], class="type">int &droplets [])
{
  class="type">class="kw">double minHumidity     = DBL_MAX;
  class="type">int    indMinHumidity = -class="num">1;
  class="type">class="kw">double totalHumidity  = class="num">0; class=class="str">"cmt">//total amount of humidity in all clouds
  for (class="type">int i = class="num">0; i < ArraySize(cloud); i++)
  {
    totalHumidity += cloud [i];
    if (cloud [i] < minHumidity)
    {
      minHumidity = cloud [i];
      indMinHumidity = i;
    }
  }
  class=class="str">"cmt">// Filling the droplets array in proportion to the value in clouds
  for (class="type">int i = class="num">0; i < ArraySize(clouds); i++)
  {
    droplets [i] = class="type">int((cloud [i] / totalHumidity)*popSize); class=class="str">"cmt">//proportional distribution of droplets
  }
  class=class="str">"cmt">// Distribute the remaining drops, if any
  class="type">int totalDrops = class="num">0;
  for (class="type">int i = class="num">0; i < ArraySize(droplets); i++)
  {
    totalDrops += droplets [i];
  }
  class=class="str">"cmt">// If not all drops are distributed, add the remaining drops to the element with the lowest humidity
  class="type">int remainingDrops = popSize - totalDrops;
  if (remainingDrops > class="num">0)
  {
    droplets [indMinHumidity] += remainingDrops; class=class="str">"cmt">//add the remaining drops to the lightest cloud
  }
}
class="type">void C_AO_ACMO::Revision()
{
  class="type">int ind = -class="num">1;
  for (class="type">int i = class="num">0; i < popSize; i++)
  {
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ind = i;
    }
    if (a [i].f < minGp) minGp = a [i].f;
  }
  if (ind != -class="num">1) ArrayCopy(cB, a [ind].c, class="num">0, class="num">0, WHOLE_ARRAY);
  UpdateRegionProperties(); class=class="str">"cmt">//updating humidity and pressure in the regions
  GenerateClouds(); class=class="str">"cmt">//disappearance of clouds and the creation of new ones
  revision = true;
  epochNow++;
}
class="type">void C_AO_ACMO::GenerateClouds()
{
  class="type">class="kw">double Ht = CalculateHumidityThreshold();
  class="kw">struct S_Areas
  {
        class="type">int regsIND []; class=class="str">"cmt">//index of the potential region
  };
  S_Areas ar [];
  ArrayResize(ar, coords);
  class="type">int sizePr = class="num">0;
  for (class="type">int i = class="num">0; i < coords; i++)
  {
    for (class="type">int r = class="num">0; r < regionsNumber; r++)
    {

云团溃散与湿度阈值的判定逻辑

这套 AO-ACMO 模型里,云团不是永久存在的。当某个坐标维度上的熵值超过初始熵的 5.0 倍,或者液滴数跌破 dMin 下限,就触发 cloudDecay,云团被视为溃散。 溃散后不是销毁,而是在高湿度区随机重生:用 RNDminusOne 从 regsIND 里挑区域索引,把中心拉回该区 x 值,再跑一遍 CalculateNewEntropy 重置熵。这样云团始终贴着市场湿度结构游走。 湿度阈值不是写死的。CalculateHumidityThreshold 会扫全部坐标与区域,抓出最小湿度 H_min 和上限 fB(即 H_max),返回 H_min + λ*(H_max - H_min)。λ 是你可以直接调的旋钮——调大则只有更湿的区域才被纳入,调小则放宽。 熵的重算用了个 S 形曲线:1/(1+2.72^(-(8-16*(t/maxT)))),随 epoch 推进从低走到高。外汇与贵金属市场高杠杆、易跳空,这类结构重算若接实盘须先开 MT5 用历史数据验证稳定性。

MQL5 / C++
if (areas [i].regions [r].humidity > Ht)
{
  sizePr = ArraySize(ar [i].regsIND);
  sizePr++;
  ArrayResize(ar [i].regsIND, sizePr, coords);
  ar [i].regsIND [sizePr - class="num">1] = r;
}
}
}
class=class="str">"cmt">//Check the conditions for cloud decay----------------------------------------
class="type">bool  cloudDecay = false;
for (class="type">int i = class="num">0; i < cloudsNumber; i++)
{
  cloudDecay = false;
  class=class="str">"cmt">//checking the cloud for too much entropy-----------------------------------
  for (class="type">int c = class="num">0; c < coords; c++)
  {
    if (clouds [i].entropy [c] > class="num">5.0 * clouds [i].entropyStart [c])
    {
      class=class="str">"cmt">//Print("Disintegration of cloud #", i, " - tore at epoch ", epochNow);
      cloudDecay = true;
      break;
    }
  }
  class=class="str">"cmt">//checking the cloud for decay----------------------------------------------
  if (!cloudDecay)
  {
    if (clouds [i].droplets < dMin)
    {
      class=class="str">"cmt">//Print("Disintegration of cloud #", i, " - dried up at epoch ", epochNow);
      cloudDecay = true;
    }
  }
  class=class="str">"cmt">//if the cloud has decayed--------------------------------------------------
  class="type">int regIND = class="num">0;
  if (cloudDecay)
  {
    class=class="str">"cmt">//creating a cloud in a very humid region---------------------------------
    for (class="type">int c = class="num">0; c < coords; c++)
    {
      regIND = u.RNDminusOne(ArraySize(ar [c].regsIND));
      regIND = ar [c].regsIND [regIND];
      clouds [i].center        [c] = areas [c].regions [regIND].x;
      clouds [i].regionIndex [c] = regIND;
    }
    CalculateNewEntropy(clouds [i], epochNow);
  }
}
}
class="type">class="kw">double C_AO_ACMO::CalculateHumidityThreshold()
{
  class="type">class="kw">double H_max = fB;
  class="type">class="kw">double H_min = DBL_MAX;
  for (class="type">int c = class="num">0; c < coords; c++)
  {
    for (class="type">int r = class="num">0; r < regionsNumber; r++)
    {
      if (areas [c].regions [r].humidity != -DBL_MAX)
      {
        if (areas [c].regions [r].humidity < H_min)
        {
          H_min = areas [c].regions [r].humidity;
        }
      }
    }
  }
  class="kw">return H_min + λ * (H_max - H_min);
}
class="type">void C_AO_ACMO::CalculateNewEntropy(S_ACMO_Cloud &cl, class="type">int t)
{
  class=class="str">"cmt">//En: class="num">1/(class="num">1+class="num">2.72^(-(class="num">8-class="num">16*(t/maxT))))
  for (class="type">int c = class="num">0; c < coords; c++)
  {

「退火曲线怎么写进 EA」

上面这段把信息熵和超熵都接到了随时间衰减的调度上。核心那句 pow(M_E, (-(8.0 - 16.0 * (t / epochs)))) 构成了一条从 e^8 到 e^-8 的指数过渡,训练早期熵权重接近 0、后期逼近 EnM0,等于给优化器一个先广搜后收敛的窗口。 He 那行注释写了 1/(1+2.72^((8-16*(t/maxT)))),和代码里的 hyperEntropy 公式一致;再用 u.Scale 把它从 [0,8] 线性映射到 [HeM0,8.0],避免后期超熵直接拍到 8.0 把学习率锁死。 在 MT5 里把这段塞进按代循环的 OnTester 或自定义训练函数,改 epochs 和 EnM0、HeM0 三个量就能直接看回测曲线斜率变化。外汇与贵金属杠杆品种波动剧烈,这类调度只影响搜索路径、不保证样本外收益,参数乱拉可能过拟合。

MQL5 / C++
  cl.entropy [c] = entropy [c] * EnM0 / (class="num">1.0 + pow(M_E, (-(class="num">8.0 - class="num">16.0 * (t / epochs)))));
  cl.entropyStart [c] = cl.entropy [c] = entropy [c];
}
class=class="str">"cmt">//----------------------------------------------------------------------------
class=class="str">"cmt">//He: class="num">1/(class="num">1+class="num">2.72^((class="num">8-class="num">16*(t/maxT))))
  cl.hyperEntropy = class="num">1.0 / (class="num">1.0 + pow(M_E, ((class="num">8.0 - class="num">16.0 * (t / epochs)))));
  cl.hyperEntropy = u.Scale(cl.hyperEntropy, class="num">0.0, class="num">8.0, HeM0, class="num">8.0);
}
让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到参数压测与收敛曲线,把重复劳动交给小布,你专注决策。

常见问题

移动消耗代表云团质量流失,乘衰减系数可避免虚假聚集,让低潜力区自然退出搜索,概率上更贴近气象熵增。
可以,小布盯盘的品种页支持导入外部优化结果做实时诊断,省去手动比对测试报告的环节。
它把高湿区(高潜力区)的采样权重拉高,使全局解更新偏向已探索的优区,但同时保留随机性以防局部最优。
随机初始化保证种群多样性,后续靠压力梯度引导,属于元启发式通用做法,外汇贵金属模型的高风险环境更忌过早偏置。