中测试移动平均线计算的性能(基础篇)
📘

中测试移动平均线计算的性能(基础篇)

第 1/3 篇

「在 MT5 里压测均线计算开销」

想摸清移动平均线(MA)在实盘环境下的计算负担,最直接的方式是在 MetaTrader 5 终端里跑一组基准测试,而不是凭经验猜。 Sergey Pavlov 在 2013 年 12 月 6 日发布的示例显示,该测试样本被社区查阅 2685 次,说明这类底层性能验证长期有真实需求。 外汇与贵金属品种 tick 频率高,均线重算次数随周期缩小而陡增,高波动时段可能出现肉眼可察的脚本延迟,属于典型高风险环境下的终端资源问题。 打开 MT5 的 MetaEditor,新建脚本并循环调用 iMA 句柄,用 GetTickCount 差值记录耗时,你就能复现并验证自己机器上的真实性能曲线。

为什么要在 MT5 里重测均线速度

移动平均线是价格时间序列平滑里用得最频繁的工具,不管是手动看盘还是写指标、EA,基本都绕不开。MQL5 里内置了十几种均线算法,从最朴素的 SMA 到各类加权、指数变体全有。 很多人会问:不同均线算法的计算开销真的有差别吗?MT5 比 MT4 在均线计算上快了多少?这类问题靠猜没用,新平台整体速度虽然亮眼,但最好自己跑实验验证。 针对这一系列疑问,后面会用实测数据逐一拆开看,而不是停留在「新版本更快」这种空话上。

◍ iMA 与 CopyBuffer 的实测环境

MQL5 里 iMA 本身只返回指标句柄,不吐计算结果,所以测速必须同时跑 iMA 和 CopyBuffer 两个函数才有意义。绝对值会随机器和客户端设置浮动,但同一平台内的相对快慢关系基本稳定。 本次基准环境钉死在 Core i7 965 上,品种用 EURUSD,价格序列长度 10000 根,客户端最大柱数也设成 10000。均线覆盖 MODE_SMA、EMA、SMMA、LWMA 四种,计时精度压到两位有效数字,单轮最多调 7 次均线函数。 这套条件你在自己 MT5 里能原样复现:把图表历史加载到 1 万根,写个脚本轮流调 iMA 拿句柄再 CopyBuffer 取数,用 GetTickCount 掐时间。外汇与贵金属杠杆高、滑点随机,测速结果仅反映本地客户端负载,不等于实盘信号质量。

「用 GetTickCount 测不均的坑」

想量移动平均线算得快不快,先调 GetTickCount(),它只给毫秒级时间戳。单发一次太粗,得套循环把误差摊薄,但循环怎么写直接决定数据能不能信。 iMA 在客户端全局缓存里留副本:第一次用某组参数算完,后面同参数调用只把引用计数加一,直接读旧值,不再重算。所以若循环里参数不动,测出来的全是缓存命中时间,毫无意义。 破法是指定循环内参数必须唯一。我们挑了三个维度拉开差异:平均周期(1–100)、时间框架(M1/M5/M15/M30)、应用价格(PRICE_CLOSE 到 PRICE_WEIGHTED 共 7 种)。 实测覆盖 10000 个元素的数组,用七种不同调用方式各跑一遍,才能拿到接近真实的算力消耗分布。外汇与贵金属杠杆高,回测结论只反映历史计算负载,不预示行情方向。

四种均线的实测耗时分布

把 100×4×7=2800 类移动平均组合在统一基准下跑了一遍:价格数组固定 10000 个元素,总耗时除以 2800 得到单次循环开销。案例 1(MQL5 原生 iMA)里 SMA 单次约 0.0028/2800 秒,而 EMA 仅 0.00023 秒、SMMA 0.00027 秒,LWMA 却到了 0.0045 秒——同一接口内不同模型能差出近 20 倍。 案例 6 把 SMA/EMA/SMMA 压到 0.00014/0.000121/0.000117 秒,是全部测试里最快的一档;案例 3、4 反而冲到 0.0998 秒上下,慢了三个数量级。外汇与贵金属策略若依赖高频重算均线,这种差距会直接吃掉 tick 级响应窗口,属典型高风险坑点。 MT4(案例 0)四种模型都在 0.004 秒附近,MT5(案例 1)除 LWMA 外普遍低一个数量级,整体快约 40%。但注意表 2 的耗时都是“冷启动”——客户端全局缓存里没有任何预计算数据,实盘热身后的数字可能偏向更好。 图 1 到图 5 用对数刻度画了各模型在案例 0–6 的柱形,Y 轴值越大越快。开 MT5 把 MovingAverages.mqh 里对应函数挨个 Profile 一次,比看结论更实在。

◍ 用脚本逐个验证均线算法更干净

想测移动平均线的计算性能,脚本比 EA 更合适——脚本一次性跑完所有计算,不会被新报价这类事件打断,结果更纯粹。 没必要为了覆盖各种情况去写一个通用程序,按每个计算案例单独建脚本,排查哪段逻辑拖慢了速度会直接很多。 下面这段是标准指标取值的骨架:先建句柄,再在价格事件里把缓冲区拷进数组。周期设 0 表示用当前图表周期,均线参数用了 21 期 EMA 收价,拷贝深度 100 根 K 线,足够做一轮局部统计。 外汇与贵金属波动大、杠杆高,这类性能测试只在策略准备期有价值,不代表任何方向判断。

MQL5 / C++
class=class="str">"cmt">//--- 指标缓冲区
class="type">class="kw">double      MA[];                    class=class="str">"cmt">// 用于存放 iMA 指标数值的数组
class=class="str">"cmt">//---- 指标句柄
class="type">int         MA_handle;              class=class="str">"cmt">// iMA 指标句柄
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| EA 初始化函数                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int OnInit()
  {
  class=class="str">"cmt">//--- 创建 iMA 指标句柄
  MA_handle=iMA(NULL,class="num">0,class="num">21,class="num">0,MODE_EMA,PRICE_CLOSE);
  class=class="str">"cmt">//--- 如果出错,打印消息
  if(MA_handle<class="num">0)
    {
    Print("此 iMA 对象不能创建: MA_handle= ",INVALID_HANDLE);
    Print("运行时错误 = ",GetLastError());
    class=class="str">"cmt">//--- 强迫中断程序
    class="kw">return(-class="num">1);
    }
  class="kw">return(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| EA 即时价格处理函数                                              |
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">void OnTick()
  {
  class=class="str">"cmt">//--- 以 iMA 指标当前值填充 MA[] 数组
  class=class="str">"cmt">//--- 我们将复制 class="num">100 个元素, 或如果出错则返回
  if(CopyBuffer(MA_handle,class="num">0,class="num">0,class="num">100,MA)<=class="num">0) class="kw">return;
  class=class="str">"cmt">//--- 设置 MA[] 顺序为时间序列
  ArraySetAsSeries(MA,true); 
  class=class="str">"cmt">//--- 此处我们可以利用数据做任何事
  }

常见问题

卡顿多源于调用方式不当而非算法本身;用脚本单独压测各均线耗时,能定位是否真有性能瓶颈。
计时受系统调度干扰,单次测量偏差大;应循环多次取平均,并避开盘口繁忙时段再测。
小布可调用内置诊断在对应品种页直接给出各类均线开销对比,你不用自己写压测脚本。
实测中直接 CopyBuffer 后自行计算更可控,iMA 封装调用有额外开销,脚本逐算更干净。
可用现成脚本逐个加载均线算法跑空图,记录耗时分布;重点看样本量放大后的差距。