以线性回归为例说明指标加速的 3 种方法·进阶篇
📘

以线性回归为例说明指标加速的 3 种方法·进阶篇

第 2/2 篇

◍ 把线性回归拆成两个内置均线

线性回归里最烦人的求和项,其实能直接认领成 MT5 自带指标:y 的总和就是简单移动平均(SMA),x*y 的总和就是线性加权移动平均(LWMA)。但 LWMA 的等价只在我们把参与回归的柱从 1 到 N 按过去到将来升序编号时才成立,所以其他等式里的枚举方式必须跟它对齐,不能各算各的。 顺着这个思路,x 的总和也就是 1+2+…+N,有现成公式替掉;x*x 的总和也能照此简化。最后只需要在 x=N 的那根最新柱上算回归均值,把 a、b 系数和回归式里的变量全部替换掉,约减完会得到一个极短的表达式:lrvalue = 3*LWMA - 2*SMA。它把原版指标里一大串循环计算全吞了。 实测这套替换在代码体积上立竿见影,指标初始化只建两个句柄,计算段只剩两三行。速度是否真更快要看内置 iMA 的优化程度,后文有专项对比。外汇和贵金属行情跳空频繁,内置均线周期(LRPeriod)设太大时回归对近期噪声的迟钝倾向明显,验证前先在策略测试器跑一遍再说。 下方片段是 OnInit 之后取数和赋值的骨架,注意 LWMA 用 MODE_LWMA、SMA 用 MODE_SMA,都走 iMA 而非 iCustom——内置函数理论上跑得最干净。

MQL5 / C++
      class="type">class="kw">double SMA [class="num">1];
      class="type">class="kw">double LWMA[class="num">1];
      CopyBuffer(h_SMA,  class="num">0, rates_total-bar, class="num">1, SMA);
      CopyBuffer(h_LWMA, class="num">0, rates_total-bar, class="num">1, LWMA);
      lrvalue = class="num">3*LWMA[class="num">0] - class="num">2*SMA[class="num">0];
  h_SMA  = iMA(NULL, class="num">0, LRPeriod, class="num">0, MODE_SMA,  PRICE_CLOSE);
  h_LWMA = iMA(NULL, class="num">0, LRPeriod, class="num">0, MODE_LWMA, PRICE_CLOSE);

「用分组均值近似线性回归提速」

想给 EA 调试提速,一个务实思路是用轻量近似指标顶替计算繁重的原版指标。调试阶段预测精度并不关键,能快速跑通策略逻辑才是重点;等方向对了,再换回重指标做精细调参。 具体做法:把回归窗口的 K 线分成左右两组,各自算平均价,连成一条直线,在末根柱上取直线数值,就得到近似回归值。相比标准最小二乘回归,这套算法砍掉了大量乘加与平方和运算,MT5 回测时帧耗时倾向明显下降。 近似质量靠图表比对验证。把标准回归(蓝)、近似算法(红)、LWMA(绿)叠在同一图:红线比绿线更贴近蓝线,说明分组均值法对回归的还原度尚可,实盘轻量部署可能够用。指标设置里切到 “Approximating” 计算方法即可启用。 下面这段是分组近似的核心循环,注意 HalfPeriod 用 MathRound 而非强制截断,左右组样本数可能差一根: 区间中点 HalfPeriod 由回归周期四舍五入得到。前半数柱 price 累加得 s1 并除以 HalfPeriod 出左组均值;后段累加得 s2 除以剩余长度出右组均值。斜率 k 为两组均值差除以半周期,末根外推值 lrvalue 由 s1 加斜率乘偏移得到。外汇与贵金属波动剧烈,近似指标仅降低算力,不削减品种固有高风险,参数有效区需自行回测确认。

MQL5 / C++
    class=class="str">"cmt">// The interval midpoint
    class="type">int HalfPeriod = (class="type">int) MathRound(LRPeriod/class="num">2);
    
    class=class="str">"cmt">// Average price of the first half
    class="type">class="kw">double s1 = class="num">0;
    for (class="type">int i = class="num">0; i < HalfPeriod; i++)
      s1 += price[bar-i];
    s1 /= HalfPeriod;
      
    class=class="str">"cmt">// Average price of the second half
    class="type">class="kw">double s2 = class="num">0;
    for (class="type">int i = HalfPeriod; i < LRPeriod; i++)
      s2 += price[bar-i];
    s2 /= (LRPeriod-HalfPeriod);
    
    class=class="str">"cmt">// Price excess by one bar
    class="type">class="kw">double k = (s1-s2)/(LRPeriod/class="num">2);
    
    class=class="str">"cmt">// Extrapolated price at the last bar
    lrvalue = s1 + k * (HalfPeriod-class="num">1)/class="num">2;

用四百万根柱逼出回归速度差

想在 MT5 里自己测指标执行效率,先把指标的日志显示开关打开,让它在 EA 交易消息日志里吐出两个时间点:OnInit() 开始处理的时间,以及 OnCalculate() 结束的时间。之所以盯这两个值,是因为绝大多数实现里 OnInit() 几乎瞬时跑完、OnCalculate() 紧接着就启动;唯一的例外是在 OnInit() 里直接创建 SMA 和 LWMA 指标那种简化写法,这时两者中间会多一段延迟——日志里那段空档就是新创建的 SMA/LWMA 当场算同样东西拖出来的。 为了把方法间的差距放大到肉眼可见,测试直接塞了 M1 周期、最大可访问历史深度的数组,总量超过 400 万根柱。每种方法跑两遍:回归分别用 20 根柱和 2000 根柱。结果显示,三种优化写法相对标准回归计算至少快两倍;柱数加到 2000 后,移动总和与简化方法比标准写法快出几百倍,外汇与贵金属行情高频回测场景下这种差距会直接反映在脚本占用主线程的时长上。 移动总和法快得离谱的原因很直白:不管回归取 20 根还是 200000 根,它每根新柱只做删旧加新两个动作,没有随长度膨胀的循环。简化方法靠不同模式均线组合,底层一样能被移动总和思路优化,所以回归拉长时耗时也不抬头。这一轮实测里,移动总和法是跑得最猛的那一个。

◍ 别急着下结论

把算法跑快之后,最直观的回报是参数优化周期被压缩——用移动总计类卷积手段替代标准逐根计算,测试器里的回归拟合速度可能提升十倍到百倍,意味着同样时间内你能把策略筛得更细、上架更频繁。 但这不等于闭眼冲。速度优势来自缓冲区与预计算,Meta 在 2012 年就曾指出移动总计要多吃 4 个辅助缓冲区,内存换时间的账得按品种和周期自己算;Vladislav 也实测过 Sx、Sxx 常量化后缓冲区形同虚设,说明不少「加速」实现本身还有坑。 真正拉开差距的,是有人愿在优化跑完前就手动下单,有人干等进度条。代码快慢只是工具,外汇与贵金属杠杆高、滑点狠,同样一套快策略实盘可能和回测概率分布脱节。先把 linear_regression.mq5 里的 LR_M_Sum 分支在 MT5 里单步跑一遍,再谈值不值得为那点速度付内存。

常见问题

拆开后用两条均线交叉代替回归线斜率,加载更轻,但拐点会滞后几根柱,适合只看方向的场景。
把总柱数按 4~8 组切分取组内均值再连线段,组数越少越快,8 组内偏差通常可忽略。
可以,小布能按你选的周期跑两套算法并列出耗时与偏差,省去手动计时的麻烦。
原文实测近似法较原生回归快数倍,若单品种加载超 1 秒再考虑替换,否则没必要动。
高频或拐点敏感策略下分组近似会平滑掉关键转折,先用历史极端行情验证再定。