种群优化算法:和弦搜索(HS)(基础篇)
◍ 和弦搜索怎么在 MT5 里跑起来
和弦搜索(Harmony Search,HS)是一类受音乐即兴启发的种群优化算法,核心是把一组解当成「乐手」,靠记忆库、音高微调、随机扰动三类动作迭代逼近最优。它在 MT5 里常被塞进 EA 的参数寻优环节,替代网格搜索去躲局部极值。 算法每代先建一个 Harmony Memory(HM),存当前若干较优解;新解以 HMCR 概率从记忆里挑、以 PAR 概率做邻域微调,剩下情况纯随机。这套机制让种群既有 exploitation 也有 exploration,参数少、易落地。 原文示例发布于 2023-07-17 10:57,累计浏览 1015 次,作者 Andrey Dik,原文仅列了「概述 / 算法 / 测试结果」三段骨架,未给可复跑代码。想验证可自己在 MT5 用 CTrade+自写 HM 类做最小化回测,先调 HM 大小与 PAR,看曲线收敛速度。外汇与贵金属杠杆高,回测顺不等于实盘稳,参数敏感期可能快速回撤。
「把调音过程当成寻优模型」
音乐里和弦不是装饰那么简单,它是一组同时或依次发声的频率组合,决定了作品的完整度与表现力。没有和弦支撑的旋律就像缺了色的线稿,结构在但张力出不来。
- 年 Z. W. Geem 提出的调和搜索(HS)算法,直接拿音乐即兴找和弦的过程当优化框架:乐手在音域内试音、记好听的组合、后续段落往更优和声靠,这对应着在多维空间里探全局极值。
HS 在即兴阶段满足三条之一——完全随机取声、从记忆里抽已有和弦、或取记忆中相邻和弦向量。它既能跑连续优化也能跑离散优化,核心卖点是结构简单且勘探/开发两阶段稳定性高,过去十年已被塞进不少复杂问题里验证过。 对外汇或贵金属交易者来说,这类元启发式思路可借来调参数组合,但市场高波动下任何“最优解”都只是历史样本里的倾向,实盘仍需人工把关。
把优化问题听成一首曲子:HS 算法的和弦逻辑
和弦搜索(HS)算法把参数优化类比成音乐家凑和弦:随机拨出一组音,听着顺耳就记进记忆,不顺就改音或借别的曲子里的音。绿色条代表一个参数维度上的可选音阶,蓝色条是一整首作品即一组候选解;记忆里存多首蓝色条变体,这就是种群。图例 1 用八音阶离散示例讲机制,实盘里参数可以是任意连续音阶加半音过渡,正确参数对应作品里的正确音符。 创作从乐器可重复频率内的随机声音开始,先生成多首曲子变体,再对每个变体里的音符做三种操作之一:随机换一个音、从别的版本按序号抄一个音、抄完再微调高低。新变体算完适应度,比原位置旧的强就覆盖,不排序种群,只做同位置替换——这点像遗传算法里的适者生存,但不用全局排名。 输入参数只有四个且直观:Population_P=50 是记忆中作品变体数;Eh_P=0.9 是从记忆借和弦的频率,越高组合性越强;Ep_P=0.1 是借来后微调音高的概率;Range_P=0.2 表示不借时编辑幅度为乐器范围的 20%。 弦乐更新公式为 h[har].c[c] = h[har].c[c] + r * B * frequency[c],其中 r 是 [-1,1] 随机数,B 随迭代衰减:B = ((maxIter-iter)/maxIter)*(maxB-minB)+minB。图例 2 显示 B 在优化末期趋小,相当于把发现的极值做精细优调。Revision() 里仅当 h>hB 才更新全局最优,和经典 HS 替换最差和弦不同,实测这种只替最佳的实现效率更高。外汇与贵金属参数优化属高风险,回测优异不代表实盘概率占优。 别把正态当圣经 HS 借用了均匀重组、均匀突变等旧思路,新颖点在音乐动机与思路组合。想把它从进化算法里单列出来,得真去啃音乐创作理论而非只调 Ep_P。
class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="kw">struct S_Harmony class=class="str">"cmt">//musical composition { class="type">class="kw">double c []; class=class="str">"cmt">//chords class="type">class="kw">double cB []; class=class="str">"cmt">//best chords class="type">class="kw">double h; class=class="str">"cmt">//harmony quality class="type">class="kw">double hB; class=class="str">"cmt">//best harmony quality }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class C_AO_HS { class=class="str">"cmt">//---------------------------------------------------------------------------- class="kw">public: S_Harmony h []; class=class="str">"cmt">//harmonies matrix 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 chords class="kw">public: class="type">class="kw">double hB; class=class="str">"cmt">//best harmony quality class="kw">public: class="type">void Init(const class="type">int chordsNumberP, class=class="str">"cmt">//chords number const class="type">int harmoniesNumberP, class=class="str">"cmt">//harmonies number
◍ 和声搜索类的私有字段与初始化入口
这段声明把和声搜索(HS)优化器的状态都收进了一个 C_AO_HS 类里。对外只暴露 Moving(iter) 和 Revision() 两个方法,真正的随机选频概率 Eh、步长调整概率 Ep、搜索范围 range 以及最大迭代次数都藏在 private 段,避免外部逻辑误改导致寻优发散。 初始化函数 Init 接收 chordsNumber(和声数量)、harmoniesNumber(记忆库规模)、EhP、EpP、rangeP 与 maxIterationsP 六个参数。实盘里若把 chordsNumber 设到 50 以上,MT5 回测 EURUSD M15 时单代计算耗时可能从 0.3 ms 跳到 2 ms 级,参数维度多时要先压这个数。 几个私有工具函数值得留意:SeInDiSp 做带步长的离散化映射,RNDfromCI 在闭区间取均匀随机数,Scale 则把内部 [InMIN,InMAX] 线性拉伸到 [OutMIN,OutMAX],revers 为真时反转方向。复制下面代码到 MT5 头文件,就能直接挂到自己的指标寻优框架上。
const class="type">class="kw">double EhP, class=class="str">"cmt">//random selection frequency const class="type">class="kw">double EpP, class=class="str">"cmt">//frequency of step-by-step adjustment const class="type">class="kw">double rangeP, class=class="str">"cmt">//range 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 chordsNumber; class=class="str">"cmt">//chords number class="kw">private: class="type">int harmoniesNumber; class=class="str">"cmt">//harmonies number class="kw">private: class="type">class="kw">double Eh; class=class="str">"cmt">//random selection frequency class="kw">private: class="type">class="kw">double Ep; class=class="str">"cmt">//frequency of step-by-step adjustment class="kw">private: class="type">class="kw">double range; class=class="str">"cmt">//range class="kw">private: class="type">int maxIterations; class="kw">private: class="type">class="kw">double frequency []; class=class="str">"cmt">//frequency range 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); class="kw">private: class="type">class="kw">double Scale(class="type">class="kw">double In, class="type">class="kw">double InMIN, class="type">class="kw">double InMAX, class="type">class="kw">double OutMIN, class="type">class="kw">double OutMAX, class="type">bool revers); }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_HS::Init(const class="type">int chordsNumberP, class=class="str">"cmt">//chords number const class="type">int harmoniesNumberP, class=class="str">"cmt">//harmonies number const class="type">class="kw">double EhP, class=class="str">"cmt">//random selection frequency const class="type">class="kw">double EpP, class=class="str">"cmt">//frequency of step-by-step adjustment const class="type">class="kw">double rangeP, class=class="str">"cmt">//range