开发轴心均值振荡器:一款新颖的累积移动平均线指标·进阶篇
📘

开发轴心均值振荡器:一款新颖的累积移动平均线指标·进阶篇

第 2/2 篇

「PMO 指标的缓冲区与均值支撑函数」

PMO 指标不需要从公式反推输入,只需三个外部参数:起始时间 startingTime 作为计算基准点;MA_close 默认 3,决定收盘 PM 信号均值的样本数;MA_open 默认 21,决定开盘 PM 信号均值的样本数。这三个数直接控制底层信号平滑度,调小则灵敏、调大则滞后。 指标内部开三个缓冲区:PMOBuffer 存最终输出,closeBuffer 与 openBuffer 分别承接收盘、开盘派生的 PM 信号。全局变量里放一个计数器 counter(初值 1)、sum_close 与 sum_open 两个累加器,另加 first_val_checked 和 first_valid_index 用来锁定起始时间之后的首个有效索引。 均值计算函数 simpleMA 要绕开一个坑:在起始时间附近,缓冲区头部没有足够元素。按 PM 定义,越界部分直接补 1,均值就不会凭空滞后。下面这段是核心声明的骨架,含输入、缓冲区和 simpleMA 实现。 外汇与贵金属波动剧烈,PMO 仅作概率参考,实盘前请在 MT5 策略测试器用历史数据验证参数敏感性。

MQL5 / C++
class=class="str">"cmt">//--- class="kw">input parameters
class="kw">input class="type">class="kw">datetime startingTime;
class="kw">input class="type">int      MA_close=class="num">3;
class="kw">input class="type">int      MA_open=class="num">21;
class=class="str">"cmt">//--- indicator buffers
class="type">class="kw">double   PMOBuffer[];
class="type">class="kw">double   closeBuffer[];
class="type">class="kw">double   openBuffer[];  
class=class="str">"cmt">//----global vars---
class="type">int counter=class="num">1;
class="type">class="kw">double sum_close=class="num">0;
class="type">class="kw">double sum_open=class="num">0;
class="type">bool first_val_checked;
class="type">int first_valid_index;
class="type">class="kw">double simpleMA(class="kw">const class="type">int pos, class="kw">const class="type">int avg_positions, class="kw">const class="type">class="kw">double &data[],class="type">int arr_tail_pos){
   class="type">class="kw">double _local_sum = class="num">0;
   for(class="type">int i=pos+avg_positions-class="num">1; i >= pos;i++){
      if(i > arr_tail_pos){
         _local_sum += class="num">1;  class=class="str">"cmt">// when requested data exceed buffer limit set trailing 1s
      }else{
         _local_sum += data[i];
      }
             
   }
   class="kw">return _local_sum/avg_positions;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator iteration function                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime &time[],
                class="kw">const class="type">class="kw">double &open[],
                class="kw">const class="type">class="kw">double &high[],
                class="kw">const class="type">class="kw">double &low[],
                class="kw">const class="type">class="kw">double &close[],
                class="kw">const class="type">long &tick_volume[],
                class="kw">const class="type">long &volume[],
                class="kw">const class="type">int &spread[])
  {
class=class="str">"cmt">//---
   ArraySetAsSeries(PMOBuffer,true);
   ArraySetAsSeries(closeBuffer,true);
   ArraySetAsSeries(openBuffer,true);
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(time,true);



class=class="str">"cmt">//--- class="kw">return value of prev_calculated for next call
   class="type">int total = rates_total - prev_calculated;
   class="type">class="kw">double _tmp_sum_close,_tmp_sum_open;
    if(total > class="num">0){

从起点时间倒推的 PMO 累加逻辑

这段循环是 PMO 指标在 MT5 里落地的核心:从 K 线数组尾部(total-1)往 0 号柱倒推,只处理 time[i] 大于等于 startingTime 的柱。倒序遍历能保证累加和(sum_close / sum_open)按时间先后累积,避免未来函数偏移。 首次命中有效时间阈值的柱会被标记为 first_valid_index,后续 simpleMA 调用都以它为基准起点。counter 从 0 开始自增,closeBuffer[i] 与 openBuffer[i] 分别用 close[i]*counter/sum_close 与 open[i]*counter/sum_open 归一,再相减得到 PMOBuffer[i]。 若 time[i] 早于 startingTime,PMOBuffer 直接置 0,不污染统计窗口。实时第 0 根柱走 else 分支,用 _tmp_sum_close 把当前 tick 的 close[0] 并入累加和再算缓冲值,保证最新价即时反映。 开 MT5 把这段塞进自定义指标 OnCalculate,把 startingTime 设成你盯盘的会话起点,就能看到 PMO 在指定窗口内的真实摆动。外汇与贵金属杠杆高,PMO 仅作概率倾向参考,不代表方向确定性。

MQL5 / C++
for(class="type">int i=total-class="num">1;i>=class="num">0;i--){
      if(time[i] >= startingTime){
            if(first_val_checked == class="kw">false){
                  first_val_checked = true;
                  first_valid_index = i;
            }
            
            sum_close += close[i];
            sum_open += open[i];
            
            closeBuffer[i] = close[i] *counter / sum_close;
            openBuffer[i] = open[i]*counter / sum_open;
            
            PMOBuffer[i] = simpleMA(i,MA_close,closeBuffer,first_valid_index)-simpleMA(i,MA_open,openBuffer,first_valid_index);
            counter++;
      }
      else{
            PMOBuffer[i] = class="num">0;
      }
      
   }
 }
else{
      _tmp_sum_close = sum_close +close[class="num">0];
      _tmp_sum_open = sum_open + open[class="num">0];
      closeBuffer[class="num">0] = close[class="num">0] *counter / _tmp_sum_close;

      openBuffer[class="num">0] = open[class="num">0] *counter / _tmp_sum_open;

      PMOBuffer[class="num">0] = simpleMA(class="num">0,MA_close,closeBuffer,first_valid_index)-simpleMA(class="num">0,MA_open,openBuffer,first_valid_index);
 }
 class="kw">return(rates_total);
}
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ EURUSD 上 PMO 与 RSI 的实战对照

在 EURUSD 图表跑 PMO,最直观的发现是它和 RSI(14) 几乎重合,但 2019 年 9 月是个分水岭。9 月 12 日前后欧洲央行释放官方通讯,PMO 上走出左肩高于右肩的“M”形(Merrill 形态),而同期 RSI 与 EURUSD 价格恰恰相反——因为 9 月价格上推时,12 日 PM 相对次日的 CMA 偏高,这条偏离对第二峰后的空头布局有参考价值。 PMO 不受 RSI 的 0–100 边界限制,零轴穿越可直接视作趋势确认。把信号压缩成 IF-THEN 规则并不复杂:零线下正反转→早警买入;零线上负反转→早警卖出;上穿零线→建议买入;下穿零线→建议卖出。外汇与贵金属波动剧烈、杠杆风险高,单一信号不可盲从,实盘中建议叠加其他确认。 响应速度由参数决定:PMO(m,n) 里 m 管振荡器滞后,n 管零轴交叉滞后。PMO(1,1) 最快,等于两期 PM 差分不做均化;折衷可用 PMO(1,5),既保快响应,零轴交叉也不至于太拖。 数值层面,H1 周期取 2019 年前 8 个月 EURUSD 跑 PMO(3,21),分布呈以零为中心的钟形,略左偏可能源于当时堆积的空头持仓。零附近的对称优势暗示多空短期总体平衡。PMO 与其非规范化版本 R² 强相关,说明常规化未扭曲开盘/收盘底层信号,还方便跨品种比对。

「PMO 还能往哪挖」

轴心均值振荡器(PMO)本质是拿累积移动平均线(CMA)套在轴心均值(PM)概念上做的指标,挂在 MT5 里和 RSI、MACD 同属振荡器家族,但 CMA 的平滑逻辑让它对中枢的贴合更死,拐点滞后更小。 原文给出的延伸方向里,最值得动手的是基于 PMO 值绕零轴的统计分布写智能系统——也就是说,先跑一批历史数据看 PMO 在正负区间的概率密度,再拿偏离均值的标准差当过滤。 附带的指标文件直接拖进 MT5 的 Indicators 目录就能编译验证,建议先调 CMA 窗口长度看零轴穿越频率的变化,外汇和贵金属波动大,这类统计策略实盘前务必用小资金测,高风险。

随文附带的 PMO 源码与日志版

这篇 PMO 文章在末尾打包了四份可直接拖进 MT4/MT5 的源码:PMO.mq4(4.32 KB)、PMO_logger.mq4(4.93 KB)、PMO.mq5(4.5 KB)、PMO_logger.mq5(5.06 KB)。其中带 logger 的两个版本多暴露了 data_logging(true/false 开关)和 filename(日志文件名字符串)两个输入参数,跑历史行情时能把 PMO 数值落盘,方便你用 Python 或 Excel 后续做分布检验。 外汇与贵金属市场杠杆高、滑点跳空频繁,PMO 单独使用可能给出信号,但作者未提供任何市场的胜率统计,实盘前务必在策略测试器里用自有数据回测。 下载 ZIP 后建议先编译 PMO_logger.mq5,把 data_logging 设为 true,选一个品种跑 2023 年全年 M5 数据,看日志里 PMO 穿越零轴的延迟是否在你容忍范围内。

常见问题

按均值线、上轨、下轨分三个独立缓冲区,索引从 0 顺次排,初始化时用 SetIndexBuffer 绑定,避免共用同一数组导致互相覆盖。
取当前 K 线时间减去 N 根周期得到起点,循环时从起点向当前遍历累加典型价,再除以计数得到累积均值,注意过滤非交易时段空值。
小布可加载 PMO 与 RSI 双窗对照,自动标出两者在 EURUSD 上的方向分歧并推送提醒,省去手动切图比对。
震荡市 PMO 靠均值支撑过滤了部分毛刺,假信号倾向少于 RSI;但趋势市两者都易钝化,需结合价格行为确认。
日志版每行打印累加值与缓冲区写入,便于排查某根 K 线算错;不懂代码也可看输出时间点对照自己图表找异常。