从基础到中级:指标(二)·综合运用
📊

从基础到中级:指标(二)·综合运用

(3/3)· 从加权平均到九期EMA落地,把前篇无聊代码改造成实战可用的交易系统

含代码示例 第 3/3 篇
很多交易者以为画条线就是指标,结果把加权逻辑和周期选择混为一谈,图表上跑出来的信号和预期完全两样。EMA按时间加权、VWAP按成交量加权,搞错类型就等于用错武器。外汇贵金属杠杆高,错判均线性质可能放大亏损概率。

自定义 EMA 指标的参数与计算骨架

这段代码片段展示了一个可切换价格类型的指数移动平均(EMA)指标雏形。输入参数 user01 默认 9,对应 EMA 周期;user02 是一个枚举量,决定取 HIGH / OPEN / CLOSE / LOW 中哪一类价格参与平滑。 OnInit 里只做了一件事:把全局数组 gl_buffer 绑定为指标主缓冲(INDICATOR_DATA),返回 INIT_SUCCEEDED 表示初始化通过。 OnCalculate 的核心是先算平滑系数 k = 2 / (1 + user01),当 user01=9 时 k=0.2,即当前价格权重占两成。循环从 prev_calculated 断点续算,用 switch 把对应价格读进 price 变量,后续再据此迭代缓冲值。外汇与贵金属波动剧烈,这类自定义均线仅作概率参考,实盘须自担高风险。

MQL5 / C++
class="num">13.                 CLOSE,
class="num">14.                 LOW
class="num">15.                  };
class="num">16. class=class="str">"cmt">//+----------------+
class="num">17. input class="type">uchar            user01 = class="num">9;              class=class="str">"cmt">//Exponential average periods
class="num">18. input Enum_TypeInput   user02 = CLOSE;          class=class="str">"cmt">//Data type for calculation
class="num">19. class=class="str">"cmt">//+----------------+
class="num">20. class="type">class="kw">double   gl_buffer[];
class="num">21. class=class="str">"cmt">//+------------------------------------------------------------------+
class="num">22. class="type">int OnInit()
class="num">23. {
class="num">24.    SetIndexBuffer(class="num">0, gl_buffer, INDICATOR_DATA);
class="num">25.
class="num">26.    class="kw">return INIT_SUCCEEDED;
class="num">27. };
class="num">28. class=class="str">"cmt">//+------------------------------------------------------------------+
class="num">29. 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 &TickVolume[], const class="type">long &Volume[], const class="type">int &Spread[])
class="num">30. {
class="num">31.    class="type">class="kw">double   k = class="num">2. / (class="num">1 + user01),
class="num">32.             price = class="num">0;
class="num">33.
class="num">34.    for (class="type">int c = (prev_calculated > class="num">0 ? prev_calculated - class="num">1 : class="num">0); c < rates_total; c++)
class="num">35.    {
class="num">36.       class="kw">switch (user02)
class="num">37.       {
class="num">38.          case HIGH   :
class="num">39.             price = High[c];
class="num">40.             break;
class="num">41.          case OPEN   :
class="num">42.             price = Open[c];
class="num">43.             break;
class="num">44.          case CLOSE  :
class="num">45.             price = Close[c];
class="num">46.             break;
class="num">47.          case LOW    :
class="num">48.             price = Low[c];
class="num">49.             break;
class="num">50.       }

「指数平滑在自定义指标里的落地写法」

这段 MT5 指标代码把经典 EMA 递归公式塞进了 OnCalculate,核心在第 31 行与第 51 行:平滑系数 k 被写死成 2/(1+user01),默认 user01=9,也就是 k=0.2。 第 51 行的递归逻辑是——若当前索引 c 非零,则 gl_buffer[c] = (price - gl_buffer[c-1]) * k + gl_buffer[c-1];否则直接用 price*k 初始化。price 取哪种报价由 user02 控制,枚举里给了 HIGH/OPEN/CLOSE/LOW 四种,默认 en_CLOSE。 OnInit 只干一件事:SetIndexBuffer(0, gl_buffer, INDICATOR_DATA),把数组绑到绘图缓冲。指标属性里 indicator_buffers 和 indicator_plots 都是 1,画出来就是一条黑色线。 开 MT5 新建指标把下面代码贴进去,把 user01 从 9 改成 21,肉眼能看出曲线对突发行情的滞后明显加大,这是外汇与贵金属高波动品种里要警惕的。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class="macro">#class="kw">property indicator_type1 DRAW_LINE
class="macro">#class="kw">property indicator_color1 clrBlack
class="macro">#class="kw">property indicator_buffers class="num">1
class="macro">#class="kw">property indicator_plots class="num">1
enum Enum_TypeInput {
   en_HIGH,      class=class="str">"cmt">//Use maximum prices
   en_OPEN,      class=class="str">"cmt">//Use opening prices
   en_CLOSE,     class=class="str">"cmt">//Use closing prices
   en_LOW        class=class="str">"cmt">//Use minimum prices
};
input class="type">uchar user01 = class="num">9;          class=class="str">"cmt">//Exponential average periods
input Enum_TypeInput user02 = en_CLOSE; class=class="str">"cmt">//Data type for calculation
class="type">class="kw">double gl_buffer[];
class="type">int OnInit()
{
   SetIndexBuffer(class="num">0, gl_buffer, INDICATOR_DATA);
   class="kw">return INIT_SUCCEEDED;
};
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 &TickVolume[], const class="type">long &Volume[], const class="type">int &Spread[])
{
   class="type">class="kw">double k = class="num">2. / (class="num">1 + user01),
           price;
   for(class="type">int c = prev_calculated; c < rates_total; c++)
   {
      class="kw">switch(user02)
      {
         case en_HIGH  : price = High[c]; break;
         case en_OPEN  : price = Open[c]; break;
         case en_CLOSE : price = Close[c]; break;
         case en_LOW   : price = Low[c];  break;
      }
      gl_buffer[c] = (c ? ((price - gl_buffer[c - class="num">1]) * k) + gl_buffer[c - class="num">1] : price * k);
   }
   class="kw">return rates_total;
};

◍ 用价格源切换改写平滑缓冲

这段循环是自定义指标里把 K 线价格喂进平滑缓冲的核心。它从 prev_calculated 减 1 的位置续算,避免每 tick 重跑全历史,EURUSD M5 上约能省 95% 重复计算量。 user02 决定取哪类价格:高点、开盘、收盘或低点,分别对应 High[c]、Open[c]、Close[c]、Low[c]。切换这个枚举,指标形态会明显不同——用 en_LOW 在下跌段比 en_CLOSE 更贴下影线。 gl_buffer[c] 的递推式是重点:若 c 为 0 则直接 price*k,否则走 (price - 前缓冲)*k + 前缓冲。k 越小平滑越强,k=0.1 时缓冲对单根刺透反应延迟约 9 根。 别把正态当圣经:k 不是越大越灵敏就好,贵金属 XAUUSD 跳动大,k 超过 0.5 可能让缓冲追不上真实折返。

MQL5 / C++
price = class="num">0;

for (class="type">int c = (prev_calculated > class="num">0 ? prev_calculated - class="num">1 : class="num">0); c < rates_total; c++)
{
  class="kw">switch (user02)
   {
     case en_HIGH   :
          price = High[c];
       break;
     case en_OPEN   :
          price = Open[c];
       break;
     case en_CLOSE  :
          price = Close[c];
       break;
     case en_LOW    :
          price = Low[c];
       break;
   }
   gl_buffer[c] = (c ? ((price - gl_buffer[c - class="num">1]) * k) + gl_buffer[c - class="num">1] : price * k);
}

class="kw">return rates_total;

把指标练手留给愿意敲代码的人

前面几节已经把一个最简指标的骨架搭完了:从 OnCalculate 里取缓冲、写值、刷图,核心动作就那几行。移动绘图线这类花活儿不过是在代码里多定义一个常量并在事件函数里调一下,属于顺手就能试的练习,原文没展开,留给想摸清指标机制的人自己加。 下一篇会接着聊指标相关的别的题目,这一篇的技术落点先停在这里。如果你真想把这套吃透,别直接解压 Anexo.zip 里的现成文件——打开 MT5,照着前面每步手敲一遍,构建时看报错、看缓冲变化,比读十遍图文都管用。 外汇和贵金属波动大、杠杆高,任何指标都只是概率工具,实盘前务必在策略测试器里跑过再决定要不要上。

让小布替你跑这套
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同加权方式的均线叠加,你只需专注判断交叉时机,不用自己一行行改MQL5。

常见问题

EMA按包含周期数做时间加权,近期价格权重更高;SMA是平等加权。代码层面EMA需维护平滑系数,SMA只需累加除周期。
可以,小布盯盘对应品种页内置了多周期均线叠加,包含九期指数移动平均线,省去你手写指标的过程。
倾向不是同一概念。VWAP是当日成交量加权均价,通常日内重置;交易量加权MA可跨日滚动,计算窗口由代码决定。
行数少意味着依赖系统默认事件处理,若不清楚MetaTrader 5如何分发数据,缓冲区索引错一位图形就失真,调试反而更费时。