密码锁算法(CLA)(基础篇)
◍ 密码锁算法在 MT5 上的基本定位
密码锁算法(CLA)是一套用于 MetaTrader 5 环境的自定义逻辑框架,作者 Andrey Dik 在 2024 年 11 月 28 日 11:11 发布了初版示例,原文记录阅读量 969、评论 4 条。它把行情状态映射成类似密码锁的位组合,用来过滤无效信号。 该小节原文只列出了章节骨架:概述、算法实现、测试结果三部分,并未展开具体公式或回测曲线。想在 MT5 里验证的人,得等后续实现节放出源码再动手,目前能确认的是发布时间与基础交互数据。 外汇与贵金属品种波动受杠杆与消息面影响大,这类算法仅作信号辅助,实盘前务必用策略测试器跑历史数据,确认样本外表现可能才考虑上模拟盘。
从密码锁到优化算法的映射思路
密码锁靠输入特定数字组合来解锁,本质是把「钥匙物理匹配」换成「序列验证」。这种机制和家庭保险箱、酒店房门里用的电子锁一致:用户自己设码或沿用出厂码,正确即释放锁舌。 把它平移到优化问题里,逻辑并不牵强。待优化参数可看作一组「数字组合」,每轮迭代改参数就像拨动锁盘试码;评估目标函数效率等价于锁具反馈「对不对」。当某组参数使指标最优,等于「解锁」了最优解。 我据此写过一类受密码锁启发的优化算法,用来搜机器学习超参或交易系统参数。外汇与贵金属市场波动剧烈、杠杆风险高,这类算法只解决「参数空间搜索」本身,不暗示任何收益概率优势,实盘前务必在 MT5 策略测试器做样本外验证。
「用数字锁结构落地参数优化」
把待优化参数编码成密码锁盘,是组合锁算法(CLA)的核心。每个参数对应一组圆盘,盘上数字 0–9;若某参数用 9 个盘,组合数覆盖 000000000 到 999999999,再线性缩放到该参数的 [最小值, 最大值]。三个参数各 9 盘就是 27 个盘,搜索空间高达 10^27 种组合,这正是需要概率算子介入的原因。 交叉等价于从不同锁复制盘值拼出新编码,变异则是随机拨盘。CLA 与二进制遗传算法不同,盘旋转可用概率分布控制:高斯分布让参数在小邻域内扰动,保留当前解优良属性;幂律分布尾部更重,大跳变概率高,利于跳出局部陷阱;均匀分布则盘上每位等概率。实盘调参时,若曲线陷入平原期,可把变异分布从高斯切到幂律试一次。 MT5 里用 S_CLA_Agent 描述单把锁:f 初始化为 -DBL_MAX(double 最小值),内嵌 S_Lock 存 code[] 盘值数组,Init(coords, lockDiscs) 按坐标数与每参盘数建数组。C_AO_CLA 类持有 agent[] 种群、parents[] 父类篮,关键变量 popSize、lockDiscs、copyProb、rotateProb 全在构造时定型。 Init 里有一行很实:maxLockNumber 按 lockDiscs 累加 9*10^i 算出组合上限,例如 lockDiscs=5 时上限为 99999。Moving 按 copyProb 决定抄父类锁还是重拨盘;Revision 刷全局最优并依适应度对 parents 排序;ArrayToNumber 把 [0,3,1,5,7,0] 压成 31570;LockToDouble 用 u.Scale 把锁号映射到参数真实区间。外汇与贵金属市场杠杆高、滑点无常,这套映射仅降低过拟合概率,不保证搜索到全局优解。
<span class="comment">class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————</span> <span class="keyword">class="kw">struct</span> S_CLA_Agent { <span class="keyword">class="type">class="kw">double</span> f; <span class="comment">class=class="str">"cmt">//fitness</span> <span class="keyword">class="kw">struct</span> S_Lock { <span class="keyword">class="type">int</span> lock []; };
◍ 把锁码算法接进自定义指标类
C_AO_CLA 这个类把「Code Lock Algorithm」封装成可挂到 MT5 优化框架里的代理群体。构造函数里写死了四个对外暴露的参数:种群规模 popSize=100、锁盘数 lockDiscs=8、复制概率 copyProb=0.8、旋转概率 rotateProb=0.03,这几个值直接决定搜索空间的覆盖密度与收敛节奏。 调参时别照搬默认值。popSize=100 在 30 维以下问题里通常够用,但若你的特征坐标超过 50,agent 数组容易早熟收敛;lockDiscs=8 意味着每个 agent 有 8 个「锁盘」来固定已搜到的优解片段,盘数太少会丢结构、太多则拖慢每代 revision。 Init 里先调用基类的 S_Lock 初始化,把 code 数组按坐标数 coords 展开,再给每个坐标的 lock 子数组分配 lockDiscs 长度,f 初始化为 -DBL_MAX 代表「还没评估」。这一段是后面 Moving / Revision 能跑通的前提,缺了 ArrayResize 会在回测时报数组越界。 外汇与贵金属行情具有高杠杆与跳空风险,任何算法给出的参数组合都只是概率倾向,实盘前务必用 MT5 策略测试器跑至少 3 年 Tick 数据验证。
S_Lock code []; class="type">void Init(class="type">int coords, class="type">int lockDiscs) { f = -DBL_MAX; ArrayResize(code, coords); for (class="type">int i = class="num">0; i < coords; i++) { ArrayResize(code [i].lock, lockDiscs); } }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class C_AO_CLA : class="kw">public C_AO { class="kw">public: class=class="str">"cmt">//-------------------------------------------------------------------- ~C_AO_CLA() { } C_AO_CLA() { ao_name = "CLA"; ao_desc = "Code Lock Algorithm"; ao_link = "[MQL5官方文档] popSize = class="num">100; class=class="str">"cmt">//population size lockDiscs = class="num">8; class=class="str">"cmt">//lock discs copyProb = class="num">0.8; class=class="str">"cmt">//copying probability rotateProb = class="num">0.03; class=class="str">"cmt">//rotate disc probability ArrayResize(params, class="num">4); params [class="num">0].name = "popSize"; params [class="num">0].val = popSize; params [class="num">1].name = "lockDiscs"; params [class="num">1].val = lockDiscs; params [class="num">2].name = "copyProb"; params [class="num">2].val = copyProb; params [class="num">3].name = "rotateProb"; params [class="num">3].val = rotateProb; } class="type">void SetParams() { popSize = (class="type">int)params [class="num">0].val; lockDiscs = (class="type">int)params [class="num">1].val; copyProb = params [class="num">2].val; rotateProb = params [class="num">3].val; } class="type">bool Init(class="kw">const class="type">class="kw">double &rangeMinP [], class=class="str">"cmt">//minimum search range class="kw">const class="type">class="kw">double &rangeMaxP [], class=class="str">"cmt">//maximum search range class="kw">const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">//step search class="kw">const class="type">int epochsP = class="num">0); class=class="str">"cmt">//number of epochs class="type">void Moving(); class="type">void Revision(); class="type">void Injection(class="kw">const class="type">int popPos, class="kw">const class="type">int coordPos, class="kw">const class="type">class="kw">double value); class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">int lockDiscs; class=class="str">"cmt">//lock discs class="type">class="kw">double copyProb; class=class="str">"cmt">//copying probability class="type">class="kw">double rotateProb; class=class="str">"cmt">//rotate disc probability S_CLA_Agent agent []; class="kw">private: class=class="str">"cmt">//-------------------------------------------------------------------