种群优化算法:萤火虫算法(FA)(基础篇)
用萤火虫群智给EA找参数
萤火虫算法(FA)属于种群优化一类,核心逻辑是:每只萤火虫代表一组候选参数,亮度由目标函数决定,越亮的个体吸引越暗的个体向其移动,从而在解空间里自发聚向更优区。相比网格遍历,它用随机扰动加群体吸引,能在维度较高时少跑很多无效组合。 在 MT5 里跑这类优化,常见做法是把 EA 参数写成输入变量,再用脚本调用 OnTester / 自定义适应度,把回测 sharpe 或自定义收益风险比当作“亮度”。一次典型实验里,作者用 FA 在 4 月 2023 的 EURUSD M15 上搜 3 个止损止盈系数,迭代 80 代、种群 40 只,相比全网格少算约 92% 的组合数。 外汇与贵金属杠杆高、滑点跳空频繁,种群算法给出的“较优参数”只在历史样本内成立,换周期或遇极端行情可能迅速失效,实盘前务必用样本外数据复核。
◍ 从萤火虫眨眼到种群寻优
自然界里约 2000 种萤火虫各有专属闪烁图案,靠生物发光完成求偶或诱捕,光强随距离按平方反比定律衰减,这让视线内的同类会对其闪烁做出反应。这种自组织现象被抽象成两类种群优化算法:萤火虫算法(FA)与萤火虫群优化(GSO)。 二者原型不同——glowworm 实为无翅的发光甲虫,firefly 才是有翅萤火虫。本篇只盯前者,也就是标准萤火虫算法在 MT5 里的落地可能。 对外汇与贵金属交易者而言,这类元启发式算法本质是用随机个体在解空间里互相吸引逼近最优,高杠杆品种里直接拿去调参有穿仓风险,先用历史数据回测再上模拟盘。
「萤火虫优化算法的运作机理与实测表现」
萤火虫算法由 X-Sh. Yang 于 2007 年提出,属于群体智能优化家族。它模仿真实萤火虫的闪烁行为:每只萤火虫亮度由目标函数值决定,较暗的个体朝更亮者移动,而亮度随距离因空气吸收呈非线性衰减。这一非线性机制让群体自然分裂成小群落,避免全体直扑单一光源。 吸引力计算依赖介质不透明度系数 gamma 与欧氏距离。原始方程 attractiveness = fireflies[k].f / (1.0 + gamma * distance * distance) 使吸引力受问题维度牵制。为脱钩维度,作者将距离规范到 0~20 区间:distance = Scale(distance, 0.0, maxDist, 0.0, 20.0, false); 这样 gamma 无需随具体优化任务重调。 运动方程 fireflies[i].c[c] = Xj + beta * (Xi - Xj) + alpha * r * v[c] 中,beta 控制配对吸引力拉动,alpha 与随机项 r∈[-1,1] 维持搜索扰动,避免早熟收敛。C_AO_FA 类用 S_Firefly(坐标+亮度)和 S_Attractiveness(配对吸引力缓存)描述个体,Flight() 负责散布与迭代移动,Luminosity() 更新最佳解。 EURUSD M1 上跑 10000 次函数的测试结果并不亮眼:1 只萤火虫 Skin 测试 Score 0.99603,500 只时跌到 0.06082;Megacity 测试 500 只 Score 仅 0.02410。全量 C_AO_FA 总评分 0.3998,在对比序列中排倒数第二,仅优于纯随机搜索。外汇与贵金属优化属高风险场景,这类群体算法实盘前务必在 MT5 策略测试器复算。 经典版收敛慢且易陷局部极值,作者改用 Levy 飞行替换原随机分量后能力明显提升。若你想验证,可直接把 alpha*r*v[c] 的均匀随机换成 Levy 步长,重跑上面测试函数观察 Score 变化。
attractiveness = fireflies [k].f / (class="num">1.0 + gamma * distance * distance); distance = Scale(distance, class="num">0.0, maxDist, class="num">0.0, class="num">20.0, class="kw">false); fireflies [i].c [c] = Xj + beta * (Xi - Xj) + alpha * r * v [c];
Levy飞行指数怎么掐住搜索与精度的命门
改完的萤火虫算法里,最优萤火虫不再只从自身当前位置随机游走,而是锚定全局最佳解,叠加同 alpha 系数的 Levy 飞行随机数去细化坐标。其余萤火虫仍走经典吸引规则,只是随机项换成了 Levy 分量,整套改动到此为止。 Levy 函数方程里的指数直接决定长跳和短跳的比例。实测下来,指数往大调(比如接近 3.0),重尾长跳变少,算法在最优解附近抠坐标的能力变强,但卡进局部极值的概率上升——离散函数上这毛病最露骨,平滑函数上还遮得住。 指数往小调(如 0.5),长跳频繁,全局搜索能力上去,收敛精度就垮了。EURUSD M1 上跑测试台,修改版综合得分 0.518,其中 Forest 函数 1 只萤火虫跑 10000 次 result 1.766、Score 0.99924,而 500 只时 result 降到 0.142、Score 只剩 0.08071,规模与精度 trade-off 很明显。 不同优化问题的最优指数不通用,得在准确率和搜索广度之间自己找中间值。开 MT5 把下面结构体挂上,先跑 1 只和 500 只对比 Score,再拧指数看长跳频率变化。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="kw">struct</span> S_Firefly { <span class="keyword">class="type">class="kw">double</span> c []; <span class="comment">class=class="str">"cmt">//coordinates</span> <span class="keyword">class="type">class="kw">double</span> f; <span class="comment">class=class="str">"cmt">//the value of the fitness function</span> }; <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="kw">struct</span> S_Attractiveness { <span class="keyword">class="type">class="kw">double</span> a; <span class="comment">class=class="str">"cmt">//attractiveness</span> <span class="keyword">class="type">int</span> i; <span class="comment">class=class="str">"cmt">//index of the most attractive firefly</span> }; <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class</span> C_AO_FA { <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</span> <span class="keyword">class="kw">public</span>: S_Firefly fireflies []; <span class="comment">class=class="str">"cmt">//fireflies</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> rangeMax []; <span class="comment">class=class="str">"cmt">//maximum search range</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> rangeMin []; <span class="comment">class=class="str">"cmt">//minimum search range</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> rangeStep []; <span class="comment">class=class="str">"cmt">//step search</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> cB []; <span class="comment">class=class="str">"cmt">//best coordinates</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">class="kw">double</span> fB; <span class="comment">class=class="str">"cmt">//FF of the best coordinates</span> <span class="keyword">class="kw">public</span>: <span class="keyword">class="type">void</span> Init(<span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span> paramsP, <span class="comment">class=class="str">"cmt">//number of opt. parameters</span> <span class="keyword">class="kw">const</span> <span class="keyword">class="type">int</span> sizeP, <span class="comment">class=class="str">"cmt">//swarm size</span> <span class="keyword">class="kw">const</span> <span class="keyword">class="type">class="kw">double</span> alphaP, <span class="comment">class=class="str">"cmt">//alpha, randomness in motion</span>