大气云模型优化(ACMO):实战·进阶篇
(2/3)· 上篇搭好云类骨架,本篇补完水滴随机分布与降雨更新全局解,并用测试函数看收敛表现
「云滴降水的区域映射与高斯采样」
这段逻辑把「云」的熵和超熵推进后,开始做降水(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 打印到注释框观察其随 α 的膨胀速度。
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] 的增量就能验证“残差归最低湿度云”不是空话。外汇与贵金属市场波动剧烈,此类算法仅作策略原型参考,实盘有较高风险。
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 用历史数据验证稳定性。
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 三个量就能直接看回测曲线斜率变化。外汇与贵金属杠杆品种波动剧烈,这类调度只影响搜索路径、不保证样本外收益,参数乱拉可能过拟合。
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); }