种群优化算法:细菌觅食优化(BFO)(基础篇)
📘

种群优化算法:细菌觅食优化(BFO)(基础篇)

第 1/2 篇

「用细菌觅食思路给 EA 找参数」

细菌觅食优化(BFO)把参数搜索模拟成菌群找食物:每只「细菌」代表一组 EA 参数,朝历史回测中更优的收益方向游动,并靠复制、迁徙避免陷在局部极值。这套思路在 MT5 里可以直接接进策略测试器的优化框架。 原文给出的示例发布于 2023 年 6 月 1 日,至统计时已有 1467 次查看、8 条讨论,说明社群里确实有人在拿它跑实盘前验证。外汇与贵金属杠杆高,回测优不代表实盘稳,任何参数组合都先用 MT5 策略测试器做样本外检验再上。 想动手的话,先把 EA 的输入参数暴露成可优化变量,再写一层 BFO 循环去调度它们;下一段给一个最朴素的菌群初始化片段。

◍ 把大肠杆菌的游泳翻滚搬进优化器

细菌觅食优化(BFO)的核心,是用大肠杆菌的社会觅食行为去逼近复杂函数的极值。它不属于梯度法,而是在高维、多峰、甚至不可导的空间里靠群体随机探索找近似解,外汇与贵金属参数寻优这类非凸问题里可能比网格暴力搜更高效,但同样伴随过拟合高风险。 大肠杆菌靠鞭毛推进:所有鞭毛逆时针转,细菌沿直线「游泳」;顺时针且方向不一致时则「翻滚」改变运动向量。原文给出的速度是每秒 10–20 倍体长,这种尺度下布朗运动干扰极强,细菌只能「平均」朝营养物质(引诱剂)移动、远离害物质(驱虫剂)。 趋化性就是细菌对化学梯度的运动反应。因为体型太小无法同时比对两极浓度,它靠运动中的浓度变化率判断梯度——浓度上升就延长本次游泳,下降就提前翻滚。BFO 里这一步等价于「沿当前方向走一步看收益,变差就随机换方向」。 Passino 把真实种群的「繁衍 / 消除 / 扩散」也写进算法:局部极值困住趋化时就靠群体迁移跳出。适应性差的个体消亡、好的自我复制,本质是自然选择。开 MT5 跑 EA 参数优化时,若只用单一种群局部搜索容易卡在次优,参考 BFO 的消除扩散机制可能提升全局覆盖概率。

BFO菌群寻优的落地逻辑与代码骨架

细菌觅食优化(BFO)规范版分五步:初始化菌群、趋化、聚集、繁衍、消除扩散。趋化靠“游动+翻滚”判定方向,聚集在成本函数里加惩罚项把菌往食物密处拉,繁衍保留健康一半替换差的一半,消除扩散则用来打破局部停滞。 实盘优化器里我用的实现跟规范版有细微差别:不标记“修改版”,但改动确实让结果更好。流程变成——初始化菌落、测适应度、判断是否繁衍(是则分裂,否则看老化)、老化到生命极限就翻滚改方向、方向对就续游不对也翻滚、再测适应度,循环到停止条件。 关键差异在生命计数器:规范版菌老死后在随机位重生,实际只会拖慢搜索;这里改成到寿命就改矢量继续游,或直接在最优解附近重生。规范版用常量运动矢量,菌会沿直线跑到天荒地老,计数器强制翻滚避免了这点。 比较健康值时我用“大于或等于”替代严格的“大于”,让菌在适应度函数全平的区域也能继续挪,否则会在原地无限翻滚。繁衍按健康降序排,前半健康菌分裂,克隆体拿新矢量从分裂点出发,菌总数恒定。 下面这段结构体和类声明是整套逻辑的容器,坐标、运动矢量、当前/前次健康、生命计数都在 S_Bacteria 里;C_AO_BFO 管菌落数组、边界、最优解和参数。

MQL5 / C++
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="kw">struct S_Bacteria
{
  class="type">class="kw">double c  [];   class=class="str">"cmt">//coordinates
  class="type">class="kw">double v  [];   class=class="str">"cmt">//vector
  class="type">class="kw">double f;       class=class="str">"cmt">//current health
  class="type">class="kw">double fLast;   class=class="str">"cmt">//previous health
  class="type">class="kw">double lifeCNT; class=class="str">"cmt">//life counter
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class C_AO_BFO
{
  class=class="str">"cmt">//----------------------------------------------------------------------------
  class="kw">public: S_Bacteria b      []; class=class="str">"cmt">//bacteria
  class="kw">public: class="type">class="kw">double rangeMax  []; class=class="str">"cmt">//maximum search range
  class="kw">public: class="type">class="kw">double rangeMin  []; class=class="str">"cmt">//manimum search range
  class="kw">public: class="type">class="kw">double rangeStep []; class=class="str">"cmt">//step search
  class="kw">public: class="type">class="kw">double cB        []; class=class="str">"cmt">//best coordinates
  class="kw">public: class="type">class="kw">double fB;            class=class="str">"cmt">//FF of the best coordinates
  class="kw">public: class="type">void Init(class="kw">const class="type">int      paramsP,           class=class="str">"cmt">//number of opt. parameters
                            class="kw">const class="type">int      populationSizeP, class=class="str">"cmt">//population size
                            class="kw">const class="type">class="kw">double lambdaP,           class=class="str">"cmt">//lambda

「细菌觅食优化器的类骨架与初始化」

这段 C_AO_BFO 类的声明把细菌觅食算法(BFO)的核心状态都压在了私有成员里:bT 数组存菌群,v、ind、val 分别管游动步长、索引和适应度,populationSize 与 parameters 锁定种群规模和待优化参数个数。lambda 控制趋化步长收缩,reproduction 是繁殖概率,lifeCounter 决定单菌最大存活代数,这几个量直接决定 EA 在 MT5 里的搜索粒度。 Init 函数是实盘前必须跑的通路。它先把 fB 置为 -DBL_MAX 表示尚未找到最优,evaluation 置 false;随后把传入的 paramsP、populationSizeP、lambdaP、reproductionP、lifeCounterP 逐一赋给成员。紧接着用 ArrayResize 把 rangeMax、rangeMin、rangeStep 扩到 parameters 长度,把 v 也扩到同长,ind、val、b 扩到 populationSize——若这里数组维度配错,后面 Swimming 和 Evaluation 会直接越界报错。 开 MT5 验证时,建议先打印 populationSize 与 parameters 的 Actual 值:例如设 parameters=5、populationSize=30,则 b 数组应精确扩到 30 个元素,val 长度同为 30,少一个都会在回测起始帧抛 4002 数组越界。外汇与贵金属品种波动跳变快,这类优化器参数若乱配,过拟合概率会明显抬升,属高风险操作。

MQL5 / C++
class="kw">const class="type">class="kw">double reproductionP,  class=class="str">"cmt">//probability of reproduction
                    class="kw">const class="type">int    lifeCounterP);  class=class="str">"cmt">//life counter
  class="kw">public: class="type">void Swimming();
  class="kw">public: class="type">void Evaluation();
  class=class="str">"cmt">//----------------------------------------------------------------------------
  class="kw">private: class="type">class="kw">double NewVector(class="type">int paramInd);
  class="kw">private: S_Bacteria bT []; class=class="str">"cmt">//bacteria
  class="kw">private: class="type">class="kw">double v       [];
  class="kw">private: class="type">int    ind     [];
  class="kw">private: class="type">class="kw">double val     [];
  class="kw">private: class="type">int    populationSize; class=class="str">"cmt">//population size
  class="kw">private: class="type">int    parameters;     class=class="str">"cmt">//number of optimized parameters
  class="kw">private: class="type">class="kw">double lambda;         class=class="str">"cmt">//lambda
  class="kw">private: class="type">class="kw">double reproduction;   class=class="str">"cmt">//probability of reproduction
  class="kw">private: class="type">int    lifeCounter;    class=class="str">"cmt">//life counter
  class="kw">private: class="type">bool   evaluation;
  class="kw">private: class="type">void   Sorting();
  class="kw">private: class="type">class="kw">double SeInDiSp(class="type">class="kw">double In, class="type">class="kw">double InMin, class="type">class="kw">double InMax, class="type">class="kw">double Step);
  class="kw">private: class="type">class="kw">double RNDfromCI(class="type">class="kw">double min, class="type">class="kw">double max);
};
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_BFO::Init(class="kw">const class="type">int     paramsP,          class=class="str">"cmt">//number of opt. parameters
                    class="kw">const class="type">int    populationSizeP, class=class="str">"cmt">//population size
                    class="kw">const class="type">class="kw">double lambdaP,         class=class="str">"cmt">//lambda
                    class="kw">const class="type">class="kw">double reproductionP,   class=class="str">"cmt">//probability of reproduction
                    class="kw">const class="type">int    lifeCounterP)    class=class="str">"cmt">//life counter
{
  fB = -DBL_MAX;
  evaluation = false;
  parameters       = paramsP;
  populationSize   = populationSizeP;
  lambda           = lambdaP;
  reproduction     = reproductionP;
  lifeCounter      = lifeCounterP;
  ArrayResize(rangeMax,  parameters);
  ArrayResize(rangeMin,  parameters);
  ArrayResize(rangeStep, parameters);
  ArrayResize(v,         parameters);
  ArrayResize(ind,       populationSize);
  ArrayResize(val,       populationSize);
  ArrayResize(b, populationSize);

常见问题

可以把参数空间当成营养场,让多组参数像菌群一样翻滚游动,按历史回测收益挑好的留下来,逐步收敛到较优解。
翻滚是随机换方向,游泳是沿当前方向小步前进;代码里翻滚用随机向量重置步向,游泳在原方向叠加步长循环试探。
小布可读取你的策略逻辑,用菌群寻优替你批量跑历史样本并标出概率占优的参数组合,你只需确认落地。
基础篇建议先设20到40个菌群个体,维度高就取上限,既能覆盖地形又不会让单轮计算拖死。
外汇样本噪声大,网格易过拟合;菌群靠随机扰动跳出局部劣解,倾向找到稳健而非表面最优的参数。