使用优化算法即时配置 EA 参数·综合运用
遗传循环里怎么跑通随机指标回测
这段逻辑是典型 EA 优化骨架:外层 epoch 循环控制进化代数,每次先 Preparation() 重置种群,再对 inpPopSize 个个体分别算参数与适应度,最后 Revision() 做选择交叉。注意 !IsStopped() 判断——用户在 MT5 策略测试器里点停止能立刻中断,不浪费 CPU。 内层 GetVariantCalc 给第 set 个个体塞入参数,VirtualStrategy 用这些参数在 barsOptimize+1 根 K 线上虚拟成交,SetFitness 把收益或回撤类的评分写回种群。循环结束 Print(GetFitness(0)) 输出最优个体的适应度,GetVariant 取出参数,ArrayCopy 存进全局 set 数组供后续实盘或再优化使用。 VirtualStrategy 函数里先用 CopyRates 抓当前周期行情,失败直接返回 -DBL_MAX 让该个体灭绝。随机指标初始化参数为 (int)set[0]、1、3,即 %K 周期取自基因,%K 减速=1、%D=3 固定。上下阈值用 set[1] 和 100-set[1] 对称设定,例如 set[1]=20 时就是经典 20/80 通道。 历史回测从 i=2 开始,用 ExtMainBuffer 的前两根值做穿越判断:当 iStMain1<=dnLevel 且 dnLevel<iStMain0 视为金叉触发买入,同时先平掉可能的空单。外汇与贵金属杠杆高,这类虚拟回测未计滑点冲击,实盘概率性失效,务必先开 MT5 用 EURUSD 十五分钟周期手跑一遍验证。
for (class="type">int epochCNT = class="num">1; epochCNT <= epochCount && !IsStopped(); epochCNT++) { Preparation(); for (class="type">int set = class="num">0; set < inpPopSize; set++) { GetVariantCalc(parametersSet, set); SetFitness(VirtualStrategy(parametersSet, inpBarsOptimize, spread), set); } Revision(); } Print("Fitness: ", GetFitness(class="num">0)); GetVariant(parametersSet, class="num">0); ArrayCopy(set, parametersSet, class="num">0, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">class="kw">double VirtualStrategy(class="type">class="kw">double &set [], class="type">int barsOptimize, class="type">class="kw">double spread) { class=class="str">"cmt">//data preparation------------------------------------------------------------ class="type">MqlRates rates []; class="type">int dataCount = CopyRates(_Symbol, PERIOD_CURRENT, class="num">0, barsOptimize + class="num">1, rates); if (dataCount == -class="num">1) { Print("Data get error"); class="kw">return -DBL_MAX; } class="type">class="kw">double hi []; class="type">class="kw">double lo []; class="type">class="kw">double cl []; ArrayResize(hi, dataCount); ArrayResize(lo, dataCount); ArrayResize(cl, dataCount); for (class="type">int i = class="num">0; i < dataCount; i++) { hi [i] = rates [i].high; lo [i] = rates [i].low; cl [i] = rates [i].close; } C_iStochastic iStoch; iStoch.Init((class="type">int)set [class="num">0], class="num">1, class="num">3); class="type">int calc = iStoch.Calculate(dataCount, class="num">0, hi, lo, cl); if (calc <= class="num">0) class="kw">return -DBL_MAX; class=class="str">"cmt">//============================================================================ class=class="str">"cmt">//test of strategy on history------------------------------------------------- S_Deals deals; class="type">class="kw">double iStMain0 = class="num">0.0; class="type">class="kw">double iStMain1 = class="num">0.0; class="type">class="kw">double upLevel = set [class="num">1]; class="type">class="kw">double dnLevel = class="num">100.0 - set [class="num">1]; class="type">class="kw">double balance = class="num">0.0; class=class="str">"cmt">//running through history----------------------------------------------------- for (class="type">int i = class="num">2; i < dataCount; i++) { if (i >= dataCount) { deals.ClosPos(-class="num">1, rates [i].open, spread); deals.ClosPos(class="num">1, rates [i].open, spread); class="kw">break; } iStMain0 = iStoch.ExtMainBuffer [i - class="num">1]; iStMain1 = iStoch.ExtMainBuffer [i - class="num">2]; if (iStMain0 == class="num">0.0 || iStMain1 == class="num">0.0) class="kw">continue; class=class="str">"cmt">//buy------------------------------- if (iStMain1 <= dnLevel && dnLevel < iStMain0) { deals.ClosPos(-class="num">1, rates [i].open, spread);
◍ 用进化群算法跑参数寻优
这段逻辑把策略寻优交给了 AO_ESG 类,本质是用一种带群体半径和扩张比的进化搜索替代网格遍历。Include 进来的头文件封装了群体移动、修订与适应度评估,调用方只需喂入参数边界与步长。 初始化时写死了几个量级:种群规模 200、分组数 100、组半径 0.1、扩张比 2.0、能量指数 10.0。这几个数直接决定搜索的覆盖密度与跳出局部极值的能力,在 MT5 里改它们能明显改变寻优耗时。 主循环按 epochCount = 总发射次数 / 种群规模 来跑,每个 epoch 先 Moving 再对全部个体算 VirtualStrategy 返回的适应度,最后 Revision 淘汰更新。终端会 Print 出最优适应度 AO.fB,并把最优参数复制到 set 数组供回测使用。 外汇与贵金属品种点差会直接进 VirtualStrategy 的 spread 形参,实盘前务必用真实点差重跑,否则寻优结果可能偏离可用区间。
if (deals.GetBuys() == class="num">0) deals.OpenPos(class="num">1, rates [i].open, spread); } class=class="str">"cmt">//sell------------------------------ if (iStMain1 >= upLevel && upLevel > iStMain0) { deals.ClosPos(class="num">1, rates [i].open, spread); if (deals.GetSels() == class="num">0) deals.OpenPos(-class="num">1, rates [i].open, spread); } } class=class="str">"cmt">//---------------------------------------------------------------------------- if (deals.histSelsCNT + deals.histBuysCNT <= class="num">0) class="kw">return -DBL_MAX; class="kw">return deals.balance; } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="macro">#include "AO_ESG.mqh" class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void Optimize(class="type">class="kw">double& set [], class="type">class="kw">double& range_min [], class="type">class="kw">double& range_step [], class="type">class="kw">double& range_max [], const class="type">int inpBarsOptimize, const class="type">int inpPopSize, const class="type">int numberFFlaunches, const class="type">class="kw">double spread) { class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">int epochCount = numberFFlaunches / inpPopSize; C_AO_ESG AO; class="type">int Population_P = class="num">200; class=class="str">"cmt">//Population size class="type">int Groups_P = class="num">100; class=class="str">"cmt">//Number of groups class="type">class="kw">double GroupRadius_P = class="num">0.1; class=class="str">"cmt">//Group radius class="type">class="kw">double ExpansionRatio_P = class="num">2.0; class=class="str">"cmt">//Expansion ratio class="type">class="kw">double Power_P = class="num">10.0; class=class="str">"cmt">//Power AO.Init(ArraySize(set), Population_P, Groups_P, GroupRadius_P, ExpansionRatio_P, Power_P); for (class="type">int i = class="num">0; i < ArraySize(set); i++) { AO.rangeMin [i] = range_min [i]; AO.rangeStep [i] = range_step [i]; AO.rangeMax [i] = range_max [i]; } class=class="str">"cmt">// Optimization------------------------------------------------------------- for (class="type">int epochCNT = class="num">1; epochCNT <= epochCount && !IsStopped(); epochCNT++) { AO.Moving(); for (class="type">int set = class="num">0; set < ArraySize(AO.a); set++) { AO.a [set].f = VirtualStrategy(AO.a [set].c, inpBarsOptimize, spread); } AO.Revision(); } Print("Fitness: ", AO.fB); ArrayCopy(set, AO.cB, class="num">0, class="num">0, WHOLE_ARRAY); }
「先关掉自优化跑一年看看」
把 SelfOptimization 参数设为 "false",EA 就只按初始参数跑,不触发任何在线重优化逻辑。我们用随机振荡指标(Stochastic Oscillator)为底层的这套 EA,在 MT5 里加载图 2 的 EA 设置,对最近一年行情做单品种回测。 图 3 给出的是禁用自优化时的净值曲线与交易统计,图 4 则是开启 SelfOptimization 后同周期同品种的结果。两者摆在一起,能直接看出自优化模块在样本期内是否改变了回撤结构与交易频率。 做这一步的意义在于建立基线:只有先确认 "false" 模式下的表现,后续开启自优化带来的差异才读得出来。外汇与贵金属杠杆高、滑点随机,样本外表现可能偏离回测,别把一年结果当常态。
画得少,看得清
自我优化落到 EA 里,本质只改几行接口代码,核心是把历史切成训练段和测试段反复往前推。WFT 跑下来,能压住过度拟合——某段历史里漂亮、实盘就崩的策略会被提前筛掉,这是普通全样本优化给不了的现实感。 沙子没金子,优化也炼不出金子:先有正期望的策略骨架,再让前向步行测试替你挑历史窗长和适应度标准。附的 Self-Optimization.zip 仅 14.18 KB,是演示用的空壳,没过实盘校验,直接上真账户属于裸奔。 外汇和贵金属杠杆高、跳空频繁,WFT 降低过拟合概率,但不承诺活得好。把示例里的优化钩子拆清楚,接你自己的信号逻辑,比追新算法更实在。