以线性回归为例说明指标加速的 3 种方法(基础篇)
「用线性回归给指标算法定速」
把指标计算从逐根重算改成增量更新,是 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 验证。
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 用历史数据核对缓存逻辑。
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;