大爆炸-大坍缩(BBBC)算法·进阶篇
「把宇宙模型搬进EA初始化与迭代」
这段 C_AO_BBBC 类的代码片段,把大爆炸—大坍缩的搜索逻辑直接塞进了 MT5 的自定义指标类里。初始化时先调 StandardInit 拉起基类,再把 epochs 和 epochNow 归零,并用 ArrayResize 给 centerMass 按坐标维度开内存,少了这步后面算质心会越界报错。 Moving 函数是每次 tick 的入口。第一次跑(revision 为 false)会按 popSize × coords 双层循环,用 u.RNDfromCI 在范围内撒随机点,再经 SeInDiSp 吸附到离散网格——相当于宇宙创世。之后 revision 置真并直接 return,不进坍缩计算。 非大爆炸周期(epochNow % bigBangPeriod != 0)走 Big Crunch:对每个坐标 c,用权重 1/fitness 做加权(fitness 下限钳在 1e-10 防除零),按 xc = Σ(1/fi)xi / Σ(1/fi) 求质心;若分母过小则回退到 cB[c]。新点由高斯扰动生成:newPoint = centerMass[c] + r * rangeMax[c] / epochNow,r 来自 u.GaussDistribution(0,-1,1,1),epochNow 越大扰动越小,搜索逐步收敛。 外汇与贵金属行情的高波动会让 fitness 跳变剧烈,质心可能被少数极差个体带偏,实盘前建议在策略测试器里把 bigBangPeriod 从 10 调到 20 观察收敛稳定性。
class=class="str">"cmt">// Initialize the base class if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return false; class=class="str">"cmt">//---------------------------------------------------------------------------- epochs = epochsP; epochNow = class="num">0; class=class="str">"cmt">// Allocate memory for arrays ArrayResize(centerMass, coords); class="kw">return true; } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_BBBC::Moving() { epochNow++; class=class="str">"cmt">// Starting initialization(Big Bang) if (!revision) { for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int c = class="num">0; c < coords; c++) { class=class="str">"cmt">// Generate random starting dots a [i].c [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]); class=class="str">"cmt">// Reduction to discrete search grid 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">// Big Crunch phase - big collapse if (epochNow % bigBangPeriod != class="num">0) { for (class="type">int c = class="num">0; c < coords; c++) { class="type">class="kw">double numerator = class="num">0; class="type">class="kw">double denominator = class="num">0; for (class="type">int i = class="num">0; i < popSize; i++) { class=class="str">"cmt">// Calculate weight as the inverse of the fitness function value class="type">class="kw">double fitness = MathMax(MathAbs(a [i].f), class="num">1e-10); class="type">class="kw">double weight = class="num">1.0 / fitness; class=class="str">"cmt">// Summation to calculate the center of mass using the equation class=class="str">"cmt">// xc = (Σ(class="num">1/fi)xi) / (Σ(class="num">1/fi)) numerator += weight * a [i].c [c]; denominator += weight; } class=class="str">"cmt">// Determine the coordinates of the center of mass centerMass [c] = denominator > class="num">1e-10 ? numerator / denominator : cB [c]; } for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int c = class="num">0; c < coords; c++) { class="type">class="kw">double r = u.GaussDistribution(class="num">0, -class="num">1.0, class="num">1.0, class="num">1); class=class="str">"cmt">// Generate a new point using the equation class=class="str">"cmt">// xnew = xc + r*xmax/k class="type">class="kw">double newPoint = centerMass [c] + r * rangeMax [c] / epochNow; class=class="str">"cmt">// Constrain within the allowed area and convert to grid a [i].c [c] = u.SeInDiSp(newPoint, rangeMin [c], rangeMax [c], rangeStep [c]); } } } class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">// Big Bang phase - big bang else { for (class="type">int i = class="num">0; i < popSize; i++)
◍ BBBC 与 RW 两类算子的移动与修订实现
在 MT5 里跑群体优化,核心就是两类动作交替:Moving 负责撒点探索,Revision 负责把当前最优解记下来。下面这段 C_AO_BBBC 和 C_AO_RW 的实现把这两件事拆得很清楚,直接能抄进 EA 的优化模块。 BBBC 的 Revision 先扫一遍种群,用 a[i].f > fB 找历史最佳适应度,命中就把索引存 bestInd,最后用 ArrayCopy 把该个体的坐标数组 cB 整体覆盖。RW 的 Revision 逻辑完全一致,只是变量名换成 ind,说明随机游走类算法也共用同一套“精英记录”机制。 Moving 的差异才值得盯。BBBC 首次进入靠 revision 标志做 Big Bang 全随机初始化,之后每个 epochNow++;若 epochNow % bigBangPeriod != 0 就走 Big Crunch 向最优坍缩。RW 的 Moving 则每次都重新 RNDfromCI 全空间随机,不保留坍缩节奏——这意味着 RW 探索更散、BBBC 收敛更有周期性。 GaussDistribution 调用里写死的第 4 个参数 8,是正态分布标准差的缩放档位,调小会让初始点更聚拢在 cB 附近,外汇参数寻优时容易早熟,贵金属多参数面里可酌情放大。 开 MT5 把这段代码挂到自定义 C_AO 基类下,改 bigBangPeriod 从默认周期试到 20,回看种群适应度曲线,能直接看出坍缩频率对黄金 H1 参数稳定的影响倾向。
{
for (class="type">int c = class="num">0; c < coords; c++)
{
a [i].c [c] = u.GaussDistribution(cB [c], rangeMin [c], rangeMax [c], class="num">8);
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="type">void C_AO_BBBC::Revision()
{
class="type">int bestInd = -class="num">1;
class=class="str">"cmt">// Find the best solution in the current population
for (class="type">int i = class="num">0; i < popSize; i++)
{
if (a [i].f > fB)
{
fB = a [i].f;
bestInd = i;
}
}
class=class="str">"cmt">// Update the best known solution
if (bestInd != -class="num">1) ArrayCopy(cB, a [bestInd].c, class="num">0, class="num">0, WHOLE_ARRAY);
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_RW::Moving()
{
for (class="type">int w = class="num">0; w < popSize; w++)
{
for (class="type">int c = class="num">0; c < coords; c++)
{
a [w].c [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]);
a [w].c [c] = u.SeInDiSp(a [w].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_RW::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 (ind != -class="num">1) ArrayCopy(cB, a [ind].c, class="num">0, class="num">0, WHOLE_ARRAY);
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_BBBC::Moving()
{
epochNow++;
class=class="str">"cmt">// Starting initialization(Big Bang)
if (!revision)
{
for (class="type">int i = class="num">0; i < popSize; i++)
{
for (class="type">int c = class="num">0; c < coords; c++)
{
class=class="str">"cmt">// Generate random starting dots
a [i].c [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]);
class=class="str">"cmt">// Reduction to discrete search grid
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">//--------------------------------------------------------------------------
for (class="type">int i = class="num">0; i < popSize; i++)
{
class=class="str">"cmt">//Big Crunch phase - big collapse
if (epochNow % bigBangPeriod != class="num">0)
{
for (class="type">int c = class="num">0; c < coords; c++)
{
class=class="str">"cmt">// Calculate the size of the search space for the current coordinate
class="type">class="kw">double range = rangeMax [c] - rangeMin [c];爆炸与微调两种粒子生成逻辑
这段代码实现了类 Big Bang - Big Crunch 优化里两类粒子坐标的生成分支。普通迭代阶段用高斯扰动做局部探索,大爆炸阶段则用宽高斯直接重撒,两者都最终约束到离散网格。 先看非大爆炸分支:r = u.GaussDistribution(0, -1.0, 1.0, 1) 取均值 0、边界 ±1、精度 1 的标准高斯随机数,newPoint = cB[c] + r * range / epochNow 让扰动幅度随 epochNow 增大而衰减,越往后搜得越细。 大爆炸分支里 a[i].c[c] = u.GaussDistribution(cB[c], rangeMin[c], rangeMax[c], 8) 把均值设为当前最优 cB[c]、边界拉满到参数上下限、精度提到 8,相当于在全局范围内粗撒一波候选解,避免陷在局部。 两个分支末尾都调 u.SeInDiSp 把连续值夹回 [rangeMin, rangeMax] 并按 rangeStep 量化,MT5 里跑这套时若 rangeStep 设得过大,可能丢掉精度 8 带来的细粒度优势,建议先拿单参数区间手测一轮。
class=class="str">"cmt">// Generate a random number in the range [-class="num">1, class="num">1] class="type">class="kw">double r = u.GaussDistribution(class="num">0, -class="num">1.0, class="num">1.0, class="num">1); class=class="str">"cmt">// Generate a new point using the equation class=class="str">"cmt">// xnew = xc + r*(xmax - xmin)/(k) class="type">class="kw">double newPoint = cB [c] + r * range / epochNow; class=class="str">"cmt">// Constrain within the allowed area and convert to grid a [i].c [c] = u.SeInDiSp(newPoint, rangeMin [c], rangeMax [c], rangeStep [c]); } } class=class="str">"cmt">// Big Bang phase - big bang else { for (class="type">int c = class="num">0; c < coords; c++) { a [i].c [c] = u.GaussDistribution(cB [c], rangeMin [c], rangeMax [c], class="num">8); a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
「BBBC 调参后的实测表现」
把 BBBC 算法重新调过之后,跑了一组固定基准:Hilly、Forest、Megacity 三类测试函数,每类各取 5 / 25 / 500 三个维度,函数调用次数统一锁在 10000 次。结果直接看数值——5 Hilly 得 0.6053,500 Hilly 掉到 0.3126;5 Megacity 还有 0.3977,500 Megacity 只剩 0.1129,维度一上去精度就被吃掉一大截。 总分算下来是 3.15688,折算 35.08%,在排名表排第 43 名。随机游走(RW)被当成搜索策略有效性的下限基准,BBBC 虽然比裸 RW 强,但离榜首 ANS 的 6.134(68.15%)差了快一倍。 可视化上有一点值得注意:解的分布还是老样子呈“星状”,但搜索重心已经从单纯往空间中心挤,转向了真正有潜力的区域。这对做参数寻优的 MT5 EA 来说是个信号——同样跑群体智能,焦点偏移可能比迭代次数更影响实盘稳定性。 外汇和贵金属市场高波动、高杠杆,任何优化算法回测好看都不等于实盘能扛,拿去跑之前先在策略测试器里复一遍上面这组 10000 次调用设定。
◍ 十种优化器在EURUSD上的回测排名
上面这张表把 9 种元启发式优化器加上随机游走(RW)在 EURUSD 测试中的核心指标摊开了。COAm(布谷鸟优化)以总评分 3.349、盈利因子 37.21 排第一,SDOm 和 NMm 紧随其后,评分分别为 3.280 和 3.233。 注意 BBBC 那行原始数据用了逗号做小数点(0,60531 等),在 MT5 里直接塞进 CSV 会解析报错,导入前必须批量替换成点号。 RW 作为基线对照组总评分只有 2.348、盈利因子 26.09,比任何带 M 标记(代表带变异机制)的优化器都低,说明在外汇高噪声环境下纯随机搜索确实偏弱。贵金属与外汇品种波动剧烈,回测优不代表实盘稳,开 MT5 用同一周期跑一遍上述任意优化器做交叉验证最实在。
收束
把 BBBC 放回种群优化算法全家桶里看,它的外部参数只有种群规模一个,实现简单、EA 跑得飞快,在大规模问题上确实能打;但低维场景下结果离散且容易卡在局部最优,这点从测试台直方图里评分 0–100 的分散度能直接看出来。 当前主流测试函数爱把全局最优搁在搜索空间正中心,BBBC 的搜索偏好刚好贴合这点,等于被动『作弊』,所以别被颜色等级 ≥0.99 的白色高亮骗了——那未必是真实泛化力。 真要下场验证,去作者 github 拉 Population-optimization-algorithms-MQL5 里的 Test_AO_BBBC.mq5,配合 TestStandFunctions.mqh 自己跑一遍高维函数,比看结论实在。外汇与贵金属交易用这类算法做参数寻优属高风险操作,实盘前务必在 MT5 策略测试器里压测。