EURUSDとGBPUSDを両方持っていて、「これでリスクを分散できている」
と思っていないでしょうか。
2つの通貨ペアが強く相関していれば、
それは分散ではなく、実質的に同じ賭けの重複かもしれません。
この記事では、複数の通貨ペアの値動きの相関係数を計算し、
チャート上のパネルに一覧表示するインジケーターを作ります。
・通貨ペアの相関とは何か、なぜ知る必要があるか
・ピアソンの相関係数の計算方法
・複数シンボルのデータを取得する際の注意点
・実際に動くパネル表示インジケーターのコード全体
通貨ペアの相関とは何か・なぜ知りたいのか
まず、「相関」という考え方を整理します。
相関とは何か(一緒に動くか、逆に動くか)
相関係数は、2つの通貨ペアが、同じ方向に動く傾向が強いか(正の相関)、
逆方向に動く傾向が強いか(負の相関)を、数値で表したものです。
相関を知らずに複数ポジションを持つリスク
・EURUSDの買いとGBPUSDの買いは、見た目は別の2ポジションに見える
・相関が強い場合、実質的には同じ方向への賭けを2倍にしているだけ
・損切りが同時に重なるリスクを過小評価しやすい
既刊「通貨強弱メーター」との違い
既刊の通貨強弱メーターは、USDやEUR等「単独の通貨」の強さを計算するものでした。
この記事の相関は、「2つの通貨ペアの関係」を計算する、別の概念です。
ピアソンの相関係数の計算方法
実際の計算方法を確認します。
相関係数の意味(+1〜-1の範囲)
相関係数は`+1`(完全に同じ方向に動く)から、
`-1`(完全に逆方向に動く)の範囲の値になります。
`0`に近いほど、関係が薄いことを意味します。
計算式の考え方
相関係数 = 共分散 ÷ (xの標準偏差 × yの標準偏差)
各通貨ペアの「変化率」を一定期間分集め、その変化率どうしの関係性を数値化します。
MQL4に相関を計算する標準関数は無い
MQL4には相関係数を直接返す関数が無いため、
この記事では`MathSqrt`等の基本的な数学関数を使い、
計算式をそのまま自作します。
複数シンボルのデータを取得する際の注意点
既刊の通貨強弱メーターでも扱った、実装上の落とし穴です。
iClose()は対象シンボルがMarket Watchに無いと0を返す
比較したい通貨ペアがMarket Watchに表示されていないと、
履歴が読み込まれず、`iClose()`が0を返してしまいます。
SymbolSelectで明示的に追加する
`SymbolSelect(symbol, true)`を使い、
比較対象の通貨ペアを`OnInit()`で、まとめて1回だけ追加しておく設計にします。
実際に動くコード全体(相関係数パネル)
チャートの通貨ペアを基準に、比較対象の相関係数を一覧表示するインジケーターです。
//+------------------------------------------------------------------+
//| CurrencyCorrelationPanel.mq4 |
//| Copyright 2026, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window
input int InpLookbackBars = 50; //相関を計算する期間(本数)
input string InpCompareSymbols = "GBPUSD,USDJPY,AUDUSD,EURJPY"; //比較対象(カンマ区切り)
string CompareList[];
string PanelPrefix = "CcyCorr_";
int OnInit()
{
ushort sep = StringGetCharacter(",", 0);
int count = StringSplit(InpCompareSymbols, sep, CompareList);
for(int i = 0; i < count; i++)
SymbolSelect(CompareList[i], true); //Market Watchに追加(iCloseの0返り対策)
for(int i = 0; i < count + 1; i++) //+1はヘッダー行
{
string name = PanelPrefix + IntegerToString(i);
ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, 20 + i * 18);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 10);
}
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
int count = ArraySize(CompareList);
for(int i = 0; i < count + 1; i++)
ObjectDelete(0, PanelPrefix + IntegerToString(i));
}
void BuildReturns(string symbol, int bars, double &out[])
{
ArrayResize(out, bars);
for(int i = 0; i < bars; i++)
{
double prevClose = iClose(symbol, 0, i + 1);
double nowClose = iClose(symbol, 0, i);
out[i] = (prevClose == 0) ? 0 : (nowClose - prevClose) / prevClose;
}
}
double CalcCorrelation(double &x[], double &y[])
{
int n = ArraySize(x);
if(n <= 1 || ArraySize(y) != n)
return(0);
double sumX = 0, sumY = 0;
for(int i = 0; i < n; i++)
{
sumX += x[i];
sumY += y[i];
}
double meanX = sumX / n;
double meanY = sumY / n;
double sumXY = 0, sumXX = 0, sumYY = 0;
for(int i = 0; i < n; i++)
{
double dx = x[i] - meanX;
double dy = y[i] - meanY;
sumXY += dx * dy;
sumXX += dx * dx;
sumYY += dy * dy;
}
if(sumXX == 0 || sumYY == 0)
return(0);
return(sumXY / MathSqrt(sumXX * sumYY));
}
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 count = ArraySize(CompareList);
double baseReturns[];
BuildReturns(_Symbol, InpLookbackBars, baseReturns);
string headerName = PanelPrefix + "0";
ObjectSetString(0, headerName, OBJPROP_TEXT, _Symbol + " との相関係数");
ObjectSetInteger(0, headerName, OBJPROP_COLOR, clrWhite);
for(int i = 0; i < count; i++)
{
double compReturns[];
BuildReturns(CompareList[i], InpLookbackBars, compReturns);
double corr = CalcCorrelation(baseReturns, compReturns);
string name = PanelPrefix + IntegerToString(i + 1);
string text = StringFormat("%s %.2f", CompareList[i], corr);
color c = clrSilver;
if(corr >= 0.7)
c = clrLimeGreen;
else if(corr <= -0.7)
c = clrOrangeRed;
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_COLOR, c);
}
return(rates_total);
}
MetaEditorでのコンパイル・表示手順
- MetaEditorで新規カスタムインジケーター「CurrencyCorrelationPanel」を作成し、上のコードを貼り付けてコンパイルする
- 任意の通貨ペアのチャートにドラッグ&ドロップで適用する
- チャート右上に、比較対象ごとの相関係数がパネル表示されることを確認する
- `InpCompareSymbols`を書き換え、比較対象を増減させて動作を確認する
よくあるエラーと注意点
実際に使う前に、確認しておきたい注意点をまとめます。
表示直後は値が0のままになることがある
ブローカーによっては、他通貨ペアの気配配信や履歴読み込みが、少し遅れることがあります。
数秒待ってから再度確認してください。
InpLookbackBarsの決め方
期間を短くすると直近の短期的な関係に、長くすると中長期的な関係に、それぞれ敏感になります。
相関は固定ではなく時間とともに変化する
・ある時期に強かった相関が、別の時期には弱まることがある
・このパネルは「今この瞬間」の相関であり、恒久的な関係ではない
・定期的に見直す前提で使う指標です
相関インジケーターの使いどころと限界
実務上の使い方と、注意点を整理します。
分散になっていない組み合わせを発見できる
相関が高い通貨ペアを複数持っている場合、このパネルを見れば、
分散のつもりが分散になっていないことに気づけます。
相関は過去データに基づく点に注意
このインジケーターが示す相関係数は、あくまで過去`InpLookbackBars`本分の、
値動きに基づく結果であり、未来の相関を保証するものではありません。
よくある質問
通貨ペアの相関について、よく検索される疑問に回答します。
相関係数がどれくらいなら「高い」と言えますか?
一般的には`0.7`以上(または`-0.7`以下)を、強い相関とみなすことが多いですが、
これは厳密な決まりではなく、目安として使ってください。
比較対象の通貨ペアはいくつでも増やせますか?
`InpCompareSymbols`にカンマ区切りで追加すれば、
理論上はいくつでも増やせます。
ただし増やすほどパネルの表示領域も必要になります。
このロジックをEAの判断に組み込めますか?
できます。
`CalcCorrelation()`関数をそのまま使えば、
「相関が高いペアでは同時に新規エントリーしない」といったフィルターにも応用できます。
【MQL4】複数の通貨ペアの値動きの相関をパネルで一覧表示するインジケーターの作り方について解説してみた(通貨強弱メーターとの違いも整理):まとめ
複数の通貨ペアを持っているとき、それが本当に分散になっているかは、
値動きを見ているだけでは判断できません。
ピアソンの相関係数を計算し、
パネルに一覧表示することで、ポジションの重複リスクを数値で確認できるようになります。
相関は時間とともに変化するため、一度きりの確認で終わらせず、
定期的に見直す前提で活用してください。
コメント