成功餐饮经营者算法(SRA)(基础篇)
SRA 算法在 MT5 里的落地形态
成功餐饮经营者算法(SRA)是一套发布于 MetaTrader 5 社区、由 Andrey Dik 在 2026 年 1 月 29 日 07:17 提交的自动化策略,原始测试者页面记录浏览量为 359、评论数为 0,说明该策略尚未经过大量公开讨论验证。 在 MT5 中,这类算法的典型入口是「EA 交易」加载,通过历史回测面板观察其信号逻辑是否依赖餐饮类上市公司的季节性波动映射至外汇品种。外汇与贵金属交易本身高风险,任何未经验证的算法都只应作为小仓位验证对象。 打开 MT5 后,可在策略测试器中选择对应 ex5 文件,把日期范围拉到 2025 全年,重点看净值曲线回撤是否超过 20%;若回撤失控,说明该算法对极端行情的容错偏低。
「把差解炖进好解里:SRA 的餐饮隐喻」
做优化的人常陷在一个误区:劣质解就该直接扔掉。SRA(成功餐饮经营者算法)反其道而行——它像餐厅老板处理冷门菜,不淘汰最不受欢迎的菜品,而是把它的成分和最畅销菜揉在一起重新做。 具体机制上,传统进化算法靠淘汰维持压力,SRA 靠“配对改造”:找出种群里最弱的解,强制与当前最优区域的特征融合,有时只调调料(保守微调),有时直接换食材(大幅扰动)。这样解空间多样性没崩,整体质量却稳步往上走。 原文作者用这套思路在多个标准测试函数上跑了基准,对比了若干知名元启发式算法,后续会给出“温度”和“烹饪实验强度”两个参数怎么调利用与探索的平衡。外汇与贵金属参数优化属高风险场景,回测优不代表实盘稳,任何结论都只是概率倾向。
◍ 把餐厅逻辑写进 C_AO_SRA 类
SRA 算法的核心不是丢弃差解,而是让最差智能体借鉴最优智能体的特征来迭代。类里用 menu[] 存当前候选解集合,menuT[] 作临时缓冲;temperature 从 1.0 起按 coolingRate=0.98 每轮衰减,低于 0.1 则钳制,避免过早收敛。menuInnovationRate=0.3 控制多大概率直接生成全新解而非改良。 构造函数把种群规模写死为 50,这四个参数(popSize / temperature / coolingRate / menuInnovationRate)决定搜索广度与开发深度。Init 方法调 StandardInit 载入参数范围与步长,动态分配两个数组并返回 true/false;若你改 popSize 到 200,MT5 回测内存占用会明显跳升,可实机观察。 Moving 方法分两阶段:revision=false 时按 rangeStep 随机铺初始解,置 c 与 cB;之后每轮先降温,再对按适应度排序后的种群前半最差者动手——80% 概率抄供体坐标,mutationRate 随温度与索引从 0.1 爬到 0.5,半数用正态扰动、半数纯随机。Revision 方法则刷全局最优、把解并回 menu 并重排,保证优解永远在前半。 下面这段是类声明与构造头的原文,逐行看参数默认值比读文档快:类名继承 C_AO,析构空实现;ao_link 指向外部文章,实盘用不到可删。popSize=50 注释写明了是 menu 大小,temperature 与 coolingRate 紧跟其后,menuInnovationRate 末位收口。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class</span> C_AO_SRA : <span class="keyword">class="kw">public</span> C_AO { <span class="keyword">class="kw">public</span>: <span class="comment">class=class="str">"cmt">//--------------------------------------------------------------------</span> ~C_AO_SRA() { } C_AO_SRA() { ao_name = <span class="class="type">class="kw">string">"SRA"</span>; ao_desc = <span class="class="type">class="kw">string">"Successful Restaurateur Algorithm(joo)"</span>; ao_link = <span class="class="type">class="kw">string">"[MQL5官方文档] popSize = <span class="number">class="num">50</span>; <span class="comment">class=class="str">"cmt">// number of agents(size of the "menu")</span> temperature = <span class="number">class="num">1.0</span>; <span class="comment">class=class="str">"cmt">// initial "temperature" for research control</span> coolingRate = <span class="number">class="num">0.98</span>; <span class="comment">class=class="str">"cmt">// cooling rate</span> menuInnovationRate = <span class="number">class="num">0.3</span>; <span class="comment">class=class="str">"cmt">// intensity of culinary experiments</span> ArrayResize(<span class="keyword">params</span>, <span class="number">class="num">4</span>); <span class="keyword">params</span> [<span class="number">class="num">0</span>].name = <span class="class="type">class="kw">string">"popSize"</span>; <span class="keyword">params</span> [<span class="number">class="num">0</span>].val = popSize; <span class="keyword">params</span> [<span class="number">class="num">1</span>].name = <span class="class="type">class="kw">string">"temperature"</span>; <span class="keyword">params</span> [<span class="number">class="num">1</span>].val = temperature; <span class="keyword">params</span> [<span class="number">class="num">2</span>].name = <span class="class="type">class="kw">string">"coolingRate"</span>; <span class="keyword">params</span> [<span class="number">class="num">2</span>].val = coolingRate;
参数回灌与双菜单冷启动
这段实现把外部调参面板的值塞回优化器内部,并搭好两个容量为种群两倍的中间菜单。popSize、temperature、coolingRate、menuInnovationRate 四个量都从 params 数组按索引 0~3 读取,改面板不用动算法主体。 Init 里先跑 StandardInit 做边界与步长校验,失败直接返回 false;通过后把 menu 和 menuT 都 Resize 成 popSize*2,再循环初始化每个 agent 的坐标维度。temperature 在初始化时被硬复位为 1.0,意味着每轮重跑都从同一退火起点开始,避免上轮残温污染。 Moving 的首步判断 revision 标志:未修订时,对前 popSize 个 agent 的每个坐标 c,用 u.RNDfromCI 在 [rangeMin[c], rangeMax[c]] 内均匀撒点。外汇与贵金属参数空间常带强非线性,这种冷启动更可能跳出局部劣解,但实盘前务必在 MT5 策略测试器用历史数据验证收敛倾向,相关品种杠杆风险极高。
params [class="num">3].name = "menuInnovationRate"; params [class="num">3].val = menuInnovationRate; } class="type">void SetParams() { popSize = (class="type">int)params [class="num">0].val; temperature = params [class="num">1].val; coolingRate = params [class="num">2].val; menuInnovationRate = params [class="num">3].val; } class="type">bool Init(const class="type">class="kw">double &rangeMinP [], class=class="str">"cmt">// minimum values const class="type">class="kw">double &rangeMaxP [], class=class="str">"cmt">// maximum values const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">// step change const class="type">int epochsP = class="num">0); class=class="str">"cmt">// number of epochs class="type">void Moving(); class="type">void Revision(); class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">class="kw">double temperature; class=class="str">"cmt">// current "temperature" class="type">class="kw">double coolingRate; class=class="str">"cmt">// cooling rate class="type">class="kw">double menuInnovationRate; class=class="str">"cmt">// intensity of culinary experiments class="kw">private: class=class="str">"cmt">//------------------------------------------------------------------- S_AO_Agent menu []; S_AO_Agent menuT []; }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//--- Initialization class="type">bool C_AO_SRA::Init(const class="type">class="kw">double &rangeMinP [], const class="type">class="kw">double &rangeMaxP [], const class="type">class="kw">double &rangeStepP [], const class="type">int epochsP = class="num">0) { if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return false; class=class="str">"cmt">//---------------------------------------------------------------------------- ArrayResize(menu, popSize * class="num">2); ArrayResize(menuT, popSize * class="num">2); for (class="type">int p = class="num">0; p < popSize * class="num">2; p++) menu [p].Init(coords); temperature = class="num">1.0; class=class="str">"cmt">// reset temperature during initialization class="kw">return true; } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//--- The main step of the algorithm class="type">void C_AO_SRA::Moving() { class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">// Initial initialization if (!revision) { for (class="type">int p = class="num">0; p < popSize; p++) { for (class="type">int c = class="num">0; c < coords; c++) { a [p].c [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]);