从零开始在MQL5中实现移动平均线:简单明了·综合运用
(3/3)· 前兩篇鋪好基礎,這篇把四種均線的算法與提速寫法收攏成可複用代碼骨架
「把计算结果写回缓冲区」
指标计算的核心收尾动作,是把每一根 K 线的均值结果存进自定义缓冲区,供图表渲染与后续调用。
代码中 MABuffer[i]=result; 将当前下标 i 对应的平滑值写入缓冲区数组,i 与 K 线序号一一对应,遗漏这一步图表上不会画出任何线。
函数末尾 return(rates_total); 告诉终端本次已处理的总柱数,下一轮 OnCalculate 会据此跳过已算部分,避免重复计算拖慢 MT5。外汇与贵金属品种跳动频繁,这种返回约定直接影响实时刷新开销,属于高风险环境下的必要优化。
class=class="str">"cmt">//--- Write the calculation result to the buffer MABuffer[i]=result; } class=class="str">"cmt">//--- class="kw">return the number of bars calculated for the next OnCalculate call class="kw">return(rates_total); }
◍ SMMA 为什么比 SMA 和 EMA 更抗噪
平滑移动平均线(SMMA)对价格尖刺的反应明显弱于简单移动平均线和指数移动平均线。把周期设为 10 的收盘价 SMMA 拉到图上,你会看到它比同周期 SMA 更顺滑,剧烈波动被压下去了,更适合判断稳定趋势方向。 它的首值是 N 个收盘价的普通平均,之后每一根都用上一根 SMMA 乘 (N-1) 再加当前收盘价,最后除以 N。这种递归写法让旧价格以线性衰减方式留存在曲线里,而不是像 EMA 那样按固定系数指数衰减。 原文给的初版指标在每次新 tick 都重算全部历史 K 线,实盘里会越跑越慢。正确做法是首次启动算全量历史,之后只更新当前 K 线——下面是核心片段与逐行拆解。 SUM1 = SUM(CLOSE(i), N) // 取最近 N 根收盘价求和,作为首值基础 SMMA1 = SUM1 / N // 首根 SMMA 等于这个平均 SMMA(i) = (SMMA1*(N-1) + CLOSE(i)) / N // 次根用首值递推 PREVSUM = SMMA(i-1) * N // 上一根平滑总和 SMMA(i) = (PREVSUM - SMMA(i-1) + CLOSE(i)) / N // 后续值的等价写法 SMMA(i) = (SMMA(i-1) * (N-1) + CLOSE(i)) / N // 最简递推式,每 tick 只算这一行 外汇与贵金属波动大、杠杆高,SMMA 只是降低假信号概率,不代表趋势判断必然可靠,实盘前请在 MT5 用历史数据验证周期参数。
SUM1 = SUM(CLOSE(i), N) SMMA1 = SUM1 / N SMMA(i) = (SMMA1*(N-class="num">1) + CLOSE(i)) / N PREVSUM = SMMA(i - class="num">1) * N SMMA(i) =(PREVSUM - SMMA(i - class="num">1) + CLOSE(i)) / N SMMA(i) = (SMMA(i - class="num">1) * (N - class="num">1) + CLOSE(i)) / N class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| SMMAonPriceCloseRAW.mq5 | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">1 class="macro">#class="kw">property indicator_plots class="num">1 class=class="str">"cmt">//--- plot MA class="macro">#class="kw">property indicator_label1 "SMMA" class="macro">#class="kw">property indicator_type1 DRAW_LINE class="macro">#class="kw">property indicator_color1 clrRed class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- input parameters input class="type">int InpPeriod=class="num">10; class=class="str">"cmt">// SMMA Period class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double MABuffer[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- indicator buffers mapping SetIndexBuffer(class="num">0,MABuffer,INDICATOR_DATA); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator iteration function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[],
平滑移动平均的递推写法
下面这段 MQL5 片段实现了基于收盘价序列的平滑移动平均(SMMA),核心在首次用简单移动平均(SMA)打底,其后改用递推公式。递推式为 result=(MABuffer[i-1]*(InpPeriod-1)+close[i])/InpPeriod,比每根 K 线重算一遍快得多。 循环从 i=InpPeriod-1 起步,说明前 InpPeriod-1 根柱线不输出数值,只在第 InpPeriod 根才写入首个 SMMA 值。若你拿 EURUSD 的 M15 跑 InpPeriod=10,前 9 根会留空,第 10 根起才有曲线。 外汇与贵金属杠杆高、滑点随机,指标仅作概率参考,实盘请先在 MT5 策略测试器验证再上仓。
const class="type">class="kw">double &high[], const class="type">class="kw">double &low[], const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) { class=class="str">"cmt">//--- class="type">class="kw">double result=class="num">0.0; for(class="type">int i=InpPeriod-class="num">1; i<rates_total; i++) { class=class="str">"cmt">//--- first calculation if(i==InpPeriod-class="num">1) { class=class="str">"cmt">//--- calculate a simple moving average for the InpPeriod first bars for(class="type">int j=class="num">0; j<InpPeriod; j++) { class="type">class="kw">double price=close[i-j]; result+=price; } result/=InpPeriod; class=class="str">"cmt">//--- write the first displayed SMMA value calculated as SMA MABuffer[InpPeriod-class="num">1]=result; } class=class="str">"cmt">//--- all subsequent calculations else result=(MABuffer[i-class="num">1]*(InpPeriod-class="num">1)+close[i])/InpPeriod; class=class="str">"cmt">//--- Write the calculation result to the buffer MABuffer[i]=result; } class=class="str">"cmt">//--- class="kw">return value of prev_calculated for the next call class="kw">return(rates_total); }
「SMMA 指标骨架里那几个坑」
自己写平滑移动平均(SMMA)指标时,MT5 的 OnInit 只干一件事:把 MABuffer 绑到 0 号缓冲区,返回 INIT_SUCCEEDED。真正麻烦的在 OnCalculate 的边界处理。 输入周期 InpPeriod 如果小于 1,代码里会强制回退到 10——这意味着你在外部参数面板填 0 或负数,指标不会报错,而是悄悄用 10 根 K 线算。rates_total 小于 ExtPeriod 时直接 return 0,新手常以为指标坏了,其实是历史数据不够。 首次运行(prev_calculated==0)走 SMA 播种:前 ExtPeriod 根 close 求平均,写进 MABuffer[ExtPeriod-1]。之后每根新 bar 从 prev_calculated-1 续算,避免全量重算拖慢 EURUSD 这类tick密集品种。外汇和贵金属杠杆高,这类自定义指标算错一个偏移,信号就可能整体右移一根,回测与实盘会明显背离。 下面这段是可直接贴进 MT5 自定义指标用的核心骨架,注意注释里的空值初始化和起始位设定。
input class="type">int InpPeriod=class="num">10; class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double MABuffer[]; class=class="str">"cmt">//--- global variables class="type">int ExtPeriod; class=class="str">"cmt">// SMMA calculation period class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- indicator buffers mapping SetIndexBuffer(class="num">0,MABuffer,INDICATOR_DATA); class=class="str">"cmt">//--- successful initialization class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator iteration function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[], const class="type">class="kw">double &high[], const class="type">class="kw">double &low[], const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) { class=class="str">"cmt">//--- Adjust the entered value of the SMMA calculation period ExtPeriod=(InpPeriod<class="num">1 ? class="num">10 : InpPeriod); class=class="str">"cmt">//--- If there is not enough historical data for calculation, class="kw">return class="num">0 if(rates_total<ExtPeriod) class="kw">return class="num">0; class="type">class="kw">double result=class="num">0; class="type">int start =class="num">0; class=class="str">"cmt">//--- If this is the first run or a change in historical data if(prev_calculated==class="num">0) { class=class="str">"cmt">//--- initialize the indicator buffer with an empty value and set the start of the calculation to ExtPeriod ArrayInitialize(MABuffer,EMPTY_VALUE); start=ExtPeriod; class=class="str">"cmt">//--- calculate a simple moving average for the ExtPeriod first bars for(class="type">int i=class="num">0; i<start; i++) result+=close[i]; result/=ExtPeriod; class=class="str">"cmt">//--- write the first displayed SMMA value calculated as SMA MABuffer[start-class="num">1]=result; } else start=prev_calculated-class="num">1; class=class="str">"cmt">//--- in the loop from the &class="macro">#x27;start&class="macro">#x27; value to the current history bar class=class="str">"cmt">//--- calculate the SMMA value for the loop current bar for(class="type">int i=start; i<rates_total; i++)
◍ SMA 递推那一行到底在算什么
上面这行把经典简单移动平均写成了递推形式:用前一刻的均值乘 (周期-1) 再加当前收盘价,最后除以周期。它等价于把最近 ExtPeriod 根收盘价取平均,但省掉了每根 K 线重跑整段窗口的循环开销。 MQL5 里 OnCalculate 返回 rates_total,是告诉终端“本次从 0 到 rates_total-1 的柱都已算完”,下一 tick 进来就直接接着尾部追加,不必重算历史。若你改了 ExtPeriod 或指标缓冲区逻辑,却忘了同步返回值的含义,图表可能出现尾部空白或重复绘制。 直接把这段贴进自定义指标试:把 ExtPeriod 设成 50 跑 EURUSD 日线,对比系统内置 SMA 曲线,两者应完全重合;外汇与贵金属杠杆高、滑点大,验证时先用模拟盘或离线数据,别拿实盘赌代码手感。
MABuffer[i]=(MABuffer[i-class="num">1]*(ExtPeriod-class="num">1)+close[i])/ExtPeriod; class=class="str">"cmt">//--- class="kw">return the number of bars calculated for the next OnCalculate call class="kw">return(rates_total); }
LWMA 的权重倾斜与低效重算陷阱
线性加权移动平均线把最大权重压在当前 K 线(编号 0),越往历史走权重线性递减,到编号 N-1 的 K 线权重直接为 0。和 SMA 比,它对最近价格变动更敏感,在高波动行情里这种倾斜能让信号更快转向,但也可能放大噪音。 原始演示指标 LWMAonPriceCloseRAW 每来一个 Tick 就把全部历史 K 线重新算一遍,公式即 LWMA = SUM(CLOSE(i) * i, N) / SUM(i, N)。在 10 周期收盘价序列上,这等于每 tick 重复做 N 次乘加,实盘 Tick 密集时 CPU 占用会明显偏高,属于典型的可优化写法。 下文给出的优化版思路是:加输入周期校验(比如拒绝 ≤0 的周期),并把计算改成增量式——只在必要时更新末尾值,而不是全量重算。外汇与贵金属杠杆高、波动剧烈,直接挂未优化指标在 1 分钟图上可能拖慢终端响应。
LWMA = SUM(CLOSE(i) * i, N) / SUM(i, N) class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| LWMAonPriceCloseRAW.mq5 | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">1 class="macro">#class="kw">property indicator_plots class="num">1 class=class="str">"cmt">//--- plot MA class="macro">#class="kw">property indicator_label1 "LWMA" class="macro">#class="kw">property indicator_type1 DRAW_LINE class="macro">#class="kw">property indicator_color1 clrRed class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- input parameters input class="type">int InpPeriod=class="num">10; class=class="str">"cmt">// LWMA Period class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double MABuffer[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- indicator buffers mapping SetIndexBuffer(class="num">0,MABuffer,INDICATOR_DATA); class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator iteration function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[], const class="type">class="kw">double &high[], const class="type">class="kw">double &low[],
「线性加权均线在MT5里的手写循环」
下面这段是直接在 MT5 自定义指标里手算 LWMA 的核心循环,不依赖内置 iMA 的加权模式,方便你改权重逻辑。外汇与贵金属波动剧烈,自写指标仅作辅助参考,实盘高风险。 外层循环从 InpPeriod-1 跑到 rates_total,保证前 InpPeriod-1 根bar不计算。内层对每根bar回看 InpPeriod 根收盘,权重按距当前远近线性递增:越近的bar乘的系数越大。 权重和 wsum 用整数累加 j(从 InpPeriod 到 1),sum 累加 close[i-j+1]*(InpPeriod-j+1),最后 result=sum/wsum 写进 MABuffer[i]。默认 InpPeriod=10,改这个数就能调灵敏度。 指标头里把 plot 设成红实线、buffer 数 1,初始化段声明 MABuffer[] 与 ExtPeriod 即可接上这段计算。复制进 MQ5 编译,能在图表上直接画出一条倾向突出近期价格的 LWMA。
const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) { class=class="str">"cmt">//--- in the loop from InpPeriod-class="num">1 to the current bar class="type">class="kw">double result=class="num">0.0; for(class="type">int i=InpPeriod-class="num">1; i<rates_total; i++) { class=class="str">"cmt">//--- calculate weights(wsum) and the sum of weight coefficients(sum) of InpPeriod bars class="type">class="kw">double sum =class="num">0.0; class="type">int wsum=class="num">0; for(class="type">int j=InpPeriod; j>class="num">0; j--) { wsum+=j; sum +=close[i-j+class="num">1]*(InpPeriod-j+class="num">1); } class=class="str">"cmt">//--- get the LWMA value for the current bar of the loop result=sum/wsum; class=class="str">"cmt">//--- Write the calculation result to the buffer MABuffer[i]=result; } class=class="str">"cmt">//--- class="kw">return value of prev_calculated for the next call class="kw">return(rates_total); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| LWMAonPriceClose.mq5 | class=class="str">"cmt">//| Copyright class="num">2024, MetaQuotes Ltd. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">1 class="macro">#class="kw">property indicator_plots class="num">1 class=class="str">"cmt">//--- plot MA class="macro">#class="kw">property indicator_label1 "LWMA" class="macro">#class="kw">property indicator_type1 DRAW_LINE class="macro">#class="kw">property indicator_color1 clrRed class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- input parameters input class="type">int InpPeriod = class="num">10; class=class="str">"cmt">// LWMA Period class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double MABuffer[]; class=class="str">"cmt">//--- global variables class="type">int ExtPeriod; class=class="str">"cmt">// LWMA calculation period class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function |
◍ LWMA 指标的 OnInit 与 OnCalculate 骨架
自定义指标在 MT5 里跑起来,靠的是 OnInit 完成缓冲区映射、OnCalculate 逐根 K 线算值。下面这段就是最精简的 LWMA(线性加权移动平均)指标核心函数,直接丢进 MQ5 文件就能编译出线。 OnInit 里只做一件事:把 MABuffer 绑到指标主缓冲(索引 0),返回 INIT_SUCCEEDED 表示初始化成功。若少了 SetIndexBuffer,图表上不会画出任何东西。 OnCalculate 的参数列表是固定的:rates_total 是历史总数,prev_calculated 是上一次已算过的柱数,后面跟着开高低收等数组。函数开头先把用户输入周期 InpPeriod 兜底成 10(若小于 1),再判断历史不足 ExtPeriod 根就直接 return 0,避免越界。 计算起点分两种情况:首次运行(prev_calculated==0)时清空缓冲并从第 ExtPeriod-1 根开始;后续 tick 只从 prev_calculated-1 根续算,这是 MT5 指标增量计算的标准写法,能省 CPU。 内层循环对每根 bar 做 ExtPeriod 权重累加:权重 wsum 为 1+2+…+ExtPeriod,sum 为各收盘价乘对应线性权重,最后 result=sum/wsum 写入 MABuffer[i]。外汇与贵金属波动大,LWMA 对近期价格敏感,信号可能更跳,实盘前务必在 MT5 策略测试器用历史数据验证。
class="type">int OnInit() { class=class="str">"cmt">//--- indicator buffers mapping SetIndexBuffer(class="num">0,MABuffer,INDICATOR_DATA); class=class="str">"cmt">//--- successful initialization class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator iteration function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnCalculate(const class="type">int rates_total, const class="type">int prev_calculated, const class="type">class="kw">datetime &time[], const class="type">class="kw">double &open[], const class="type">class="kw">double &high[], const class="type">class="kw">double &low[], const class="type">class="kw">double &close[], const class="type">long &tick_volume[], const class="type">long &volume[], const class="type">int &spread[]) { class=class="str">"cmt">//--- Adjust the entered value of the LWMA calculation period ExtPeriod=(InpPeriod<class="num">1 ? class="num">10 : InpPeriod); class=class="str">"cmt">//--- If there is not enough historical data for calculation, class="kw">return class="num">0 if(rates_total<ExtPeriod) class="kw">return class="num">0; class="type">class="kw">double result=class="num">0; class="type">int start =class="num">0; class=class="str">"cmt">//--- If this is the first run or a change in historical data if(prev_calculated==class="num">0) { class=class="str">"cmt">//--- initialize the indicator buffer with an empty value and set the start of the calculation to ExtPeriod-class="num">1 ArrayInitialize(MABuffer,EMPTY_VALUE); start=ExtPeriod-class="num">1; } class=class="str">"cmt">//--- If this is not the first run, the calculation will start at the current bar else start=prev_calculated-class="num">1; class=class="str">"cmt">//--- in the loop from the &class="macro">#x27;start&class="macro">#x27; value to the current history bar class=class="str">"cmt">//--- calculate the LWMA value for the loop current bar for(class="type">int i=start; i<rates_total; i++) { class=class="str">"cmt">//--- calculate weights(wsum) and the sum of weight coefficients(sum) of ExtPeriod bars class="type">class="kw">double sum =class="num">0.0; class="type">int wsum=class="num">0; for(class="type">int j=ExtPeriod; j>class="num">0; j--) { wsum+=j; sum +=close[i-j+class="num">1]*(ExtPeriod-j+class="num">1); } class=class="str">"cmt">//--- get the LWMA value for the current bar of the loop result=sum/wsum; class=class="str">"cmt">//--- Write the calculation result to the buffer MABuffer[i]=result; } }
把已处理柱数交还给下一次计算
在自定义指标的 OnCalculate 收尾处,直接 return(rates_total) 是把当前已经计算过的 K 线总数交还系统。 MT5 在下一次报价到来时会以这个返回值作为 prev_calculated 传入,避免从零重算整段历史。 若你在这里返回了比 rates_total 小的数,指标会反复重绘前面已算过的柱,CPU 占用和视觉闪烁都会上升。 开 MT5 随便挂一个自己写的指标,把末行改成 return(0) 对比一下,就能看到重算带来的卡顿差异。
class=class="str">"cmt">//--- class="kw">return the number of bars calculated for the next OnCalculate call class="kw">return(rates_total); }
「四类均线该怎么挑不踩坑」
MT5 里最常用的四类均线——SMA、EMA、SMMA、LWMA,底层权重逻辑完全不同,直接决定你对行情的反应速度。SMA 给周期内每根 K 线同等权重,EMA 和 LWMA 给近期价格更高权重,SMMA 则介于 EMA 与 SMA 之间做了二次平滑。 SMA 胜在直观,长线看方向、找支撑阻力很稳,但所有价格平权意味着它对外汇、贵金属这种突发波动品种反应偏慢,信号常滞后 1~2 根 K 线。EMA 对近期数据敏感,短线抓入场快,可金价秒级跳水时容易出假突破。 SMMA 抗噪强,震荡市里比 EMA 少很多毛刺,代价是转向速度跟 SMA 差不多。LWMA 按线性权重压最新价,适应最快,但计算量明显高于前三者,且在 EURUSD 五分钟这种上下扫的行情里同样会打假信号。 开 MT5 把同一品种切到 M15,分别挂 SMA(20)、EMA(20)、SMMA(20)、LWMA(20),肉眼比对去年非农夜的拐点,就能确认哪类均线在你的周期上更不容易吃假信号。贵金属与外汇杠杆高,均线只是概率工具,任何交叉都需结合风控。
◍ 四种均线的实战取舍
把周期统一设为 10 来跑同一条价格序列,红色 SMA、绿色 EMA、金色 SMMA、蓝色 LWMA 的分离度非常直观:SMMA 几乎削平了短期毛刺,整体趋势方向最干净;EMA 和 LWMA 因为给最近收盘价更高权重,拐头速度明显快于 SMA。外汇与贵金属属于高杠杆高风险品种,这种灵敏度差异在镑美或 XAUUSD 上会被放大,选型前先在 MT5 回放几根日线确认。 落到具体识别表现:SMA 曲线最平滑,但支撑阻力位滞后;EMA 贴价紧、反转反应快;SMMA 比 SMA 还平滑,对突发波动迟钝;LWMA 响应速度第一,波动却高于 EMA 和 SMMA。实盘里常见做法是叠加两条不同类均线,例如 SMA(50) 定方向、EMA(10) 找入场,而非单押一种。 文末给的 9 个 mq5 附件就是照此思路拆开的:SMAonPriceCloseRAW 是裸算不做优化,SMAonPriceCloseECO 上了资源节约型计算,SMAonPriceClose 再叠优化处理;EMA、SMMA、LWMA 各有一 RAW 一 ECO 两版。想验证结论,直接把 SMAonPriceCloseRAW.mq5(4.86 KB)和 SMMAonPriceClose.mq5(6.63 KB)拖进 MT5 同一图表,周期都设 10,肉眼就能比出平滑度。 下面这段调用标准库 CiMA 的脚本,是快速把 SMA(14) 挂上当前图的最简办法,比手写缓冲区省事: #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "[MQL5官方文档] #property version "1.00" #include <Indicators\Trend.mqh> CiMA ma; //+------------------------------------------------------------------+
| // | Script program start function |
|---|
//+------------------------------------------------------------------+ int OnInit(){ ma.DeleteFromChart(ChartID(), 0); ma.Create(_Symbol, PERIOD_CURRENT, 14, 0, MODE_SMA, PRICE_CLOSE); ma.AddToChart(PERIOD_CURRENT, 0);
class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#include <Indicators\Trend.mqh> CiMA ma; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Script program start function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit(){ ma.DeleteFromChart(ChartID(), class="num">0); ma.Create(_Symbol, PERIOD_CURRENT, class="num">14, class="num">0, MODE_SMA, PRICE_CLOSE); ma.AddToChart(PERIOD_CURRENT, class="num">0);
别急着下结论
上面这段 OnTick 只做了 ma.Refresh() 和取主线第 0 根值,连 Print 都注释掉了,说明它只是把移动均线对象挂上 tick 刷新,还没接任何信号逻辑。 真要拿它做价格行为过滤,得在 curMA 后面补交叉判断或斜率判断,而不是盯着一个数发呆。外汇和贵金属杠杆高,光有均线刷新不等于能进场,回测前先想清楚你拿它卡什么阈值。 留个尾巴:把注释行打开,跑一周 EURUSD M5,看 curMA 在亚盘和美盘的跳动频率差多少,再决定要不要加缓冲带。
class="kw">return INIT_SUCCEEDED; } class="type">void OnTick(){ ma.Refresh(); class="type">class="kw">double curMA = ma.Main(class="num">0); class=class="str">"cmt">//Print("当前MA值:", maValue); }