RCI(順位相関指数)を使って、
買われすぎ・売られすぎの水準から戻ったタイミングで
逆張りエントリーするEAを作ります。
MT4にはRCIを計算する標準関数がないため、
既刊`rci-indicator-mt4.md`で自作したロジックを土台にして、
今回はインジケーターではなく発注まで行うEAとして実装します。
・RCIの計算ロジックをEAの発注条件に組み込む方法
・閾値をまたいだ瞬間だけエントリーする判定設計
・損切り必須・ポジション重複防止という資金管理の基本
・実際に動くEAのコード全体
RCI逆張りEAの全体像
まず、このEAがどういう流れで動くのかを確認します。
「またいだ瞬間」だけエントリーする設計
RCIが-80以下の間ずっとエントリーすると、
同じ方向に何度も新規注文を出してしまいます。
そこで、前の確定足では-80以下、
直近の確定足で-80を上回った「またいだ瞬間」だけをエントリー条件にします。
既刊インジケーターとの役割分担
・既刊rci-indicator-mt4.mdは「矢印で知らせる」だけのインジケーター
・本記事は同じ判定ロジックを使って「実際に発注する」EAに発展させたもの
・判定の考え方は共通だが、損切り・重複防止はEAだけに必要な要素
このEAが対応するポジションの数
シンプルさを優先し、
この記事のEAは同時に1ポジションまでしか持たない設計にしています。
ポジションを増やす拡張は、
後述の「よくある質問」で考え方だけ触れます。
RCI値を計算する関数を用意する
既刊インジケーターのRCI計算ロジックを、EA用に書き直します。
時間の順位と価格の順位を比べる考え方
RCIは、直近n本について
「時間が新しい順の順位」と「価格が高い順の順位」を比べ、
どれだけ一致しているかを-100〜+100で数値化したものです。
考え方の詳細は既刊rci-indicator-mt4.mdで解説しているため、
この記事ではEAに組み込む実装だけに絞って説明します。
価格の順位を数える関数
double GetPriceRank(int shift, int period)
{
double priceI = Close[shift];
int rank = 1;
for(int j = 0; j < period; j++)
{
if(Close[shift - period + 1 + j] > priceI)
rank++;
}
return(rank);
}
指定した足より価格が高い足の本数を数え、
その数+1を価格の順位にしています。
順位の差からRCI値を求める関数
double CalcRCI(int period, int baseShift)
{
double sumD2 = 0.0;
for(int i = 0; i < period; i++)
{
int targetShift = baseShift + i;
double priceRank = GetPriceRank(targetShift, period);
double timeRank = i + 1;
double d = timeRank - priceRank;
sumD2 += d * d;
}
return((1.0 - 6.0 * sumD2 / (period * (period * period - 1.0))) * 100.0);
}
時間の順位と価格の順位の差を二乗して合計し、
Spearman順位相関の公式に当てはめて-100〜+100に正規化しています。
エントリー条件を組み立てる
RCI値から、実際のエントリー判定を作ります。
確定済みの足だけを判定に使う
形成中の足(shift=0)は値が変わり続けるため、
判定には必ず確定済みの足(shift=1以降)だけを使います。
未確定の足を使うと、
同じシグナルが出たり消えたりする不具合の原因になります。
買いエントリーの条件
double rciNow = CalcRCI(InpRciPeriod, 1); double rciPrev = CalcRCI(InpRciPeriod, 2); bool buySignal = (rciPrev <= InpOversold && rciNow > InpOversold); bool sellSignal = (rciPrev >= InpOverbought && rciNow < InpOverbought);
直前の確定足が閾値の外側、
今回の確定足が閾値の内側に戻った瞬間だけを
trueにする条件です。
レンジ相場で張り付くとシグナルが出ない点
強いトレンド中はRCIが+80や-80に張り付いたまま動かず、
「またいだ瞬間」が発生しないことがあります。
これは不具合ではなく、
無駄な連続エントリーを防ぐための設計どおりの挙動です。
損切り・ポジション重複防止という資金管理の基本
発注する前に、必ず組み込むべき安全策を確認します。
損切り(StopLoss)は必ず設定する
・損切りを設定しないEAは、1回の逆行で大きな損失につながる
・この記事のEAはInpStopLossPipsで必ず損切り値を指定する設計にする
・0を指定しても動くコードにはしない(人為的に無効化できないようにする)
マジックナンバーで自分の注文だけを数える
int CountMyOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderMagicNumber() == InpMagicNumber && OrderSymbol() == _Symbol)
count++;
}
}
return(count);
}
他のEAや手動注文を数に含めないよう、
マジックナンバーと通貨ペアで絞り込みます。
ポジション重複エントリーを防ぐ
`CountMyOrders() == 0`を新規エントリーの条件に加えることで、
すでに1ポジション持っている状態での
二重発注を防ぎます。
実際に動くEAのコード全体
ここまでの要素をまとめたEAの全体です。
//+------------------------------------------------------------------+
//| RciReversalEA.mq4 |
//| Copyright 2026, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property strict
input int InpRciPeriod = 9; //RCI計算期間
input double InpOversold = -80.0;//売られすぎ閾値
input double InpOverbought = 80.0; //買われすぎ閾値
input double InpLots = 0.1; //発注ロット数
input int InpStopLossPips = 20; //損切り(pips)
input int InpSlippage = 3; //許容スリッページ
input int InpMagicNumber = 20261010;//マジックナンバー
double GetPriceRank(int shift, int period)
{
double priceI = Close[shift];
int rank = 1;
for(int j = 0; j < period; j++)
{
if(Close[shift - period + 1 + j] > priceI)
rank++;
}
return(rank);
}
double CalcRCI(int period, int baseShift)
{
double sumD2 = 0.0;
for(int i = 0; i < period; i++)
{
int targetShift = baseShift + i;
double priceRank = GetPriceRank(targetShift, period);
double timeRank = i + 1;
double d = timeRank - priceRank;
sumD2 += d * d;
}
return((1.0 - 6.0 * sumD2 / (period * (period * period - 1.0))) * 100.0);
}
int CountMyOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderMagicNumber() == InpMagicNumber && OrderSymbol() == _Symbol)
count++;
}
}
return(count);
}
int OnInit()
{
if(InpStopLossPips <= 0)
{
Alert("InpStopLossPipsは1以上を指定してください(損切り必須設計)");
return(INIT_PARAMETERS_INCORRECT);
}
return(INIT_SUCCEEDED);
}
void OnTick()
{
if(Bars < InpRciPeriod * 3)
return;
double rciNow = CalcRCI(InpRciPeriod, 1);
double rciPrev = CalcRCI(InpRciPeriod, 2);
bool buySignal = (rciPrev <= InpOversold && rciNow > InpOversold);
bool sellSignal = (rciPrev >= InpOverbought && rciNow < InpOverbought);
if(CountMyOrders() > 0)
return;
double pip = (Digits == 3 || Digits == 5) ? Point * 10 : Point;
if(buySignal)
{
double sl = Bid - InpStopLossPips * pip;
OrderSend(_Symbol, OP_BUY, InpLots, Ask, InpSlippage, sl, 0,
"RciReversalEA", InpMagicNumber, 0, clrBlue);
}
else if(sellSignal)
{
double sl = Ask + InpStopLossPips * pip;
OrderSend(_Symbol, OP_SELL, InpLots, Bid, InpSlippage, sl, 0,
"RciReversalEA", InpMagicNumber, 0, clrRed);
}
}
`pip`の計算で5桁(3桁)ブローカーに対応しているのは、
既刊の複数のEA記事と同じ、よくある落とし穴への対応です。
MetaEditorでのコンパイル・バックテスト手順
実際に動かすまでの手順を確認します。
- MetaEditorで新規EA「RciReversalEA」を作成し、上のコードを貼り付けてコンパイルする
- ストラテジーテスターで任意の通貨ペア・期間を指定し、「全ティック」モードでバックテストする
- InpRciPeriod・InpOversold・InpOverboughtを変えて、エントリー回数の変化を確認する
- InpStopLossPipsを0や負の値にして、Alertが出てEAが起動しないことを確認する
よくあるエラーと注意点
実装・運用でつまずきやすいポイントをまとめます。
OrderSendが失敗してもエラー処理がない
・このコードは解説用にOrderSendの戻り値チェックを省略している
・実運用では戻り値が-1の場合にGetLastError()で原因を調べる処理を追加する
・既刊get-last-error.mdのエラーコード確認方法が参考になる
RCIが張り付いてエントリーが来ない
強いトレンドの局面では、
RCIが閾値に張り付いたままシグナルが出ないことがあります。
これはエラーではなく、
むやみな逆張りを避けるための設計どおりの挙動です。
損切り幅を狭くしすぎるとすぐ決済される
InpStopLossPipsを極端に小さくすると、
通常のスプレッドの範囲内で損切りに達してしまうことがあります。
通貨ペアのスプレッドより十分に広い値を設定してください。
よくある質問
RCI逆張りEAについて、よく検索される疑問に回答します。
短期・中期・長期のRCIを組み合わせられますか?
できます。
`CalcRCI(InpRciPeriod, shift)`の期間を変えて複数回呼び出し、
短期が売られすぎ・中期が上向きのときだけエントリーする、
といった条件に拡張できます。
このEAは必ず勝てますか?
断定はできません。
RCIは順位の一致度を示す指標であり、
他のオシレーターと同様にダマシも発生します。
損切り設定は外さずに運用することをおすすめします。
決済ロジックを追加できますか?
できます。
この記事では損切りのみを実装していますが、
反対側のシグナルが出たタイミングでOrderClose()を呼ぶ処理を
OnTick()内に追加すれば、早期決済ロジックにできます。
【MQL4】RCI(順位相関指数)で逆張りエントリーするEAの作り方(自作RCI関数+損切り必須設計):まとめ
MT4に標準関数がないRCIも、
既刊インジケーターの計算ロジックを再利用すれば、
EAの発注条件として組み込めます。
「またいだ瞬間」だけ反応する判定と、
損切り必須・ポジション重複防止という資金管理の基本を
組み合わせることが、実運用に耐える設計の最低条件です。
コメント