种群优化算法:引力搜索算法(GSA)(基础篇)
◍ 用引力搜索在 MT5 里找最优参数
引力搜索算法(GSA)把每个候选解当成有质量的粒子,质量大的粒子引力强、吸引其他粒子向最优区域靠拢,迭代中逐步收敛到适应度更高的参数组合。在 MT5 里跑这套,本质是用群体智能替代手工穷举调参。 该算法在 2023 年 6 月 30 日公开的示例中被打包成可直接加载的 EA,原文页面记录浏览量 1 187 次,说明量化交易者对其有实际兴趣。外汇与贵金属市场高杠杆、高波动,任何优化结果都只是历史样本上的概率倾向,实盘前必须用 MT5 策略测试器重跑。 打开 MT5 的「文件-打开数据文件夹」,把 GSA 的 EA 放进 MQL5/Experts 目录,重启终端即可在导航器看到。先拿 EURUSD 的 H1 跑 2020–2022 年数据,观察收敛曲线是否在第 40–60 代稳定,再决定是否用于实盘验证。
「引力搜索算法的物理底子」
引力搜索算法(GSA)由 E. Rashedi 提出,用来啃非线性优化这类硬骨头,核心机制直接借用了牛顿的万有引力定律。在算法框架里,每个候选解被当成有质量的物体,质量大小映射其适应度优劣,质量越大的粒子对周围粒子的吸引越强,从而把搜索引导向更优区域。 宇宙间任意两个质点沿连线相互吸引,力的大小与质量乘积成正比、与距离平方成反比——这条平方反比定律在 1686 年 4 月牛顿向皇家学会提交未出版书稿时,曾被罗伯特·胡克主张是从他那里得来。牛顿在 1687 年 7 月 5 日首版的《自然哲学数学原理》中把它系统写进经典力学,用归纳推理从观测里抽出了这条经验律。 做 EA 参数寻优时,GSA 和网格搜索最大的差别就在‘质量’这层映射:适应度好的解质量大,会像大质量天体一样把附近粒子拉过来exploration 转 exploitation。外汇与贵金属市场高杠杆、跳空频繁,拿这类全局优化器调入场阈值前,最好先用历史极端行情段回测,看质量更新公式会不会在样本外塌成局部极值。
把引力定律塞进优化器
引力搜索算法(GSA)把每个候选解当成有质量的物体,物体间依牛顿引力相互吸引,质量由适应度折算。质量大的物体移动慢、负责收敛,质量小的物体被拉向大质量体且移动快,全局最优倾向由低速的大质量体锁定。算法分探索与开拓两阶段:前期步长大、避开局部最优,后期步长小、细化搜索。 经典 GSA 用主动、被动、惯性三种质量,实际代码常合并为单一质量以省算量。物体结构只需坐标 c[]、速度 v[]、质量 M、适应度 f、到其它体的距离 R[]、受力 F[]。类 C_AO_GSA 额外存最佳坐标 cB 与最佳适应度 fB,外部参数 G_constant、a_constant、e_constant 控制引力属性,作者把更多常数外置以求调参灵活。 质量计算方程为 Mo=(Fo-Fmin)/(Fmax-Fmin),取值本应在 0~1。但零质量会让速度等于光速,故代码把下限钳在 1.1、上限近 1;若 Fmax=Fmin 则全体质量同为 1,对应适应度平面均匀、各方向等价。距离 R 取两体坐标差的平方和开根,力按牛顿引力算并用 e_constant 校正,引力常数 G 随迭代递减,末期搜索强度加大。 速度由加速度(力/质量)乘迭代步长得到,作者引入 0~1 均匀分布的力扰动与速度扰动,把运动从完全确定调到完全随机。Revision() 每轮扫一遍物体、刷新全局最佳。下面这段结构声明是跑通整套逻辑的底料,复制进 MT5 能直接编译验证。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="kw">struct</span> S_Object { <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> v []; <span class="comment">class=class="str">"cmt">//velocity</span> <span class="keyword">class="type">class="kw">double</span> M; <span class="comment">class=class="str">"cmt">//mass</span> <span class="keyword">class="type">class="kw">double</span> f; <span class="comment">class=class="str">"cmt">//fitness</span> <span class="keyword">class="type">class="kw">double</span> R []; <span class="comment">class=class="str">"cmt">//euclidean distance to other objects</span> <span class="keyword">class="type">class="kw">double</span> F []; <span class="comment">class=class="str">"cmt">//force vector</span> }; <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class</span> C_AO_GSA { <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</span> <span class="keyword">class="kw">public</span>: S_Object o []; <span class="comment">class=class="str">"cmt">//object</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">//manimum 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>
◍ 把引力搜索的参数接口接进EA
这个类把前面说的引力搜索算法封装成可直接调用的结构,对外只暴露三个公共方法:Init 做参数初始化,Moving 跑单次迭代,Revision 做收敛修订。你在 MT5 里写 EA 时,只要 new 出这个对象并传参,就能把价格坐标当成粒子来算引力扰动。 Init 的入参有 11 个,coordinatesNumber 是坐标维度(比如 EURUSD 的 H1 收盘+成交量就是 2 维),objectsNumber 是粒子数,PowerOfdistance 控制距离衰减阶数,GraviPert 与 VelocPert 的 Min/Max 划定扰动边界,G_constant、a_constant、e_constant 是算法常数,maxIterationsP 限制最多迭代轮次。 私有段里 SeInDiSp 负责把输入值夹到 [InMin,InMax] 并按 Step 离散化,RNDfromCI 则从闭区间随机取值。外汇和贵金属波动受消息面影响大,这类算法在高杠杆品种上误触发概率偏高,实盘前务必用策略测试器跑满 200 根以上样本。 接的时候别把 maxIterations 设太小,50 以下往往还没走出局部谷底就停了;设到 300 附近,配合 revision 标记位,群体才可能稳定收敛到较优坐标。
class="kw">public: class="type">void Init(class="kw">const class="type">int coordinatesNumberP, class=class="str">"cmt">//coordinates number class="kw">const class="type">int objectsNumberP, class=class="str">"cmt">//objects number class="kw">const class="type">class="kw">double PowerOfdistanceP, class=class="str">"cmt">//power of distance class="kw">const class="type">class="kw">double GraviPert_MinP, class=class="str">"cmt">//gravitational perturbation Min class="kw">const class="type">class="kw">double GraviPert_MaxP, class=class="str">"cmt">//gravitational perturbation Min class="kw">const class="type">class="kw">double VelocityPert_MinP, class=class="str">"cmt">//Velocity perturbation Min class="kw">const class="type">class="kw">double VelocityPert_MaxP, class=class="str">"cmt">//Velocity perturbation Max class="kw">const class="type">class="kw">double G_constantP, class=class="str">"cmt">//G constant class="kw">const class="type">class="kw">double a_constantP, class=class="str">"cmt">//a constant class="kw">const class="type">class="kw">double e_constantP, class=class="str">"cmt">//e constant class="kw">const class="type">int maxIterationsP); class=class="str">"cmt">//max Iterations class="kw">public: class="type">void Moving(class="type">int iter); class="kw">public: class="type">void Revision(); class=class="str">"cmt">//---------------------------------------------------------------------------- class="kw">private: class="type">int coordinatesNumber; class=class="str">"cmt">//coordinates number class="kw">private: class="type">int objectsNumber; class=class="str">"cmt">//objects number class="kw">private: class="type">class="kw">double PowerOfdistance; class=class="str">"cmt">//power of distance class="kw">private: class="type">class="kw">double GraviPert_Min; class=class="str">"cmt">//gravitational perturbation Min class="kw">private: class="type">class="kw">double GraviPert_Max; class=class="str">"cmt">//gravitational perturbation Min class="kw">private: class="type">class="kw">double VelocPert_Min; class=class="str">"cmt">//velocity perturbation Min class="kw">private: class="type">class="kw">double VelocPert_Max; class=class="str">"cmt">//velocity perturbation Max class="kw">private: class="type">class="kw">double G_constant; class=class="str">"cmt">//G constant class="kw">private: class="type">class="kw">double a_constant; class=class="str">"cmt">//a constant class="kw">private: class="type">class="kw">double e_constant; class=class="str">"cmt">//e constant class="kw">private: class="type">int maxIterations; class="kw">private: class="type">bool revision; 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);