群体优化算法:思维进化计算(MEC)算法(基础篇)
用思维进化计算给 EA 找参数
思维进化计算(MEC)属于群体优化算法的一类,思路是把候选参数集当成「种群」,再按「趋同」和「异化」两步迭代逼近似优解,而不是像网格搜索那样盲目穷举。在 MT5 里跑这类算法,通常要把待优化参数挂到 EA 输入变量上,由算法框架调用回测接口评估每组参数的适应度。 原文给出的示例发布于 2024 年 5 月 27 日,社区显示浏览量 887、评论 0,作者为 Andrey Dik,全文规划为概述、算法、测试结果三块。也就是说,这一篇只是入口,真要看 MEC 在外汇或贵金属上的实际表现,得等后面测试结果那节。 如果你手头有 MT5,可以先把这篇的 EA 框架载入策略测试器,选「自定义优化」模式,感受一下群体算法和内置遗传算法的耗时差异。外汇与贵金属杠杆高、滑点扰动大,任何优化结果都只是历史样本上的概率倾向,不能直接当实盘依据。
◍ 用社会行为替代梯度下降的进化思路
进化计算属于计算智能的分支,核心不是套用经典数值解法,而是借生物进化机制去逼近可行解。当目标函数不可导、或局部极值密布导致序列法容易卡死时,这类群体算法往往更扛造。 面对高维复杂问题,群体式搜索的优势在于能较快捞到‘够用’的次优解,而不是死磕理论最优——实盘调参场景下,次优但稳健通常比理论最优更有价值。外汇与贵金属波动具有高噪声、高杠杆风险,任何优化结果都只能作为概率倾向,不能直接当确定性信号。
- 年 Chengai 等人提出的思维进化计算(MEC)不模拟人脑,而是建模人类社会行为:每个个体是群体中的智能体,决策受本组成员与其他组影响;个体想获得更高地位就向组内最成功者学习,而团队间竞争又迫使所有人遵循同一套原则。MEC 的关键在组内与组间持续交换信息,相当于用‘信息自由流动’驱动社会式进化。
MEC 用局部竞争做局部搜索、用异化操作做全局搜索,并设留言板记录种群进化历史,据此反控优化进程。想在 MT5 里验证,可先按这个结构搭一个种群历史缓存,观察留言板信息如何改变下一代采样分布。
「把人群互动映射成多群体搜索」
MEC 本质是把人类社会里主流与异端思想的竞争搬进优化器:每个「思想」是一组参数向量,其下可长「意识形态分支」,相当于局部群体。原始 SMEC 用正态分布在组内核周围撒点做局部竞争,再用异化操作删掉垫底组、补新组控全局搜索。 实战里这套容易陷局部极值,所以把正态分布换成 Levy 飞行定律更划算——伪代码里主导组除最后一个想法外,其余分支按 Levy 律偏移,最后一个则随机抽本组某个想法的理论值拼装,组合能力明显强于纯随机初始化。 类骨架先立住:S_Idea 只存坐标与适应度,Init 用 -DBL_MAX 占位;C_AO_MEC 管两组思想数组 leadIdeolGroup / alteIdeolGroup 和全局最优 cB、fB。下面这段是结构原样,逐行看就懂数据怎么落袋。 struct S_Idea 定义想法结构;Init 方法按 size 重设坐标数组 c,并把适应度 f 置为 -DBL_MAX 表示未评估。class C_AO_MEC 公开了 cB 最佳坐标数组、fB 最佳适应度、idBr 意识形态分支数组,后续 Moving / Revision 都围着这几个成员转。 外汇与贵金属参数寻优用这类算法属高风险:回测漂亮不等于实盘能跑,Levy 步长调错可能几代就发散。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="kw">struct</span> S_Idea { <span class="keyword">class="type">void</span> Init(<span class="keyword">class="type">int</span> size) { <span class="functions">ArrayResize</span> (c, size); f = -<span class="macro">DBL_MAX</span>; } <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">//fitness</span> }; <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class</span> C_AO_MEC { <span class="comment">class=class="str">"cmt">//----------------------------------------------------------------------------</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>: S_Idea idBr []; <span class="comment">class=class="str">"cmt">//ideological branches</span>
把搜索边界和思维力灌进类里
族群优化器的公开接口先定义三组边界数组:rangeMax、rangeMin、rangeStep,分别约束每个维度的上界、下界与搜索步长。这三个数组长度必须等于坐标维数,否则后续 Init 会越界。 Init 是构造函数式入口,接收 coordinatesNumberP(坐标数)、populationSizeP(种群规模)、ideasNumberP(想法数)、thoughtPowerP(思维力,浮点)。调用时若 ideasNumberP 设 50、populationSizeP 设 100,意味着每代维护 100 个候选解、从中抽 50 个想法交互。 私有侧用三个 S_Idea 数组分装领先组、备选组、临时组,外加 vect/ind/val 辅助数组记录向量与排序索引。Sorting 负责按适应度重排想法,SeInDiSp 做带步长的离散空间映射,RNDfromCI 在闭区间取均匀随机数。 开 MT5 新建 EA 把这段类声明贴进 header,先只改 rangeMin[0]=-1.0、rangeMax[0]=1.0、rangeStep[0]=0.01 跑一维参数寻优,看 thoughtPower 在 0.3 与 0.7 下收敛代数的差异,外汇与贵金属品种波动剧烈,这类寻优结果仅代表历史样本倾向,实盘仍属高风险。
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">void Init(const class="type">int coordinatesNumberP, class=class="str">"cmt">//coordinates number const class="type">int populationSizeP, class=class="str">"cmt">//population size const class="type">int ideasNumberP, class=class="str">"cmt">//ideas number const class="type">class="kw">double thoughtPowerP); class=class="str">"cmt">//thought power class="kw">public: class="type">void Moving(); class="kw">public: class="type">void Revision(); class=class="str">"cmt">//---------------------------------------------------------------------------- class="kw">private: S_Idea leadIdeolGroup []; class=class="str">"cmt">//leading ideological group class="kw">private: S_Idea alteIdeolGroup []; class=class="str">"cmt">//alternative ideological group class="kw">private: S_Idea tempIdeolGroup []; class=class="str">"cmt">//temporal ideological group class="kw">private: class="type">int coordinatesNumber; class=class="str">"cmt">//coordinates number class="kw">private: class="type">int populationSize; class=class="str">"cmt">//population size class="kw">private: class="type">int ideasNumber; class=class="str">"cmt">//ideas number class="kw">private: class="type">class="kw">double thoughtPower; class=class="str">"cmt">//thought power class="kw">private: class="type">int ideasBr; class=class="str">"cmt">//number of ideological branches class="kw">private: class="type">int leadIdGroupSize; class=class="str">"cmt">//leading ideological group size class="kw">private: class="type">int alteIdGroupSize; class=class="str">"cmt">//alternative ideological group size class="kw">private: class="type">class="kw">double vect []; class=class="str">"cmt">//vector class="kw">private: class="type">int ind []; class="kw">private: class="type">class="kw">double val []; class="kw">private: class="type">bool revision; class="kw">private: class="type">void Sorting(S_Idea &ideas []); 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="type">void C_AO_MEC::Init(const class="type">int coordinatesNumberP, class=class="str">"cmt">//coordinates number const class="type">int populationSizeP, class=class="str">"cmt">//population size const class="type">int ideasNumberP, class=class="str">"cmt">//ideas number