既刊のiMFI(Money Flow Index)インジケーターの記事のFAQでは、
「買われすぎ・売られすぎの領域に入ったらEAの条件に使える」
という使い方を予告していました。
ですが、このサイトには既に同じ「閾値に入ったら反転エントリー」
という骨格のEAが複数あります。
この記事ではあえて、価格とiMFIが逆方向に動く、
「ダイバージェンス(逆行現象)」を検知して反転エントリーするEAを作ります。
・既刊の閾値反転EA群とダイバージェンス判定の違い
・スイング高値・安値を検知するフラクタル型の判定関数
・価格とiMFIの逆行を比較する方法
・損切り必須・ポジション重複防止の書き方
・実際に動くEAのコード全体
なぜ閾値判定ではなく「逆行」を狙うのか
まず、既刊の反転EA群と本記事の違いを整理します。
既刊iMFIインジケーターのおさらい
既刊インジケーターは、MFIが80以上(買われすぎ)・20以下(売られすぎ)の、
領域に入った瞬間を矢印で知らせる設計でした。
既刊の閾値反転EA群との違い
このサイトには、CCI・Williams%R・DeMarkerという、
3つの指標でそれぞれ「閾値に入ったら反転」という骨格のEAが既にあります。
| EA | 判定方法 | 見ているもの |
|---|---|---|
| 既刊CCI逆張りEA | ±100超えの閾値 | その瞬間の値の大きさ |
| 既刊Williams%R逆張りEA | -80/-20の閾値 | その瞬間の値の大きさ |
| 既刊DeMarkerフィルターEA | 0.3/0.7の閾値 | その瞬間の値の大きさ |
| 本記事(iMFIダイバージェンスEA) | 価格とiMFIの向きの不一致 | 2つのスイング間の変化の方向 |
既刊の3件はいずれも「今の値が大きいか小さいか」だけを見ていますが、
本記事は「価格とiMFI、2つの動きの向きが一致しているか」を見ます。
ダイバージェンス(逆行現象)という設計思想
・価格が新高値を更新してもiMFIが更新できていない状態を「弱気の逆行」とみなす
・価格が新安値を更新してもiMFIが更新できていない状態を「強気の逆行」とみなす
・勢いが価格に追いついていない(息切れしている)とみなす、閾値判定とは別の発想
スイング高値・安値とダイバージェンスの判定ロジック
発注条件の土台になる、2段階の判定ロジックを確認します。
iMFI関数のシグネチャ(既刊記事で確認済み)
double iMFI( string symbol, // 通貨ペア名(NULLで現在のチャート) int timeframe, // 時間足(0で現在の時間足) int period, // 計算期間 int shift // 何本前のバーの値を取得するか );
引数の構成は既刊imfi-mt4.mdが、
2026-10-31にMQL4公式リファレンスで直接確認済みのものと同じです。
スイング高値・安値を検知するフラクタル型の判定関数
bool IsSwingHigh(int s, int k)
{
for(int j = 1; j <= k; j++)
{
if(High[s] <= High[s - j] || High[s] <= High[s + j])
return(false);
}
return(true);
}
int FindSwingHigh(int fromShift, int maxShift, int k)
{
for(int s = fromShift; s <= maxShift - k; s++)
if(IsSwingHigh(s, k))
return(s);
return(-1);
}
前後k本よりも高値が高いバーだけを、
スイング高値として検知します。
安値側も同じ考え方で`IsSwingLow`/`FindSwingLow`を作ります。
直近2つのスイングを比較してダイバージェンスを判定する
直近のスイング高値(h1)と、その1つ前のスイング高値(h2)を見つけ、
価格は更新しているのにiMFIは更新していない状態を検知します。
bool bearishSignal = false;
int h1 = FindSwingHigh(InpSwingK, InpLookback, InpSwingK);
if(h1 == InpSwingK)
{
int h2 = FindSwingHigh(h1 + 1, InpLookback, InpSwingK);
if(h2 != -1)
{
double mfi1 = iMFI(_Symbol, 0, InpMfiPeriod, h1);
double mfi2 = iMFI(_Symbol, 0, InpMfiPeriod, h2);
if(High[h1] > High[h2] && mfi1 < mfi2)
bearishSignal = true;
}
}
`h1 == InpSwingK`という条件は、
そのスイングが「今まさに確定した」瞬間だけに絞るための工夫です。
発注条件・損切り・ポジション重複の防止
判定だけでは発注にならないため、安全策を組み込みます。
安値側のダイバージェンスも同じ考え方で判定する
bool bullishSignal = false;
int l1 = FindSwingLow(InpSwingK, InpLookback, InpSwingK);
if(l1 == InpSwingK)
{
int l2 = FindSwingLow(l1 + 1, InpLookback, InpSwingK);
if(l2 != -1)
{
double mfi1 = iMFI(_Symbol, 0, InpMfiPeriod, l1);
double mfi2 = iMFI(_Symbol, 0, InpMfiPeriod, l2);
if(Low[l1] < Low[l2] && mfi1 > mfi2)
bullishSignal = true;
}
}
価格は新安値を更新しているのに、
iMFIは前回の安値より高い(売られすぎの勢いが弱まっている)状態を検知します。
損切り・利確を必ず指定する
double sl = High[h1] + InpStopLossBufferPips * point; double tp = Bid - InpTakeProfitPips * point; OrderSend(_Symbol, OP_SELL, InpLots, Bid, 3, sl, tp, "MfiDivergence", InpMagic, 0, clrRed);
損切り(`sl`)はスイング高値のさらに外側に置き、
利確(`tp`)・損切りを発注時点で必ず指定し、未設定のまま放置しません。
ポジション重複を防ぐチェック
bool HasOpenPosition()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) &&
OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagic)
return(true);
}
return(false);
}
同じマジックナンバー・同じ通貨ペアでポジションが既にあるかを確認し、
あれば新規エントリーをスキップします。
実際に動くiMFIダイバージェンスEAのコード全体
ここまでの要素をまとめた、EAの全体です。
//+------------------------------------------------------------------+
//| MfiDivergenceEA.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
input int InpMfiPeriod = 14; //MFIの計算期間
input int InpSwingK = 3; //スイング判定の前後本数
input int InpLookback = 60; //スイングを探す最大遡り本数
input double InpLots = 0.1; //発注ロット数
input int InpStopLossBufferPips = 150; //スイングからさらに離す損切り幅(pips)
input int InpTakeProfitPips = 400; //利確(pips)
input int InpMagic = 20261104; //マジックナンバー
datetime lastBarTime = 0;
bool IsSwingHigh(int s, int k)
{
for(int j = 1; j <= k; j++)
{
if(High[s] <= High[s - j] || High[s] <= High[s + j])
return(false);
}
return(true);
}
bool IsSwingLow(int s, int k)
{
for(int j = 1; j <= k; j++)
{
if(Low[s] >= Low[s - j] || Low[s] >= Low[s + j])
return(false);
}
return(true);
}
int FindSwingHigh(int fromShift, int maxShift, int k)
{
for(int s = fromShift; s <= maxShift - k; s++)
if(IsSwingHigh(s, k))
return(s);
return(-1);
}
int FindSwingLow(int fromShift, int maxShift, int k)
{
for(int s = fromShift; s <= maxShift - k; s++)
if(IsSwingLow(s, k))
return(s);
return(-1);
}
bool HasOpenPosition()
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) &&
OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagic)
return(true);
}
return(false);
}
void OnTick()
{
if(Time[0] == lastBarTime)
return;
lastBarTime = Time[0];
int needed = InpLookback + InpSwingK + 2;
if(Bars < needed)
return;
bool bearishSignal = false;
int h1 = FindSwingHigh(InpSwingK, InpLookback, InpSwingK);
if(h1 == InpSwingK)
{
int h2 = FindSwingHigh(h1 + 1, InpLookback, InpSwingK);
if(h2 != -1)
{
double mfi1 = iMFI(_Symbol, 0, InpMfiPeriod, h1);
double mfi2 = iMFI(_Symbol, 0, InpMfiPeriod, h2);
if(High[h1] > High[h2] && mfi1 < mfi2)
bearishSignal = true;
}
}
bool bullishSignal = false;
int l1 = FindSwingLow(InpSwingK, InpLookback, InpSwingK);
if(l1 == InpSwingK)
{
int l2 = FindSwingLow(l1 + 1, InpLookback, InpSwingK);
if(l2 != -1)
{
double mfi1 = iMFI(_Symbol, 0, InpMfiPeriod, l1);
double mfi2 = iMFI(_Symbol, 0, InpMfiPeriod, l2);
if(Low[l1] < Low[l2] && mfi1 > mfi2)
bullishSignal = true;
}
}
if(HasOpenPosition())
return;
double point = _Point;
if(_Digits == 3 || _Digits == 5)
point *= 10;
if(bearishSignal)
{
double sl = High[h1] + InpStopLossBufferPips * point;
double tp = Bid - InpTakeProfitPips * point;
int ticket = OrderSend(_Symbol, OP_SELL, InpLots, Bid, 3, sl, tp,
"MfiDivergence", InpMagic, 0, clrRed);
if(ticket < 0)
Print("売り発注に失敗しました。エラーコード: ", GetLastError());
}
else if(bullishSignal)
{
double sl = Low[l1] - InpStopLossBufferPips * point;
double tp = Ask + InpTakeProfitPips * point;
int ticket = OrderSend(_Symbol, OP_BUY, InpLots, Ask, 3, sl, tp,
"MfiDivergence", InpMagic, 0, clrBlue);
if(ticket < 0)
Print("買い発注に失敗しました。エラーコード: ", GetLastError());
}
}
スイング高値・安値とiMFIの向きが一致しない瞬間だけ、
新規エントリーが発生します。
MetaEditorでのコンパイル・バックテスト手順
実際に動かすまでの手順です。
- MetaEditorで新規Expert Advisor「MfiDivergenceEA」を作成し、上のコードを貼り付ける
- F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
- ストラテジーテスターでこのEAを選択し、任意の通貨ペア・期間でバックテストを実行する
- 「結果」タブで、スイングの向きとiMFIの向きが逆になった直後にエントリーが発生していることを確認する
- InpSwingK・InpLookbackを変えて、シグナルの頻度と検知の精度がどう変わるかを確認する
よくあるエラーと注意点
実際に運用する前に、確認しておきたい点です。
InpLookbackが短すぎて2つ目のスイングが見つからない
・InpLookbackが短いと1つ目は見つかっても2つ目が見つからない
・この場合シグナルは出ない(h2が-1のまま)ため、エラーにはならず安全に働く
・スイングの出現間隔は時間足で変わるため
・InpLookbackは通貨ペア・時間足で調整する
フラクタル判定の確定には時間差がある
`IsSwingHigh`はInpSwingK本先のバーまで見て判定するため、
スイングが確定するのはそのK本分、遅れたタイミングです。
本記事では`h1 == InpSwingK`という条件で、
確定した瞬間だけを捉える設計にしています。
コンパイルエラーになる
`FindSwingHigh`/`FindSwingLow`の引数は、
3個すべて指定してください。
`k`だけを省略することはできません。
よくある質問
iMFIダイバージェンスEAについて、よく検索される疑問に回答します。
・既刊iMFIインジケーターを併用すれば、値の推移を目視でも確認できる
・InpSwingKを大きくするほど、大きめのスイングだけに絞られる
・既刊の閾値反転EA群と同時運用する場合は、マジックナンバーを分けて区別する
既刊のCCI逆張りEA等と何が違いますか?
判定の対象が違います。
既刊の3つのEAは「今の値が閾値を超えたかどうか」という1点だけを見ていますが、
本記事は「価格とiMFI、2つの動きの向きが一致しているか」という関係性を見ています。
同じ時間帯でも、両方のEAが同時にシグナルを出すとは限りません。
ダイバージェンスは必ず反転しますか?
いいえ。
ダイバージェンスはあくまで「勢いが息切れしているサイン」であり、
その後も価格がさらに同方向へ動き続けることがあります。
本記事のEAも損切りを必ず設定しており、
反転が起きなかった場合は損切りで撤退する前提で設計しています。
iMFI以外のオシレーターでも同じ考え方を使えますか?
使えます。
本記事のスイング比較の部分は、
iMFIの値を取得している箇所を既刊iRSI等に置き換えるだけで、
同じダイバージェンス判定のロジックとして応用できます。
【MQL4】iMFI(Money Flow Index)の逆行現象「ダイバージェンス」を検知して反転エントリーするEAの作り方(閾値反転EA群との違いも解説):まとめ
この記事では、既刊iMFIインジケーターのFAQで予告されていた閾値反転ではなく、
価格とiMFIの逆行現象(ダイバージェンス)を検知して、
反転エントリーするEAを解説しました。
既刊のCCI・Williams%R・DeMarkerの閾値反転EA群とは、
『今の値の大きさ』ではなく、
『2つのスイング間の向きの一致・不一致』を見る点で判定の軸を変えています。
損切り必須・ポジション重複防止という最低限の安全策を備えた上で、
InpSwingK・InpLookbackの調整で、
スイングの検知粒度を変えられる構成にしています。
コメント