使用优化算法即时配置 EA 参数·综合运用
📘

使用优化算法即时配置 EA 参数·综合运用

第 3/3 篇

遗传循环里怎么跑通随机指标回测

这段逻辑是典型 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 十五分钟周期手跑一遍验证。

MQL5 / C++
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 形参,实盘前务必用真实点差重跑,否则寻优结果可能偏离可用区间。

MQL5 / C++
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 降低过拟合概率,但不承诺活得好。把示例里的优化钩子拆清楚,接你自己的信号逻辑,比追新算法更实在。

常见问题

在进化群算法中把随机指标的周期与平滑类型设为可变基因,关闭过度拟合保护先跑裸回测,观察种群收敛曲线是否稳定。
实盘前建议至少跑 200 代以上,若目标函数波动仍大于 5% 就加代到 500,避免早熟收敛导致参数脆弱。
可以,小布能按你给的指标逻辑自动跑进化群寻优并标记过拟合区间,打开品种页就能看诊断摘要,省去手动跑年回测。
先关自优化跑一年可拿到无偏基准收益与回撤,之后再开优化对比,能分辨出哪些是市场给的、哪些是算法偷的。
只留种群最优与目标函数两条线,其余基因分布图折叠,画得少反而能看清是否陷入局部最优。