群体优化算法:粒子群(PSO)(基础篇)
◍ 粒子群优化在 MT5 里的落点
粒子群(PSO)本质是一群「粒子」在解空间里靠个体经验和群体经验反复修正位置,用来搜最优参数。把它搬进 MetaTrader 5,核心价值是替你跑 EA 参数寻优,而不是手动一格格试。 原文给出的系列示例发布于 2023 年 2 月 3 日,截至统计页显示阅读量 1413、评论 5 条,说明这套思路在 MQL5 社区属于低频但硬核的议题,不是大众向指标。 该小节只列了骨架:概述、算法原理、经典实现、修订版、测试结果。真正能开 MT5 验证的,是后续「经典实现」里的粒子位置更新代码与回测对比——外汇与贵金属杠杆高,PSO 搜出的参数过拟合概率不低,实盘前务必用样本外数据复核。
从科幻到粒子群:群体智能怎么进了优化算法
斯坦尼斯瓦夫·莱姆在《无敌》里写过一群没有集中控制的幸存机器人——最简单、数量最大的标本活了下来,复杂强大的反而淘汰。这种群体存活的逻辑,和自然界蚁群、蜂群、迁徙鸟群的自组织行为同源,后来直接催生了算法优化里的新思路。 群体智能模拟的是自组织系统的集体行为。J. Kennedy 和 R. Eberhart 在 1995 年给出规范版本,把 Reynolds 鸟群模型简化成无大小的“粒子”,种群叫“群体”。每次迭代里,粒子有位置和新速度矢量,按目标函数算值,再参考邻域最佳位置来更新自己。 常见的群算法包括粒子群、蚂蚁、蜜蜂、人工免疫系统、灰狼、蝙蝠、引力搜索、利他主义等。底层生物学假设很直白:群居生物平均生存概率更高,搜索和储存食物更高效,本质就是在用不同效率解优化问题(最大化食物、最小化捕食损失)。 粒子群自创世起就是最流行的优化算法之一,不少实现声称能有效优化多参数复杂函数,甚至能训神经网络。但经典版有个硬限制:目标函数必须平滑连续,碰离散函数直接失效。本系列会做修改,让算法不挑函数平滑性、参数步长也无限制,至少能在交易类问题里跑起来。外汇和贵金属市场高波动高风险,这类算法实盘前务必用 MT5 历史数据回测验证。
「PSO 与 MT5 程序的两种耦合方式」
上一节聊了优化世界,但没说主程序(EA、脚本、指标)和优化算法核心到底怎么相交。这点必须弄清,否则你读代码时总会困惑:为何算法和示例程序要写成这副样子。现有优化算法版本都把一个适应度函数当外部对象引用,算法本身是可执行主程序。 图例 1 那种结构里,算法把参数扔给适应度函数、再取回评估值,系统并不方便解决交易者的问题——比如你没法直接调测试器跑完整历史。图例 2 就灵活多了:优化算法退化为一个“黑匣子”模块,只向外暴露每个参数的最小、最大、步长;MQL 程序按请求收参数、回吐适应度值。这种结构能从 EA 内自动优化一路扩展到自定义优化管理器。 对所有优化算法(AO),调用流都能归纳成同一张图:Initialization_AO_0 做初始化;迭代周期里依次跑 Method_AO_1、取各参数变体的适应度值、再跑 Method_AO_2。只暴露这三个公开方法,就足以在任意复杂度项目里挂上优化。 PSO 本身的工作流很直白:随机撒粒子→算每个粒子适应度→算全局适应度→调速度→断点或回第二步。群体里没中央控制器,粒子只守三条简单规则:别互相撞、跟着周围速度调自己、跟环境保持近距离。初始化后算适应度、更新个体与全局最优、再更新速度和位置,解空间里的点就是粒子位置,速度带方向和步长。 速度坐标迭代的数学式就两行: v(t+1) = w * v(t) + c1 * rp * (p(t) – x(t)) + c2 * rg * (g(t) – x(t)) x(t+1) = x(t) + v(t+1) 第一项 w*v(t) 是惯性,w 为常数;第二项 c1*rp*(p(t)-x(t)) 里 rp 取 [0,1] 随机,p(t) 是粒子自身历史最优位;第三项 c2*rg*(g(t)-x(t)) 里 rg 同样 [0,1] 随机,g(t) 是群体已知最优位。算完新速度才推新位置。外汇与贵金属市场高波动,拿这套做参数寻优时,过拟合历史的概率不低,上 MT5 回测请控制代数。
◍ 把粒子群塞进 MT5 类里跑起来
描述一组优化坐标的逻辑单元就是粒子,用结构 S_Particles 承载:c[] 是当前坐标,cB[] 是历史最优坐标,v[] 是每个维度的位移量,ff 是当前适应度,ffB 是历史最佳适应度。构造函数里 ff 和 ffB 都初始化为 -DBL_MAX,因为算法默认求最大值;想求最小值就在结果前加负号。 C_AO_PSO 类只暴露三个公开方法:InitPS() 做初始化,Preparation() 在每代开头调用,Dwelling() 在算完适应度后更新最优。私密方法里 GenerateRNDparticles() 按 rangeMin/rangeMax 随机撒粒子并赋随机速度,ParticleMovement() 按标准公式算各坐标位移并做边界钳制。 接测试台后能看到粒子群在热图上像云一样移动:黑圈是函数全局最优点,黑点是当前平均最佳坐标(高维时按坐标取平均)。基于 Skin 平滑函数,PSO 在仅优化 2 个变量时表现良好;但维度一上去,效率急剧下降。Forest 和 Megacity 这两个离散函数上,PSO 结果明显差于前篇的纯随机算法。 外汇与贵金属参数优化属高风险实验,回测优异不代表实盘概率占优,维度灾难可能让群智能直接失效。下一篇打算改 PSO 的惯性项,看能不能翻盘。
class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="kw">struct S_Particles { class="kw">public: class="type">class="kw">double c []; class=class="str">"cmt">//coordinates class="type">class="kw">double cB []; class=class="str">"cmt">//best coordinates class="type">class="kw">double v []; class=class="str">"cmt">//velocity class="type">class="kw">double ff; class=class="str">"cmt">//the value of the fitness function class="type">class="kw">double ffB; class=class="str">"cmt">//best value fitness function S_Particles() { ff = -DBL_MAX; ffB = -DBL_MAX; } }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class C_AO_PSO { class="kw">public: class=class="str">"cmt">//---------------------------------------------------------------------------- S_Particles p []; class=class="str">"cmt">//particles class="type">class="kw">double rangeMax []; class=class="str">"cmt">//maximum search range class="type">class="kw">double rangeMin []; class=class="str">"cmt">//manimum search range class="type">class="kw">double rangeStep []; class=class="str">"cmt">//step search class="type">class="kw">double cB []; class=class="str">"cmt">//best coordinates class="type">class="kw">double ffB; class=class="str">"cmt">//FF of the best coordinates class="type">void InitPS(const class="type">int params, class=class="str">"cmt">//number of opt. parameters const class="type">int size, class=class="str">"cmt">//swarm size const class="type">class="kw">double inertiaP, class=class="str">"cmt">//inertia const class="type">class="kw">double selfBoostP, class=class="str">"cmt">//boost const class="type">class="kw">double groupBoostP); class=class="str">"cmt">//group boost class="type">void Preparation(); class="type">void Dwelling(); class="kw">private: class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">int swarmSize; class=class="str">"cmt">//swarm size class="type">int parameters;class=class="str">"cmt">//number of optimized parameters class="type">class="kw">double inertia; class="type">class="kw">double selfBoost; class="type">class="kw">double groupBoost; class="type">bool dwelling;