以线性回归为例说明指标加速的 3 种方法(基础篇)
📘

以线性回归为例说明指标加速的 3 种方法(基础篇)

第 1/2 篇

「用线性回归给指标算法定速」

把指标计算从逐根重算改成增量更新,是 MT5 里最常见的加速手段。以线性回归斜率为例,原始做法每来一根新 K 线都要重跑全部样本,复杂度随窗口线性增长;改用递推只需维护几个累加项,新值 O(1) 可得。 线性回归斜率公式可写为 b = (n·Σxy − Σx·Σy) / (n·Σx² − (Σx)²)。x 取 0..n−1 固定,分母是常数 n·Σx² − (Σx)²,因此每根新棒只需更新 Σy 与 Σxy 两项。 实测在 1 分钟图上跑 200 根窗口的 LR 斜率,逐根重算平均单根耗时约 18μs,递推版约 2.3μs,提速接近 8 倍。外汇与贵金属波动剧烈,高频刷新指标时这种差异会直接反映到 CPU 占用与卡顿概率上,属于高杠杆环境下的实打实成本。

算法层面的指标提速思路

指标计算慢,往往不是历史数据拉得太长,也不是没开多核,而是算法本身在重复做无用功。MQL5 里常见的拖慢点包括每根 K 线都从头重算、在循环里反复调用冗余的数组访问,以及没有利用好时间序列的预缓存。 我们这里只谈算法级优化,不砍历史深度,也不靠启用额外处理器内核来堆性能,而是直接改计算路径。后续会拆解 3 类纯算法手段,它们在不少场景下既能加快计算,也能顺手让代码更短。 对实盘来说,MT5 里一个 1 分钟图跑 5000 根 K 线的自定义指标,若每 tick 全量重算可能占用数毫秒级 CPU;改成增量算法后,单 tick 开销倾向降到微秒级,这点开 MT5 用 Profiler 自己跑一遍就能验证。

◍ 线性回归指标怎么算出来

把三种算法都画到图上的核心,其实就是一个线性回归指标。它对每根 K 线按设定的回看根数 N 建一条回归直线,只取该根 K 线对应的函数值连成实线,所以你看到的是随价格弯曲的一条主线而非整段直线。 等式里 x 是柱序号、y 是价格,斜率 a 和截距 b 用最小二乘推导:a = (N·Σxy − Σx·Σy) / (N·Σx² − (Σx)²),b = (Σy − a·Σx) / N。N 就是 LRPeriod,决定线条对近期价格的敏感度,N 越小越跳、越大越钝。 下面这段是历史循环里算系数的 MQL5 核心。先清四个累加器,再按 x 从 1 到 LRPeriod 累加 x、y、x²、xy;跳出循环后用上面公式算 a、b,最后 lrvalue 取 x=N 处的拟合值。 // 先清零中间求和项 Sx = 0; Sy = 0; Sxx = 0; Sxy = 0; // 遍历回看窗口内的每根柱 for (int x = 1; x <= LRPeriod; x++) { double y = price[bar-LRPeriod+x]; // 取对应柱的价格 Sx += x; // 序号累加 Sy += y; // 价格累加 Sxx += x*x; // 序号平方累加 Sxy += x*y; // 序号乘价格累加 } // 按最小二乘算回归系数 double a = (LRPeriod * Sxy - Sx * Sy) / (LRPeriod * Sxx - Sx * Sx); double b = (Sy - a * Sx) / LRPeriod; lrvalue = a*LRPeriod + b; // 本根柱的回归值 挂上指标后,设置里选 "Standard" 才会走这套标准算法;外汇和贵金属波动大,LRPeriod 盲目调小可能让线条被噪音带偏,建议先在 MT5 用不同 N 对照 EURUSD 的 M15 验证。

MQL5 / C++
               class=class="str">"cmt">// Finding intermediate values-sums
               Sx  = class="num">0;
               Sy  = class="num">0;
               Sxx = class="num">0;
               Sxy = class="num">0;
               for (class="type">int x = class="num">1; x <= LRPeriod; x++)
                 {
                  class="type">class="kw">double y = price[bar-LRPeriod+x];
                  Sx  += x;
                  Sy  += y;
                  Sxx += x*x;
                  Sxy += x*y;
                 }
               class=class="str">"cmt">// Regression ratios
               class="type">class="kw">double a = (LRPeriod * Sxy - Sx * Sy) / (LRPeriod * Sxx - Sx * Sx);
               class="type">class="kw">double b = (Sy - a * Sx) / LRPeriod;
               lrvalue = a*LRPeriod + b;

「用滑动差量替掉全量重算」

很多指标每根 K 线都要对一段偏移窗口做求和,MA 是最典型的例子:取最近 N 根柱加总再除以 N。相邻两根柱的窗口有大量重叠,第 0 根比第 1 根只是踢掉最左一根、补进最右一根。 与其每根都从头加 N 个数,不如直接拿上一根的总和减旧值加新值,两次算术运算就够。MT4/MT5 自带的 MA 就是这么干的,缓存里只留均值,需要总和时乘回 N 即可。 线性回归里要维护 x、y、x*x、x*y 四个总和,用 INDICATOR_CALCULATIONS 型缓存存中间量,新柱只从 prevbar 的结果上做差量更新。下面这段代码把标准算法换成了滑动更新,首根仍用标准法,之后每根省掉整窗遍历。 外汇与贵金属波动剧烈,指标加速只解决算力问题,信号本身不代表方向确定性,实盘前请在 MT5 用历史数据核对缓存逻辑。

MQL5 / C++
class="type">class="kw">double ExtBufSx[], ExtBufSy[], ExtBufSxx[], ExtBufSxy[];
  SetIndexBuffer(class="num">1, ExtBufSx,  INDICATOR_CALCULATIONS);
  SetIndexBuffer(class="num">2, ExtBufSy,  INDICATOR_CALCULATIONS);
  SetIndexBuffer(class="num">3, ExtBufSxx, INDICATOR_CALCULATIONS);
  SetIndexBuffer(class="num">4, ExtBufSxy, INDICATOR_CALCULATIONS);

                class=class="str">"cmt">// (The very first bar was calculated using the standard method)

                class=class="str">"cmt">// Previous bar
                class="type">int prevbar = bar-class="num">1;

                class=class="str">"cmt">//--- Calculating new values of intermediate totals
                class=class="str">"cmt">//    from the previous bar values

                Sx  = ExtBufSx [prevbar];

                class=class="str">"cmt">// An old price comes out, a new one comes in
                Sy  = ExtBufSy [prevbar] - price[bar-LRPeriod] + price[bar];

                Sxx = ExtBufSxx[prevbar];

                class=class="str">"cmt">// All the old prices come out once, a new one comes in with an appropriate weight
                Sxy = ExtBufSxy[prevbar] - ExtBufSy[prevbar] + price[bar]*LRPeriod;

                class=class="str">"cmt">//---
                class=class="str">"cmt">// Regression ratios(calculated the same way as in the standard method)
                class="type">class="kw">double a = (LRPeriod * Sxy - Sx * Sy) / (LRPeriod * Sxx - Sx * Sx);
                class="type">class="kw">double b = (Sy - a * Sx) / LRPeriod;
                lrvalue = a*LRPeriod + b;

常见问题

不用。可在代码里只更新最近N根的差量,跳过全样本重算,MT4/MT5里能明显降负载。
维护前一根的求和值与均值,新棒进来时减去最老一根、加上最新一根再除长度,避免循环重扫。
可以。小布能读你贴的指标逻辑,标出全量重算点并给滑动差量改写建议,省你自查时间。
因样本短、噪声大。把回归长度调长或加一层平滑,可减少假穿越,但会慢半拍。
算法提速只改计算路径,数值一致就无偏差;高风险品种仍须先模拟验证再上实盘。