MACDのヒストグラム(OsMA)を表示していても、

ゼロラインをまたいだ瞬間を目で追い続けるのは、意外と見落としがちです。

この記事では、OsMAがゼロラインを下から上へ、

または上から下へクロスした確定足だけに、矢印を表示するインジケーターを作ります。

OsMAとは何か、MACDとの関係

OsMA(Oscillator of Moving Average)は、

MACDのメインライン(2本の移動平均線の差)と、

シグナルライン(メインラインの移動平均)の、

差をヒストグラムで表したものです。

補足: MT4の標準MACDインジケーターは、

実はメインラインとシグナルラインの2本ではなく、

ヒストグラム(OsMA相当)とメインラインを表示しています。

この記事のOsMAは、そのヒストグラム部分に相当します。

OsMAがプラスならメインラインがシグナルラインより上、

マイナスなら下にある状態を意味します。

なぜ「ゼロクロスの瞬間」だけを検知したいのか

「OsMAがプラスかマイナスか」という条件だけで矢印を出すと、

プラスが続く間ずっと矢印が出続けてしまいます。

要点: 知りたいのは「プラスの状態が続いていること」ではなく、

「いまマイナスからプラスに切り替わったこと」です。

状態ではなく、変化を検知する設計にします。

現在値だけの条件の弱点

osma > 0

この条件だけでは、OsMAがプラスの間の全ての足が条件を満たします。

1本前との比較でクロスを検知する

クロスの瞬間だけを検知するには、1本前の値との比較を加えます。

//下から上へゼロラインを超えた
currentOsma > 0 && previousOsma <= 0

//上から下へゼロラインを割った
currentOsma < 0 && previousOsma >= 0

形成中の0番足は値が変化し続けるため、

この記事のコードは1本前(確定足)を基準に判定します。

iOsMA関数の引数

double iOsMA(
   string symbol,
   int    timeframe,
   int    fast_ema_period,
   int    slow_ema_period,
   int    signal_period,
   int    applied_price,
   int    shift
);

`fast_ema_period`・`slow_ema_period`、

`signal_period`のこの3つは、

標準的なMACDの設定(12・26・9)と揃えるのが一般的です。

`shift`に1を指定すると、1本前の確定足の値を取得できます。

実際に動くコード全体(ゼロクロス矢印インジケーター)

チャート上に、上クロスを緑の矢印、

下クロスを赤の矢印で表示する、実際にMetaEditorでコンパイルできるコードです。

//+------------------------------------------------------------------+
//|                                  OsmaCrossArrow.mq4                |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 LimeGreen
#property indicator_color2 Tomato

input int FastEmaPeriod   = 12;
input int SlowEmaPeriod   = 26;
input int SignalPeriod    = 9;
input int ArrowGapPoints  = 30;//高値・安値から矢印を離すポイント数

double UpArrow[];
double DownArrow[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   SetIndexBuffer(0, UpArrow);
   SetIndexStyle(0, DRAW_ARROW, STYLE_SOLID, 2);
   SetIndexArrow(0, 233);
   SetIndexEmptyValue(0, EMPTY_VALUE);

   SetIndexBuffer(1, DownArrow);
   SetIndexStyle(1, DRAW_ARROW, STYLE_SOLID, 2);
   SetIndexArrow(1, 234);
   SetIndexEmptyValue(1, EMPTY_VALUE);

   IndicatorShortName("OsMA Zero Cross Arrow");
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                 const int prev_calculated,
                 const datetime &time[],
                 const double &open[],
                 const double &high[],
                 const double &low[],
                 const double &close[],
                 const long &tick_volume[],
                 const long &volume[],
                 const int &spread[])
  {
   int needed = SlowEmaPeriod + SignalPeriod + 3;
   if(rates_total < needed)
      return(0);

   int limit = (prev_calculated == 0) ? rates_total-2 : rates_total-prev_calculated+1;
   if(limit > rates_total-2)
      limit = rates_total-2;

   for(int i=limit; i>=1; i--)
     {
      double currentOsma  = iOsMA(NULL, 0, FastEmaPeriod, SlowEmaPeriod, SignalPeriod, PRICE_CLOSE, i);
      double previousOsma = iOsMA(NULL, 0, FastEmaPeriod, SlowEmaPeriod, SignalPeriod, PRICE_CLOSE, i+1);

      UpArrow[i]   = EMPTY_VALUE;
      DownArrow[i] = EMPTY_VALUE;

      if(currentOsma > 0 && previousOsma <= 0)
         UpArrow[i] = Low[i] - ArrowGapPoints * Point;
      else if(currentOsma < 0 && previousOsma >= 0)
         DownArrow[i] = High[i] + ArrowGapPoints * Point;
     }

   return(rates_total);
  }
//+------------------------------------------------------------------+

MetaEditorで実際に動かす手順

  1. MT4のMetaEditorで「ファイル」→「新規作成」→「カスタムインディケータ」を選ぶ
  2. 「OsmaCrossArrow」という名前で作成し、テンプレートの中身をすべて削除して上のコードを貼り付ける
  3. F7キーでコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  4. チャートへ適用し、標準MACDインジケーターも同時に表示する
  5. MACDのヒストグラムがゼロラインをまたぐ足で、矢印が出ることを目視で確認する

よくあるエラーと対処

  • 矢印が全く表示されない: `needed`の本数に足りるヒストリーデータがあるか確認してください。読み込み直後は表示までに少し時間がかかることがあります
  • 矢印が価格に近すぎて見づらい: `ArrowGapPoints`を大きくしてください。5桁ブローカーでは30ポイントでも近く感じる場合があります
  • 同じ方向の矢印が連続で出る: 1本前との比較条件が正しく効いていない可能性があります。`previousOsma`に渡す`shift`が`i+1`になっているか確認してください
  • MACDのクロスと矢印のタイミングがずれる: MACDのゴールデンクロス(2本の移動平均線の交差)と、OsMAのゼロクロスは別の条件です。次の見出しで違いを解説します

MACDのゴールデンクロスとの違い

MACDのゴールデンクロス・デッドクロスは、

メインラインとシグナルラインという2本の線の交差を見る手法です。

OsMAのゼロクロスは、その2本の「差」がゼロを超えた瞬間を見る手法です。

実は同じ現象: メインラインとシグナルラインの差がOsMAなので、

2本のクロスとOsMAのゼロクロスは、

理論上は同じタイミングで発生します。

違いが出る場面: 表示上の見やすさは異なります。

線の交差は目で追いにくい一方、

ヒストグラムの符号反転は判定しやすい、という実務上の差があります。

OsMAゼロクロスの使いどころと限界

OsMAのゼロクロスは、トレンドの初動を捉えやすい一方、

レンジ相場ではゼロライン付近を何度も往復し、

ダマシのシグナルが連発しやすい弱点があります。

注意: このインジケーターは矢印を表示するだけで、

トレンドの強さまでは判定していません。

ADXなど別の指標と組み合わせて、

レンジ相場のシグナルを除外する設計が実務的です。

よくある質問

OsMAゼロクロスの実装でよく検索されている疑問を、3つにまとめて回答します。

EAのエントリー条件にそのまま使えますか?

この記事のコードは表示専用のインジケーターです。

`currentOsma`と`previousOsma`を比較するif文の部分だけを、

EAの`OnTick()`内にコピーすれば、エントリー条件として使えます。

適用価格をPRICE_CLOSE以外にできますか?

`iOsMA()`の第6引数(`applied_price`)を、

`PRICE_TYPICAL`等の他の定数に変更すれば、

終値以外の平均価格でOsMAを計算できます。

上位足のOsMAを判定する方法はありますか?

`iOsMA()`の第2引数(`timeframe`)に、

`PERIOD_H4`等の上位足を指定すれば、

下位足のチャートのまま上位足のOsMAを取得できます。

【MQL4】OsMAのゼロクロスだけを矢印表示するインジケーターの作り方|MACDのクロスとの違いも解説:まとめ

この記事では、OsMAがゼロラインをクロスした確定足だけに、

矢印を表示するインジケーターの作り方を解説しました。

「プラスかマイナスか」という状態ではなく、

「いま切り替わったか」という変化を検知することが、

矢印を連発させないための設計のポイントです。

OsMAのゼロクロスはMACDのゴールデンクロスと理論上は同じ現象ですが、

ヒストグラムの符号反転として見る方が判定しやすい、という実務上の利点があります。

レンジ相場のダマシを減らすには、ADXなど別の指標との組み合わせも検討してください。

関連記事