细菌趋化优化(BCO)(基础篇)
细菌趋化优化在 MT5 里的落地起点
细菌趋化优化(BCO)是一类受微生物觅食行为启发的群体智能算法,核心逻辑是让候选解像细菌一样通过趋化步移、复制与驱散在解空间里找更优区。把它搬进 MetaTrader 5,意味着你能用 EA 形式在真实行情上跑参数寻优,而不是只在离线脚本里空转。 作者 Andrey Dik 在 2025 年 4 月 23 日发布的示例帖里,该 BCO 示例在发布当周获得 609 次查看、0 条评论,说明这类原生算法移植内容在中文圈外仍有稳定关注但讨论偏冷。 实际动手时,建议先在 MT5 策略测试器里用历史数据跑通「算法的实现」与「测试结果」两段逻辑,确认趋化迭代次数与步长对回测曲线的影响,再考虑接实盘。外汇与贵金属杠杆高、滑点随机,任何优化结果都只是概率倾向,不能直接当成未来收益依据。
「把细菌趋化当成优化器来用」
优化圈子很早就开始从生物过程里偷师,进化、群体互动、觅食行为都成了算法蓝本。在 multi-modal、不可微和离散问题上,这类仿生法常被回测证明优于经典梯度法或传统启发式。 其中一种路线是趋化算法,源头可追到 Bremermann 等人:细菌沿浓度梯度对趋化因子做响应。早先的 BFO 就是模拟这套觅食逻辑,原作者还在三维梯度里跑过并拿去训神经网络。 新模型和老趋化最大的不同有两点:细菌会直接利用浓度数值,而不是浓度一高就无脑同向移动;另外细菌虽是单细胞,却会感知环境、导航、调灵敏度,对恒定化学条件能改敏感阈值,这在 MT5 参数寻优里意味着不易卡在局部平台。 S. D. Müller 和 P. Koumatsakas 在 2002 年发了微观版模型,只盯单菌趋化、不建群体宏观运动。对外汇/贵金属 EA 调参来说,这类单体重力式搜索值得在 MT5 策略测试器里手测一遍,毕竟杠杆品种回撤风险极高,任何优化结论都只是概率倾向。
◍ BCO 原生实现的代码骨架与翻车数据
细菌趋化优化(BCO)把解空间搜索模拟成细菌沿化学梯度走直线+瞬间转弯。MT5 里用 S_BCO_Bacterium 结构存每只能菌的状态:fPrev 记上一次目标函数值,fHistory[] 记最近 hs 步的历史。C_AO_BCO 类跑主循环,Moving 方法管位移、Revision 方法管全局最优与历史滚动。 按作者原文方程落地后,直接拿 Hilly / Forest / Megacity 三组测试函数各跑 10000 次:5 Hilly's result 0.4292、500 Hilly's result 0.2515;500 Megacity's result 0.0944。All score 1.99387,只占到 22.15%,连评级表都不配进。 翻车点很具体:原文有些式子数学上不成立(拿一个数除一个向量),有些直接退化成和量纲完全对不上的极小/极大值。我边写边把每个方程的函数值打印出来分析,确认原描述形态根本跑不动。 另外原文用正态分布去算操作角度,物理意义其实等价于在每个坐标上从当前位置直接给个新增量,完全可以简化。基于这两点,我后面会甩开作者描述,按 BCO 核心思想自己重写一套实现。
BCOm 怎么在 MT5 里落地跑起来
改造后的细菌觅食逻辑把控制参数压到一个(不算种群规模),核心是用截断正态做坐标扰动,而不是原版那套旋转角加温控。第一次迭代照旧在 [x_min, x_max] 内随机撒点;之后每步先算历史均变 delta_ave,再算相对变化 delta,下限钳在 0.0001,避免除零卡死。 Moving() 里对每个坐标抛 50% 概率:命中就用 allowedDispl[c] * delta 定步长,走 GaussDistribution 截尾正态;不命中直接抄 x_best 对应坐标。这点和自然细菌反着来——养料比上一步少,移动反而加速;环境好就减速,属于刻意牺牲生物学拟真换计算轻量。 Revision() 没动,仍负责把 f(x[i]) 压进长度 hs 的环形历史,hs 默认 10 步。原版每坐标每代算多次 sin/cos,这版只在概率触发时算一次高斯,种群 50 时单代浮点开销大约降一个数量级,回测速度可能明显提升,但是否更准得你 own 数据测。 下面这段结构定义和类壳子可直接贴进 MT5 的 EA 或脚本工程编译,S_BCO_Bacterium 存历史与上次值,C_AO_BCO 把 popSize=50、hs=10 写死成初值,你改这两个数就能看收敛节奏变化。外汇或贵金属参数优化属高风险,过拟合历史不代表实盘概率占优。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="kw">struct</span> S_BCO_Bacterium { <span class="keyword">class="type">class="kw">double</span> fPrev; <span class="comment">class=class="str">"cmt">// previous value of the objective function</span> <span class="keyword">class="type">class="kw">double</span> fHistory []; <span class="comment">class=class="str">"cmt">// history of objective function values</span> <span class="keyword">class="type">void</span> Init(<span class="keyword">class="type">int</span> coords, <span class="keyword">class="type">int</span> historySize) { <span class="functions">ArrayResize</span> (fHistory, historySize); <span class="functions">ArrayInitialize</span> (fHistory, <span class="number">class="num">0.0</span>); fPrev = -<span class="macro">DBL_MAX</span>; } }; <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class</span> C_AO_BCO : <span class="keyword">class="kw">public</span> C_AO { <span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span> ~C_AO_BCO() { } C_AO_BCO() { ao_name = <span class="class="type">class="kw">string">"BCO"</span>; ao_desc = <span class="class="type">class="kw">string">"Bacterial Chemotaxis Optimization"</span>; ao_link = <span class="class="type">class="kw">string">"[MQL5官方文档] popSize = <span class="number">class="num">50</span>; <span class="comment">class=class="str">"cmt">// population size(number of bacteria)</span> hs = <span class="number">class="num">10</span>; <span class="comment">class=class="str">"cmt">// number of steps to calculate average change</span> T0 = <span class="number">class="num">1.0</span>; <span class="comment">class=class="str">"cmt">// initial temperature</span> b = <span class="number">class="num">0.5</span>; <span class="comment">class=class="str">"cmt">// parameter b</span> tau_c = <span class="number">class="num">1.0</span>; <span class="comment">class=class="str">"cmt">// parameter tau_c</span> v = <span class="number">class="num">1.0</span>; <span class="comment">class=class="str">"cmt">// velocity</span> ArrayResize(<span class="keyword">params</span>, <span class="number">class="num">6</span>); <span class="keyword">params</span> [<span class="number">class="num">0</span>].name = <span class="class="type">class="kw">string">"popSize"</span>; <span class="keyword">params</span> [<span class="number">class="num">0</span>].val = popSize; <span class="keyword">params</span> [<span class="number">class="num">1</span>].name = <span class="class="type">class="kw">string">"hs"</span> <span class="keyword">params</span> [<span class="number">class="num">1</span>].val = hs;