EURUSDが上がっているのを見て、
「これはユーロが強いのか、それとも単にドルが弱いだけなのか」と迷ったことはないでしょうか。
既刊の通貨強弱メーターインジケーターの記事では、
8通貨の強弱をパネルに表示してこの疑問に答えましたが、
この記事ではそのロジックを「表示するだけ」から一歩進めて、
強弱の「差」が一定以上あるときだけ自動でエントリーするEAに発展させます。
この記事で分かること
・既刊インジケーターの通貨強弱計算ロジックを発注条件に転用する方法
・表示中の通貨ペアを基準通貨・決済通貨に分解する方法
・強弱差のしきい値をエントリー条件に使う考え方
・新しいバー判定・損切り必須・ポジション重複防止を備えた実際に動くコード全文
設計思想: なぜ通貨強弱の「差」を発注条件に使うのか
まず、通貨強弱の「差」に注目する理由を整理します。
既刊インジケーターとの関係
既刊記事では、7つの主要通貨ペアの変化率から、
主要8通貨(USD/EUR/GBP/JPY/AUD/NZD/CAD/CHF)の、
強弱を計算し、
パネルに一覧表示しました。
この記事では、同じ計算ロジックをそのまま使い、表示ではなく発注条件の判定に転用します。
1つの通貨ペアだけでは分からないこと
EURUSDが上がっていても、それがEURの強さなのかUSDの弱さなのかは、
EURUSD単体のチャートだけでは判断できません。
「差」を使う理由
補足
・通貨強弱メーターは「今どの通貨が強いか」を示すだけで、エントリーの判断は人間が行う
・本記事では、表示中の通貨ペアの基準通貨・決済通貨の強弱の差を数値化し
・その差がしきい値を超えたときだけ自動でエントリーする設計にする
通貨強弱の計算ロジック
既刊インジケーターのPercentChange関数を、そのまま使います。
PercentChangeで変化率を取る
double PercentChange(string symbol)
{
double past = iClose(symbol, 0, InpLookbackBars);
double now = iClose(symbol, 0, 0);
if(past == 0)
return(0); //履歴未取得時の保険(ゼロ除算防止)
return((now - past) / past * 100.0);
}
InpLookbackBars本前の終値と、直近の終値を比較した変化率(%)を返します。
USDは7ペアの平均、他7通貨は対USDペアそのまま
void GetStrengthValues(double &values[])
{
double chgEURUSD = PercentChange("EURUSD");
double chgGBPUSD = PercentChange("GBPUSD");
double chgAUDUSD = PercentChange("AUDUSD");
double chgNZDUSD = PercentChange("NZDUSD");
double chgUSDJPY = PercentChange("USDJPY");
double chgUSDCAD = PercentChange("USDCAD");
double chgUSDCHF = PercentChange("USDCHF");
values[0] = (-chgEURUSD - chgGBPUSD - chgAUDUSD - chgNZDUSD
+ chgUSDJPY + chgUSDCAD + chgUSDCHF) / 7.0; //USD
values[1] = chgEURUSD; //EUR
values[2] = chgGBPUSD; //GBP
values[3] = chgAUDUSD; //AUD
values[4] = chgNZDUSD; //NZD
values[5] = -chgUSDJPY; //JPY
values[6] = -chgUSDCAD; //CAD
values[7] = -chgUSDCHF; //CHF
}
既刊インジケーターと同じ考え方で、
USDが分母側(USDJPY等)にあるペアは符号を反転させています。
通貨コードから強弱配列のインデックスを探す
string CcyNames[8] = {"USD","EUR","GBP","AUD","NZD","JPY","CAD","CHF"};
int CurrencyIndex(string code)
{
for(int i = 0; i < 8; i++)
if(CcyNames[i] == code)
return(i);
return(-1);
}
対応していない通貨コードの場合は-1を返し、
発注条件の側でこのエラー値を必ずチェックします。
発注条件: 強弱差がしきい値を超えたら順張り
通貨強弱の計算ができたら、実際の発注条件を組み立てます。
表示中の通貨ペアをbase/quoteに分解する
string baseCode = StringSubstr(_Symbol, 0, 3); string quoteCode = StringSubstr(_Symbol, 3, 3);
EURUSDなら、baseCodeは”EUR”、quoteCodeは”USD”になります。
diff = base強さ – quote強さ
baseの強さからquoteの強さを引いた値をdiffとします。
diffが大きく正の値なら「baseが強くquoteが弱い」状態、
負の値なら逆の状態と判断できます。
InpThresholdの考え方
実践アドバイス
・InpThresholdを0に近づけるほどエントリー頻度が増える
・増えるほど、わずかな差でのダマシも増える
・まずは既定値0.3前後から試し、通貨ペアや時間足にあわせて調整する
新しいバー判定と損切り・ポジション重複防止
発注条件が整っても、資金管理の基本設計を省略しないようにします。
IsNewBar()で1バー1回に絞る
datetime LastBarTime = 0;
bool IsNewBar()
{
datetime t = Time[0];
if(t == LastBarTime)
return(false);
LastBarTime = t;
return(true);
}
強弱差はティックごとに細かく変動するため、この判定が無いと、
同じバーの中で何度も発注を試みてしまいます。
固定pipsの損切り
double PipSize()
{
if(Digits == 3 || Digits == 5)
return(Point * 10);
return(Point);
}
4桁(2桁)・5桁(3桁)どちらのブローカーでも、
同じpips数で損切り幅を指定できるようにしています。
OrdersTotalループでの重複防止
int GetMyTicket()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagic)
return(OrderTicket());
}
return(-1);
}
同じ通貨ペア・同じマジックナンバーのポジションがすでにある場合は、
新規エントリーをスキップします。
実際に動くコード全体
通貨強弱の計算から発注まで、実際にMetaEditorでコンパイルできるEAです。
//+------------------------------------------------------------------+
//| CurrencyStrengthFilterEA.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 InpLookbackBars = 20; //強弱を計算する期間(本数)
input double InpThreshold = 0.3; //エントリーする強弱差のしきい値(%)
input double InpLots = 0.1; //発注ロット数
input int InpStopLossPips = 30; //損切り(pips)
input int InpMagic = 20261025; //マジックナンバー
string Symbols7[7] = {"EURUSD","GBPUSD","AUDUSD","NZDUSD","USDJPY","USDCAD","USDCHF"};
string CcyNames[8] = {"USD","EUR","GBP","AUD","NZD","JPY","CAD","CHF"};
datetime LastBarTime = 0;
int OnInit()
{
for(int i = 0; i < 7; i++)
SymbolSelect(Symbols7[i], true); //Market Watchに追加(iCloseの0返り対策)
return(INIT_SUCCEEDED);
}
double PipSize()
{
if(Digits == 3 || Digits == 5)
return(Point * 10);
return(Point);
}
bool IsNewBar()
{
datetime t = Time[0];
if(t == LastBarTime)
return(false);
LastBarTime = t;
return(true);
}
double PercentChange(string symbol)
{
double past = iClose(symbol, 0, InpLookbackBars);
double now = iClose(symbol, 0, 0);
if(past == 0)
return(0);
return((now - past) / past * 100.0);
}
void GetStrengthValues(double &values[])
{
double chgEURUSD = PercentChange("EURUSD");
double chgGBPUSD = PercentChange("GBPUSD");
double chgAUDUSD = PercentChange("AUDUSD");
double chgNZDUSD = PercentChange("NZDUSD");
double chgUSDJPY = PercentChange("USDJPY");
double chgUSDCAD = PercentChange("USDCAD");
double chgUSDCHF = PercentChange("USDCHF");
values[0] = (-chgEURUSD - chgGBPUSD - chgAUDUSD - chgNZDUSD
+ chgUSDJPY + chgUSDCAD + chgUSDCHF) / 7.0;
values[1] = chgEURUSD;
values[2] = chgGBPUSD;
values[3] = chgAUDUSD;
values[4] = chgNZDUSD;
values[5] = -chgUSDJPY;
values[6] = -chgUSDCAD;
values[7] = -chgUSDCHF;
}
int CurrencyIndex(string code)
{
for(int i = 0; i < 8; i++)
if(CcyNames[i] == code)
return(i);
return(-1);
}
int GetMyTicket()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagic)
return(OrderTicket());
}
return(-1);
}
void CheckStrengthEntry()
{
if(GetMyTicket() >= 0)
return;
string baseCode = StringSubstr(_Symbol, 0, 3);
string quoteCode = StringSubstr(_Symbol, 3, 3);
int baseIdx = CurrencyIndex(baseCode);
int quoteIdx = CurrencyIndex(quoteCode);
if(baseIdx < 0 || quoteIdx < 0)
{
Print("対応していない通貨コードのため判定をスキップしました: ", _Symbol);
return;
}
double values[8];
GetStrengthValues(values);
double diff = values[baseIdx] - values[quoteIdx];
double sl_pips = InpStopLossPips * PipSize();
if(diff > InpThreshold)
{
double sl = Ask - sl_pips;
int result = OrderSend(_Symbol, OP_BUY, InpLots, Ask, 3, sl, 0,
"CcyStrength Buy", InpMagic, 0, clrBlue);
if(result < 0)
Print("買い注文に失敗しました。エラーコード=", GetLastError());
return;
}
if(diff < -InpThreshold)
{
double sl = Bid + sl_pips;
int result = OrderSend(_Symbol, OP_SELL, InpLots, Bid, 3, sl, 0,
"CcyStrength Sell", InpMagic, 0, clrRed);
if(result < 0)
Print("売り注文に失敗しました。エラーコード=", GetLastError());
return;
}
}
void OnTick()
{
if(!IsNewBar())
return;
CheckStrengthEntry();
}
MetaEditorでのコンパイル・実行手順
- MetaEditorで新規Expert Advisor「CurrencyStrengthFilterEA」を作成し、上のコードを貼り付ける
- F7キーでコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
- デモ口座でEURUSD等のチャートにドラッグ&ドロップで適用する
- ストラテジーテスターで、強弱差がInpThresholdを超えた瞬間にだけ発注される様子を確認する
よくあるエラーと注意点
実際に運用する前に確認しておきたい点をまとめます。
ブローカーのシンボルサフィックスでズレる
注意
・ブローカーによっては通貨ペア名の末尾に.aや.pro等のサフィックスが付く
・この場合、_SymbolをそのままStringSubstrに渡すと、
base/quoteの切り出しがずれる
・サフィックス付きの環境では、先にサフィックスを取り除く処理を追加する必要がある
対応していない通貨コードではスキップされる
CcyNames配列に無い通貨コード(例: トルコリラ等)の通貨ペアで動かすと、
CurrencyIndex()が-1を返し、
Printでログを出したうえでエントリーをスキップする設計になっています。
InpThresholdを小さくしすぎるとオーバートレードになる
強弱差は常に揺れ動いているため、しきい値を極端に小さくすると、
新しいバーが来るたびに発注を繰り返すオーバートレード状態になりやすい点に注意してください。
MQL4とMQL5の違い
このロジックをMQL5に移植する場合の違いを確認します。
iCloseの引数構成が変わる
MQL4のiClose(symbol, timeframe, shift)は、
MQL5には同名関数がなく、
CopyCloseで配列を取得してからインデックスで参照する設計になります。
発注はCTradeクラスに変わる
OrderSendの代わりに、
CTradeクラスのBuy()/Sell()メソッドを使う設計になります。
通貨強弱の計算ロジック自体は、MQL5でもほぼそのまま応用できます。
よくある質問
通貨強弱フィルターEAについて、よくある疑問に回答します。
既刊の通貨強弱メーターと同時に使えますか?
使えます。
このEAは独自にPercentChangeを計算しているため、
既刊インジケーターをチャートに表示しながら、同時にEAを動かすことができます。
7ペア以外の通貨ペアでも使えますか?
基準通貨・決済通貨の両方がCcyNames配列の8通貨に含まれていれば使えます。
例えばEURGBPやEURJPYのようなクロスペアでも、
baseCode・quoteCodeがそれぞれEUR/GBP、
EUR/JPYとなるため動作します。
InpThresholdの推奨値はどれくらいですか?
まずは既定値0.3前後から試すことを推奨します。
通貨ペアや時間足、InpLookbackBarsの長さによって、
強弱差の出やすさが変わるため、バックテストで調整してください。
【MQL4】通貨強弱の差が一定以上のときだけ順張りエントリーするEAの作り方(通貨強弱メーターの考え方を発注条件に):まとめ
EURUSDが上がっていても、それがEURの強さかUSDの弱さかは、
その通貨ペア単体では分かりません。
既刊インジケーターと同じ計算ロジックで8通貨の強弱を数値化し、
表示中の通貨ペアの基準通貨・決済通貨の強弱差がしきい値を超えたときだけ、
新しいバー判定・損切り必須・重複エントリー防止を備えた、
エントリーを行う設計にまで落とし込みました。
コメント