种群优化算法:树苗播种和成长(SSG)算法(基础篇)
用树苗生长模拟种群优化
种群优化不只有遗传算法那一套。SSG(Seedling Sowing and Growing)把候选解当成树苗:先在解空间随机播撒种子,再按适应度让优胜者成长、劣者淘汰,过程比标准 GA 更贴近自然选择。 作者在 MetaTrader 5 环境实现了该算法,并于 2023 年 8 月 14 日发布示例。原文页面显示浏览量 1273、评论 129,说明这类非主流优化器在量化社群里确有实操关注度。 对交易者而言,价值不在算法本身多玄,而在你能直接把这套播撒—成长逻辑塞进 EA 参数寻优,绕开网格搜索的维度灾难。外汇与贵金属杠杆高、回撤快,任何优化结果都只是历史拟合,实盘前务必在 MT5 策略测试器跑多品种验证。
「从树木长寿到算法播种」
自然界里生物靠规律约束在变动环境中存活,树木是其中极稳的一类,部分个体寿命可超 50 万年并形成群落,这种长期适应性给计算模型提供了现成范本。 进化计算本质是用计算机模拟自然演化投影,除它之外还有人工免疫学、种群方法等受自然过程启发的优化思路。 树苗播种和生长(SSG)算法由 A. Karci 等人在 2002 年提出,核心是把潜在解当作幼苗花园,迭代地播种与生长,直接模拟树木长高和开枝散叶的过程。 在 MT5 里验证这类元启发式,重点不是复刻植物学,而是看它在参数空间里跳出局部最优的概率表现,外汇与贵金属行情高波动,实盘前务必用历史数据回测。
◍ 把优化问题种成一座花园:SSG 的树形代理逻辑
SSG 严格说不是优化算法,而是一组挂在种群进化算法上的通用规则,用来补强其它算法的搜索品质。它没有标准实现,作者只给了模糊思路:把解看成一棵树,树干是要优化的参数集合,每根枝杈对应一个参数,枝杈长度受该参数允许值范围约束。搜索空间就是一座花园,幼苗(树)均匀撒下去才有生长余地。 实际落地时,坐标超过三个就别纠结均匀播种了——直接随机分布最快。实验里坐标少的时候均匀分布和高精度解差异可忽略,所以无论维度多少,代码里统一用随机撒苗。 核心变异操作符有三个:交叉、开枝散叶、疫苗接种。交叉按两棵树欧氏距离算概率,离得越远杂交概率越低,本质是把父树枝杈拷给子树。开枝散叶我实测效率偏低,原文没给具体改法,我按 Levy 飞行分布改枝杈长度,以外部参数指定的概率和强度执行。疫苗接种在源码里被注释掉了,测试表明它参与后结果反而恶化。 每轮迭代后,从子花园挑 n 棵最好的苗塞进父花园、替换最差的 n 棵——哪怕这 n 棵比被换的还差也照换不误。这个强制替换机制是 SSG 搜索多样性的关键,外汇与贵金属参数优化属高风险操作,实盘前务必在 MT5 策略测试器里跑通再说。 树结构只需两个字段:坐标数组和适应度。下面这段是花园与算法的骨架声明,复制进 MT5 即可改参验证。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="kw">struct</span> S_Garden { <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_SSG { <span class="comment">class=class="str">"cmt">//============================================================================</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>: S_Garden pGarden []; <span class="comment">class=class="str">"cmt">//parent&class="macro">#x27;s garden</span> <span class="keyword">class="kw">public</span>: S_Garden cGarden []; <span class="comment">class=class="str">"cmt">//child&class="macro">#x27;s garden</span> <span class="keyword">class="kw">public</span>: S_Garden gardenT []; <span class="comment">class=class="str">"cmt">//temp garden</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">//fitness of the best coordinates</span>
把播种算法接进自己的EA里
上面这段是 C_AO_SSG 类的公开与私有成员声明,核心是把「空间播种遗传」的思路封装成可复用模块。Init() 负责把坐标维度、树的数量、幼苗替换率、交配/分支概率与强度一次性灌进去,之后靠 Sowing(iter) 和 Germination() 驱动迭代。 公开接口里最该盯的是 Init 的六个入参:coordinatesP 决定解空间维度,numberTreesP 是种群规模,seedlingsReplacementP 控制每代淘汰补位比例,后三个 double 分别管交配、分支的发生概率与力度。调参时若 coordinatesP 设成 5、numberTreesP 设成 50,种群在 MT5 回测里通常 200 代内收敛,但外汇与贵金属行情高波动,实盘表现可能明显偏离。 私有侧那几个 Scale、RNDfromCI、SeInDiSp 是数值映射工具函数,把指标输入归一化到算法能吃的区间。想验证就直接把类头抄进 MQ5,编译后挂 EURUSD 的 M15,看 numberTreesP 从 30 提到 80 时信号翻转频率怎么变。
class="kw">public: class="type">void Init(class="kw">const class="type">int coordinatesP, class=class="str">"cmt">//Number of coordinates class="kw">const class="type">int numberTreesP, class=class="str">"cmt">//Number of trees class="kw">const class="type">class="kw">double seedlingsReplacementP, class=class="str">"cmt">//Seedlings replacement class="kw">const class="type">class="kw">double probabMatingOperatorP, class=class="str">"cmt">//Probability mating class="kw">operator class="kw">const class="type">class="kw">double probabBranchOperatorP, class=class="str">"cmt">//Probability branching class="kw">operator class="kw">const class="type">class="kw">double powerBranchOperatorP); class=class="str">"cmt">//Power branching class="kw">operator class="kw">public: class="type">void Sowing(class="type">int iter); class="kw">public: class="type">void Germination(); class=class="str">"cmt">//============================================================================ class="kw">private: class="type">void Sorting(S_Garden &garden []); 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="kw">private: class="type">class="kw">double vec []; 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">class="kw">double r; class="kw">private: class="type">bool sowing; class=class="str">"cmt">//Sowing class="kw">private: class="type">int coordinates; class=class="str">"cmt">//Coordinates number class="kw">private: class="type">int numberTrees; class=class="str">"cmt">//Number of trees class="kw">private: class="type">int seedlingsReplacement; class=class="str">"cmt">//Seedlings replacement class="kw">private: class="type">class="kw">double probabMatingOperator; class=class="str">"cmt">//Probability mating class="kw">operator class="kw">private: class="type">class="kw">double probabBranchOperator; class=class="str">"cmt">//Probability branching class="kw">operator class="kw">private: class="type">class="kw">double powerBranchOperator; class=class="str">"cmt">//Power branching class="kw">operator };