English Русский Deutsch
preview
初心者からエキスパートへ:MQL5を使ったアニメーションニュース見出し(XI) - ニュース取引における相関

初心者からエキスパートへ:MQL5を使ったアニメーションニュース見出し(XI) - ニュース取引における相関

MetaTrader 5 |
71 0
Clemence Benjamin
Clemence Benjamin

内容


はじめに

複数の通貨ペアを同時に取引できることは魅力的ですが、同時に大きなリスクも伴います。特に、高い相関を持つペアを同時に取引すると、すべてのポジションが同じ方向に動く場合、リスクが増幅されます。ひとつの判断ミスが複数の損失につながる可能性があります。

もう一つの一般的な課題は、主要経済指標の発表直前に生じます。この瞬間、多くのトレーダーはどのポジションを取るべきか迷い、意思決定が困難になります。以前の公開記事では、取引ボタンを使って複数のペアを選択し、ワンクリックで取引できる機能を紹介しました。この機能によって迅速な注文執行が可能になった一方で、新たにリスク増大という課題も浮き彫りになりました。

ここで重要になるのが、相関の概念です。ペア間の関係性を測定することで、トレーダーは戦略をより洗練させ、不要なリスクを回避できます。また、先行・追随(キャッチアップ)戦略のように、ある銘柄の動きを別の銘柄の予測に活用する手法も適用可能です。

複数通貨ペア管理の基盤を構築した次のステップは、News Headline EAへの相関分析の統合です。これにより、意図した取引方向と合致しないペアを除外し、先行銘柄を特定することで、高インパクトのニュース時における取引効率を向上させつつ、手動介入も可能な柔軟性を維持できます。



金融相関の理解

金融市場における相関とは、2つの通貨ペアの値動きがどの程度連動しているかを定量的に示す統計的指標です。これは現代のリスク管理の基礎であり、特にマルチペア環境での戦略的取引において強力なツールとなります。

News Headline EAにおいて相関を理解することは、単なる取引実行ツールを、高度な戦略管理機能を備えたEAに変えることを意味します。

相関係数 - 重要な指標

相関は相関係数によって表され、値は-1から+1の範囲を取ります。

  • +1(完全正の相関):2つの通貨ペアは完全に同じ方向に動きます。たとえば、通貨ペアAが1%上昇すると、通貨ペアBも1%上昇します。反対に下落する場合も同様です。(例:同一インデックスに連動するほぼ同一のETF)
  • 0(無相関):2つの通貨ペアの値動きには関連性がなく、完全に独立しています。
  • -1(完全負の相関):2つの通貨ペアは完全に逆方向に動きます。通貨ペアAが1%上昇すると、通貨ペアBは1%下落します。(例:株式とそのプットオプションによる典型的なヘッジ)

多くの相関はこれらの極端な値の間に位置します(例:+0.85、-0.30)。

相関が静的ではなく文脈に依存している理由

ニュース取引において最も重要な概念は、相関は固定された法則ではないという点です。相関は時間とともに変化する動的な関係であり、特定の状況下では完全に崩れることもあります。

時間足による違い:ニュース時のEUR/USDとGBP/USDの1分足チャートでの相関は、日足チャートでの相関と大きく異なる場合があります。EAは、ニュース取引用に相関の参照期間を短期間(例:15〜30分)に設定して計算する必要があります。

  1. レジーム変化:中央銀行政策の変更など、主要な経済変化により、長年安定していた相関が変わることがあります。
  2. 市場ストレス:安全資産への逃避や市場パニック時(例:2008年の金融危機)には、相関が予期せず+1または-1に収束することがあります。この場合、分散投資は機能せず、すべての通貨ペアが同じ方向に動くため、EAのリスク管理能力が試されます。

News Headline EAにおける相関計算

最も一般的な方法はピアソン相関係数です。EAは、定められた期間(N)の2つの資産(AとB)の収益率または価格変化から、自動的に相関係数を計算します。

計算式は次のとおりです。

相関関係の計算

ここで

  • 𝑟_A = 資産Aの収益率
  • 𝑟_B = 資産Bの収益率
  • Cov(𝑟_A.r_B) = 2つの収益率系列の共分散 
  • 𝜎_A = 資産A収益率の標準偏差
  • 𝜎_B = 資産B収益率の標準偏差

実装上のポイント

  • データ:EAは既に全選択通貨ペアの価格データを保持しています。
  • 関数化:相関係数を返す関数をコーディングします。
  • 参照期間:ニュース取引では、短期間のデータ(例:1分足または5分足で50〜100バー)を用います。


実装戦略

相関機能を実装するにあたり、私は2つの段階で作業を進めました。まず、CTradingButtonsクラスを拡張し、相関計算ロジックと、チャート上に結果を表示するためのビジュアルインターフェースの両方を組み込みました。これにより相関関連の機能がモジュール化され再利用可能となり、ヘッダファイル内で完結しつつ、任意のEAから柔軟に呼び出せる設計になっています。

次に、新しい相関機能をメインEAに統合しました。これにより、既存の取引ボタン、ニュースレーン、ミニチャートタイルとシームレスに連携できるようになっています。2段階に分けて開発することで、開発やデバッグが効率化されました。計算ロジックやUI要素を単独で検証した上で、より大きなシステムに統合できるためです。

以下のセクションでは、ヘッダ内に相関を組み込む方法について詳しく説明します。計算式、チャート上のマーカー配置、既存の通貨ペアラベルとの整合性、そして終了時にオブジェクトが残らないようにするクリーンアップ処理についても触れます。

手順1:CTradingButtonsの相関対応拡張

1.1. 設計定数と目的

まず、相関計算を制御するいくつかのコンパイル時定数を定義します。計算期間は、計算に使用する収益率サンプルの数を示します。最大ラグは、先行通貨ペアや遅行通貨ペアの関係を見つけるために、一方の系列をどれだけシフトさせるかを決めます。閾値は、相関が十分に強いと判断され、「相関あり」とマークする基準として使います。これらの単純な定数により、応答性とノイズ感度のバランスを容易に調整できます。計算期間が短い場合は計算結果の反応は速くなりますがノイズが増え、長い場合は滑らかになりますが反応は遅くなります。

// ---------------- Correlation defaults & utilities (merged) -------
#define CORR_WINDOW    40     // samples used for correlation window (returns)
#define CORR_MAX_LAG    3     // maximum lag (in bars) to test for cross-correlation
#define CORR_THRESHOLD 0.60   // threshold for "correlated" marker

1.2. 収益率系列の取得(価格を比較可能なデータに変換する方法)

相関は、生の価格ではなく「収益率(価格変化率)」に基づいて計算します。これは、価格水準の違いによる影響を排除し、異なる価格スケールを持つ通貨ペア間でもピアソン相関の公式が意味を持つようにするためです。FetchReturns関数は、指定通貨ペアと時間足の直近終値を取得し、それを単純なリターン(変化率)に変換します。 

ここで

  • R_tは時刻tにおける収益率
  • 𝑃_tは現在の終値
  • 𝑃_t−1は前日の終値
  • Δ𝑃_𝑡 = 𝑃_𝑡 − 𝑃_𝑡−1FetchReturns関数は、最新の収益率を配列として返します(最新順)。また、ラグ検証をおこなうために余分なバーも確保します。データが不足している場合は、処理を安全に中止するよう設計されています。

// Fetch recent returns for `symbol` on timeframe `tf`.
// Returns number of return samples placed into `ret[]` (most recent first).
int FetchReturns(const string symbol, const ENUM_TIMEFRAMES tf, const int samples, const int maxLag, double &ret[])
{
   if(StringLen(symbol) == 0 || samples <= 0) return 0;
   int need = samples + MathMax(0, maxLag) + 5;
   if(need <= 1) return 0;

   double closes[];
   ArrayResize(closes, need);
   int copied = CopyClose(symbol, tf, 0, need, closes);
   if(copied <= 1) return 0;

   int available = copied - 1; // returns available
   int use = MathMin(samples, available - MathMax(0, maxLag));
   if(use <= 0) return 0;

   ArrayResize(ret, use);
   for(int i = 0; i < use; i++)
   {
      double older = closes[i+1];
      double newer = closes[i];
      if(older == 0.0) ret[i] = 0.0;
      else ret[i] = (newer - older) / older;
   }
   return use;
}

1.3. 2つの収益率配列からピアソン相関を計算する(数学的説明)

長さnの2つの整列された収益率配列を用意した後、ピアソン相関係数rを計算します。一般的な計算式は以下の通りです。

ここで

  • rはピアソン相関係数で、−1から+1の範囲の数値となり、線形関係の強さを示します。
  • x_iは系列xの第i観測値(EAでは通貨ペアXの第i収益率、たとえばa[i])
  • y_iは系列yの第i観測値(EAでは通貨ペアYの第iの戻り値、たとえばb[i])
  • x̄はx系列の平均値:x̄ = (1/n) Σ x_i
  • ȳはy系列の平均値:ȳ = (1/n) Σ y_i

この関数は、数値的に次の手順で実装されます。まず平均を計算し、次に共分散と分散を求め、最後に分散の積の平方根で割ります。値がすべて同じなど、相関が計算できないケースでは、0を返します。また、収益率を単純なパーセンテージ変化として計算しているため、結果は通貨ペア間でスケールに依存せず比較可能です。
// Compute Pearson correlation from two return arrays of length n
double ComputePearsonFromReturns(const double &a[], const double &b[], const int n)
{
   if(n <= 1) return 0.0;
   double meanA = 0.0, meanB = 0.0;
   for(int i = 0; i < n; i++) { meanA += a[i]; meanB += b[i]; }
   meanA /= n; meanB /= n;

   double cov = 0.0, varA = 0.0, varB = 0.0;
   for(int i = 0; i < n; i++)
   {
      double da = a[i] - meanA;
      double db = b[i] - meanB;
      cov += da * db;
      varA += da * da;
      varB += db * db;
   }

   if(varA <= 0.0 || varB <= 0.0) return 0.0;
   double r = cov / MathSqrt(varA * varB);
   if(r > 1.0) r = 1.0;
   if(r < -1.0) r = -1.0;
   return r;
}

1.4. ラグ間でのピーク相関探索(先行/遅行通貨ペアの検出)

主要な改善点の一つは、現在のチャート上の通貨ペアと各候補通貨ペアの間で最も強い相関を、わずかなラグを許容しながら探索することです。具体的には、各候補通貨ペアについて、一方の収益率系列を他方に対して−maxLagから+maxLagまでシフトさせ、各シフトにおけるピアソン相関係数rを計算します。その中で絶対値が最大となるr(peakCorr)と、対応するラグ値を保持します。ラグが正の場合は、候補通貨ペアが現在の通貨ペアに対して遅行していることを意味します。逆にラグが負の場合は、候補通貨ペアが現在の通貨ペアに先行していることを示します。これにより、EAは先行銘柄を推奨できる仕組みとなっています。関数は、peakCorrlagAtPeakを返します。データが不足している場合は、falseを返すように設計されています。
// Compute cross-correlation peak and lag between currSym and otherSym.
// Outputs peakCorr and lagAtPeak (int).
bool ComputeCrossCorrPeak(const string currSym, const string otherSym, const ENUM_TIMEFRAMES tf, const int window, const int maxLag, double &peakCorr, int &lagAtPeak)
{
   if(StringLen(currSym) == 0 || StringLen(otherSym) == 0 || window <= 1) return(false);

   double rCurr[], rOther[];
   int nCurr = FetchReturns(currSym, tf, window, maxLag, rCurr);
   int nOther = FetchReturns(otherSym, tf, window, maxLag, rOther);
   if(nCurr == 0 || nOther == 0) return(false);

   int available = MathMin(nCurr, nOther);
   if(available < window) return(false);

   bool found = false;
   peakCorr = 0.0;
   lagAtPeak = 0;

   for(int lag = -maxLag; lag <= maxLag; lag++)
   {
      int startCurr = 0;
      int startOther = lag;
      if(startOther < 0) { startCurr = -startOther; startOther = 0; }

      int maxSamples = available - MathMax(startCurr, startOther);
      if(maxSamples < window) continue;

      double a[], b[];
      ArrayResize(a, window);
      ArrayResize(b, window);
      for(int i = 0; i < window; i++)
      {
         a[i] = rCurr[startCurr + i];
         b[i] = rOther[startOther + i];
      }

      double r = ComputePearsonFromReturns(a, b, window);

      if(!found || MathAbs(r) > MathAbs(peakCorr))
      {
         peakCorr = r;
         lagAtPeak = lag;
         found = true;
      }
   }

   return(found);
}

1.5. マーカー描画 - ペインター関数(視覚的フィードバック)

相関結果を既存の通貨ペアラベルを邪魔することなく表示するために、UI上に最小限のマーカーを追加します。これは、色付きドットと数値相関値を表示する小さなラベルで構成されています。DrawCorrelationMarkerは、指定された座標にOBJ_LABELを作成し、相関が強い場合は緑、弱いまたは閾値未満の場合は赤の色を設定し、たとえば「● 0.72」のような短いテキストを書き込みます。これにより、UIをコンパクトかつ視認性の高い状態に保つことができます。また、DrawRecommendationは「推奨先行通貨ペア」を表示するための小さな補助関数で、DrawCorrelationTitleはマーカー領域の上部に静的な「Correlation」ヘッダを配置します。これら3つの関数はいずれもオブジェクト管理のラッパーとして設計されており、描画処理の一貫性を保ちながら、zオーダーやフォントの統一管理を容易にします。
// Draw a small correlation marker near (x,y) — dot + numeric value only (no symbol text).
void DrawCorrelationMarker(const string objName, const int x, const int y, const double corr, const bool correlated)
{
   if(StringLen(objName) == 0) return;
   long chart = ChartID();
   if(ObjectFind(chart, objName) == -1)
      ObjectCreate(chart, objName, OBJ_LABEL, 0, 0, 0);

   ObjectSetInteger(chart, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(chart, objName, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(chart, objName, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(chart, objName, OBJPROP_FONTSIZE, 10);
   int col = correlated ? clrLime : clrRed;
   ObjectSetInteger(chart, objName, OBJPROP_COLOR, col);
   ObjectSetInteger(chart, objName, OBJPROP_SELECTABLE, 0);
   ObjectSetInteger(chart, objName, OBJPROP_ZORDER, 20);

   // Show a small dot and the numeric correlation (2 decimals). Example: "● 0.72"
   string text = StringFormat("● %.2f", corr);
   ObjectSetString(chart, objName, OBJPROP_TEXT, text);
}

// Draw recommendation label (top-left of panel area)
void DrawRecommendation(const string objName, const int x, const int y, const string text)
{
   if(StringLen(objName) == 0) return;
   long chart = ChartID();
   if(ObjectFind(chart, objName) == -1)
      ObjectCreate(chart, objName, OBJ_LABEL, 0, 0, 0);

   ObjectSetInteger(chart, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(chart, objName, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(chart, objName, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(chart, objName, OBJPROP_FONTSIZE, 10);
   ObjectSetInteger(chart, objName, OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(chart, objName, OBJPROP_SELECTABLE, 0);
   ObjectSetInteger(chart, objName, OBJPROP_ZORDER, 21);
   ObjectSetString(chart, objName, OBJPROP_TEXT, text);
}

// Draw static "Correlation" title above correlation area
void DrawCorrelationTitle(const string objName, const int x, const int y)
{
   if(StringLen(objName) == 0) return;
   long chart = ChartID();
   if(ObjectFind(chart, objName) == -1)
      ObjectCreate(chart, objName, OBJ_LABEL, 0, 0, 0);

   ObjectSetInteger(chart, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(chart, objName, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(chart, objName, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(chart, objName, OBJPROP_FONTSIZE, 11);
   ObjectSetInteger(chart, objName, OBJPROP_COLOR, clrWhite);
   ObjectSetInteger(chart, objName, OBJPROP_SELECTABLE, 0);
   ObjectSetInteger(chart, objName, OBJPROP_ZORDER, 22);
   ObjectSetString(chart, objName, OBJPROP_TEXT, "Correlation");
}

1.6. 相関設定とUI状態を取引クラス内でどこに保持するか

統合をすっきりさせるため、相関に関するパラメータと、チェックボックスごとの座標情報はCTradingButtonsクラスのメンバーとして保持します。コンストラクタ側で適切なデフォルト値を設定しておくことで、EAは単に取引パネルを生成するだけで相関機能をすぐに利用できるようになります。主なメンバーには、相関計算で使用するm_corr_window、m_corr_maxlag、m_corr_thresholdに加え、UI上でドットの位置を微調整するための縦/横方向のオフセット値、そしてチェックボックスのピクセル位置を保持するm_check_x/m_check_yの配列があります。これらの状態情報がクラス内部にまとまっていることで、UpdateCorrelationMarkersInternalの実装がシンプルになり、相関関連の状態がEA全体に散らばることを防げます。
      // initialize correlation defaults here (cannot initialize at declaration)
      m_corr_marker_prefix = "CorrMarker_";
      m_corr_rec_name      = "Corr_Recommendation";
      m_corr_title_name    = "Corr_Title";
      m_corr_tf            = PERIOD_M1;
      m_corr_window        = CORR_WINDOW;
      m_corr_maxlag        = CORR_MAX_LAG;
      m_corr_threshold     = CORR_THRESHOLD;

      // vertical offsets (push correlation UI down a bit)
      m_corr_rec_y_offset    = 24; // recommendation label y-distance in pixels (tweak)
      m_corr_marker_v_offset = 8;  // marker offset relative to checkbox top (tweak)

      // horizontal offset default (shift markers slightly right)
      m_corr_marker_h_offset = 6;  // default shift to the right by 6 pixels (tweak as needed)

      ArrayResize(m_check_x, 0);
      ArrayResize(m_check_y, 0);

1.7. 公開コントロール:EAから相関設定を実行時に調整できるようにする

EAが実行時に相関設定を調整できるよう、2つの小さなメソッドを公開します。SetCorrelationParamsは、使用する時間足、計算期間、最大ラグ、相関閾値を変更するためのメソッドです。SetCorrelationMarkerHorizontalOffsetは、画面サイズやチェックボックスの幅が異なる環境でもマーカー位置を調整できるよう、水平オフセットを変更します。どちらのメソッドも内部の更新ルーチンを呼び出し(または今後呼び出す予定)、マーカーを即座に再描画します。この構造により、UIの微調整をシンプルに保つことができます。
   // Configure correlation evaluation (optional runtime tuning)
   void SetCorrelationParams(const int timeframe, const int window, const int maxlag, const double threshold)
   {
      m_corr_tf = timeframe;
      m_corr_window = MathMax(1, window);
      m_corr_maxlag = MathMax(0, maxlag);
      m_corr_threshold = MathMax(0.0, MathMin(1.0, threshold));
      PrintFormat("SetCorrelationParams: tf=%d window=%d maxlag=%d thresh=%.2f", m_corr_tf, m_corr_window, m_corr_maxlag, m_corr_threshold);
   }

   // Exposed update so EA can refresh markers (public wrapper)
   void UpdateCorrelationMarkers()
   {
      UpdateCorrelationMarkersInternal();
   }

   // Public setter for horizontal offset of correlation markers
   void SetCorrelationMarkerHorizontalOffset(const int px)
   {
      m_corr_marker_h_offset = px;
      UpdateCorrelationMarkersInternal();
   }

1.8. 各チェックボックス単位の描画:DrawCorrelationForIndex(マーカーの計算と描画をおこなう中核処理)

各チェックボックスに対応する通貨ペア(または銘柄)ごとに、現在チャート上の基準銘柄との間で相関のピーク値を計算し、設定された閾値を満たすかどうかを判定します。座標は、チェックボックス生成時に記録したm_check_x / m_check_y(またはチェックボックスオブジェクトから取得した座標)を利用し、さらに任意の水平・垂直オフセット値を加えて、ドットと数値ラベルを配置します。この関数では、ComputeCrossCorrPeakとDrawCorrelationMarkerを呼び出すことで、計算処理と描画処理の責務を明確に分離しています。
   void DrawCorrelationForIndex(const int idx)
   {
      if(idx < 0 || idx >= ArraySize(availablePairs)) return;
      string sym = availablePairs[idx];
      if(StringLen(sym) == 0) return;
      if(idx >= ArraySize(m_check_x) || idx >= ArraySize(m_check_y)) return;

      int x = m_check_x[idx];
      int y = m_check_y[idx];
      if(x < 0 || y < 0) return;

      // move marker slightly to the right with configurable horizontal offset
      int markerX = MathMax(2, x - 18 + m_corr_marker_h_offset);
      int markerY = y + m_corr_marker_v_offset; // configurable vertical offset

      double peakCorr = 0.0;
      int lag = 0;
      bool ok = ComputeCrossCorrPeak(Symbol(), sym, (ENUM_TIMEFRAMES)m_corr_tf, m_corr_window, m_corr_maxlag, peakCorr, lag);
      if(!ok) peakCorr = 0.0;
      bool correlated = (MathAbs(peakCorr) >= m_corr_threshold);

      string objName = m_corr_marker_prefix + sym;
      DrawCorrelationMarker(objName, markerX, markerY, peakCorr, correlated);
   }

1.9. 全体の統括処理:UpdateCorrelationMarkersInternal (先行銘柄選定 + 再描画)

この関数は、解決済みの通貨ペアリストを反復処理し、チェックボックスのピクセル座標を確認したうえで、各マーカーを描画し、さらに先行銘柄の候補を判定する中核処理です。先行性判定では、絶対値として最も大きいピーク相関を持ち、かつ「負のラグによって先行性を示している銘柄」を優先して選択します(先行性の定義は調整可能です)。選ばれた銘柄については、DrawRecommendationを用いて「Recommended leader: … r=lag=…」といった短い読みやすいメッセージとして表示します。また、同エリア上部にはDrawCorrelationTitleにより「Correlation」タイトルを描画します。この関数は、チェックボックスの状態変更や画面レイアウトの変化があった際に呼び出すことを想定しています。 

   void UpdateCorrelationMarkersInternal()
   {
      int n = ArraySize(availablePairs);
      if(n == 0) return;

      for(int i = 0; i < n; i++)
      {
         if(StringLen(availablePairs[i]) == 0) continue;

         if(i >= ArraySize(m_check_x) || i >= ArraySize(m_check_y) || m_check_x[i] < 0 || m_check_y[i] < 0)
         {
            string chkName = "Chk_" + availablePairs[i];
            if(ObjectFind(ChartID(), chkName) >= 0)
            {
               int y = (int)ObjectGetInteger(ChartID(), chkName, OBJPROP_YDISTANCE);
               int x = (int)ObjectGetInteger(ChartID(), chkName, OBJPROP_XDISTANCE);
               if(ArraySize(m_check_x) < n) ArrayResize(m_check_x, n);
               if(ArraySize(m_check_y) < n) ArrayResize(m_check_y, n);
               m_check_x[i] = x;
               m_check_y[i] = y;
            }
            else
               continue;
         }

         DrawCorrelationForIndex(i);
      }

      double bestCorr = 0.0;
      string bestSym = "";
      int bestLag = 0;
      for(int j = 0; j < n; j++)
      {
         if(StringLen(availablePairs[j]) == 0) continue;
         double p = 0.0; int l = 0;
         bool ok = ComputeCrossCorrPeak(Symbol(), availablePairs[j], (ENUM_TIMEFRAMES)m_corr_tf, m_corr_window, m_corr_maxlag, p, l);
         if(!ok) continue;
         if(l < 0 && MathAbs(p) > MathAbs(bestCorr))
         {
            bestCorr = p;
            bestSym = availablePairs[j];
            bestLag = l;
         }
      }

      int recX = checkStartX;
      int recY = m_corr_rec_y_offset; // configurable vertical offset for recommendation
      string recText;
      if(bestSym != "")
         recText = StringFormat("Recommended leader: %s  r=%.2f lag=%d", bestSym, bestCorr, bestLag);
      else
         recText = "No clear leader detected";

      // Draw title above the recommendation/markers area
      DrawCorrelationTitle(m_corr_title_name, recX, MathMax(2, recY - 18));
      DrawRecommendation(m_corr_rec_name, recX, recY, recText);
   }

1.10. クリーンアップ:DeleteCorrelationMarkers(オブジェクトの適切な削除)

ヘッダ/UIがDeinitにより破棄される際、相関用に描画したすべてのラベルオブジェクトを、タイトルや推奨先行銘柄表示を含めて削除します。これにより、EAやボタンパネルをチャートから取り外した際に、ラベルが残ってしまうのを防ぎ、チャートをEA適用前の状態へ戻すことができます。
   void DeleteCorrelationMarkers()
   {
      for(int i = 0; i < ArraySize(availablePairs); i++)
      {
         if(StringLen(availablePairs[i]) == 0) continue;
         string obj = m_corr_marker_prefix + availablePairs[i];
         if(ObjectFind(ChartID(), obj) >= 0) ObjectDelete(ChartID(), obj);
      }
      if(ObjectFind(ChartID(), m_corr_rec_name) >= 0) ObjectDelete(ChartID(), m_corr_rec_name);
      if(ObjectFind(ChartID(), m_corr_title_name) >= 0) ObjectDelete(ChartID(), m_corr_title_name);
   }

相関UIがトリガーされる場所とタイミング(統合ポイント)

チェックボックスのレイアウトやユーザー操作と同期を保つため、チェックボックス生成後にUpdateCorrelationMarkersInternalを呼び出して初期マーカーを描画し、さらにHandleChartEvent内でチェックボックスがトグルされたときにも再度UpdateCorrelationMarkersInternalを呼び出します。呼び出し箇所は以下の2か所です。CreatePairCheckboxesは末尾でUpdateCorrelationMarkersInternalを実行し初期マーカーを描画します。HandleChartEventはチェックボックスが変更された際にUpdateCorrelationMarkersInternalを実行します。
      // end of CreatePairCheckboxes()
      // inside HandleChartEvent() when checkbox clicked:
      UpdateCorrelationMarkersInternal();

手順2:EAへの相関の統合

2.1. ヘッダのインクルード:相関APIを利用可能にする

相関ユーティリティを含む取引ボタン用ヘッダをインクルードし、EA側でその公開API(チェックボックス生成、相関パラメータ設定、マーカー更新、deinit処理など)を呼び出せるようにします。このインクルードは、buttonsEAを使用する前にEAの先頭で記述する必要があります。

#include <TradingButtons.mqh>
#include <Trade\Trade.mqh>
#include <Canvas\Canvas.mqh>
#include <ChartMiniTiles.mqh>   // <-- CTM class include (make sure this file is in MQL5/Include/)

2.2. 相関関係のユーザー入力:トレーダーが調整できるようにする

相関の感度や安定性を調整できるよう、timeframe、window、maxlag、thresholdをユーザー入力として公開します。これらの値はOnInit()で読み取り、ヘッダへ渡します。

// Correlation tuning exposed to EA
input group "Correlation (TradingButtons)"
input int CorrTimeframe = PERIOD_M1;
input int CorrWindow = 40;
input int CorrMaxLag = 3;
input double CorrThreshold = 0.60;

2.3. ペアUI(チェックボックス)の生成:相関マーカーのアンカーポイント

ペア用チェックボックスを生成します。これにより、ヘッダ側は各通貨ペアの横にドットや数値を配置するためのUI座標を取得できます。この処理は初期化の早い段階で実行し、ヘッダがチェックボックスの位置を参照できるようにします。

// Create pair checkboxes aligned below the canvas lanes:
int checkboxY = InpTopOffset + (InpSeparateLanes ? 8 : 28) * lineH + 6; // same as before
buttonsEA.CreatePairCheckboxes(majorPairs, pairSelected, checkboxY);

2.4. 相関パラメータの受け渡しと初回描画の実行

チェックボックスを作成した後、EA側の入力パラメータをヘッダへ渡し、初期マーカー描画を強制して、起動直後から相関UIが表示されるようにします。

// Set correlation params in TradingButtons
buttonsEA.SetCorrelationParams(CorrTimeframe, CorrWindow, CorrMaxLag, CorrThreshold);

// Force initial correlation markers
buttonsEA.UpdateCorrelationMarkers();

2.5. イベントのフォワード処理とレイアウト変更への対応

チャートイベントをヘッダへフォワードし、チェックボックスのクリックやその他のUI操作をヘッダ側で処理できるようにします。また、チャートレイアウトが変更された場合(CHARTEVENT_CHART_CHANGE)、ミニタイルと相関マーカーの両方を再配置し、位置が常に正しく保たれるようにします。

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   // Let the tiles class handle object clicks (toggle button). If handled, stop processing.
   if(tiles.HandleEvent(id, sparam))
      return;

   // Forward to the TradingButtons header afterward
   buttonsEA.HandleChartEvent(id, sparam, majorPairs, pairSelected);

   // Also respond to chart change events for CTM layout
   if(id == CHARTEVENT_CHART_CHANGE)
   {
      tiles.UpdateLayout();
      buttonsEA.UpdateCorrelationMarkers();
   }
}

2.6. タイマー駆動の更新:相関マーカーを最新状態に保つ

EAはタイマーイベントごとに複数の定期処理を実行し、その後ヘッダに相関マーカーの再計算と再描画を依頼します。これにより、ドットや数値が直近の価格変動を常に反映するようにします。

// Keep CTM updated every tick (timer-driven)
tiles.UpdateLayout();

// Keep correlation markers fresh — call the header's public updater
buttonsEA.UpdateCorrelationMarkers();

2.7. クリーンアップ:ヘッダのDeinitで相関オブジェクトを削除する

EAが停止するとき、ヘッダの初期化解除(クリーンアップ)処理を呼び出し、ヘッダ側で相関マーカー、推奨ラベル、タイトルなどのオブジェクトをすべて削除できるようにします。EA側ではbuttonsEA.Deinit()を呼び出します。ヘッダ内部では、個々のマーカーを削除する処理を実行します。以下はヘッダ側のクリーンアップ関数の例です。

void OnDeinit(const int reason)
{
   EventKillTimer();
   buttonsEA.Deinit();

   // ... other UI destruction ...

   // delete CTM tiles and toggle button
   tiles.Delete();
}


テストと結果

更新版のNews Headline EAをMetaTrader 5の実際のチャート上で動作させることが、機能検証には最も効果的です。EAの多くの機能はリアルタイムデータに依存しており、ストラテジーテスターだけでは完全な検証ができないためです。

以下の画像に示すとおり、各主要通貨ペアラベルには相関値とカラー付きドットが表示されます。赤は負の相関、ライムグリーンは正の相関を表します。数値は-1〜+1の範囲で、現在のチャートペア(先行として扱われる)との相関の強さと方向を示します。正の相関は先行ペアと同方向に動きやすいことを示し、負の相関は反対方向に動きやすいことを意味します。

News Headline EAの相関関係のテスト

図1:更新版のNews Headline EAを用いたGPBAUDでの相関テスト

画像に示されているように、相関値はゆっくりと変動します。これは、市場のボラティリティだけでなく、相関ウィンドウの大きさによっても影響を受けるためです。この挙動は統計的関係における自然な遅れを反映しており、相関分析がリアルタイムの取引判断を補完する安定した指標として機能することを示しています。


結論

金融相関は、高インパクトの経済イベント時に複数の銘柄を取引する際、取引対象の選別やリスクエクスポージャの低減に有効な手法です。本記事では、この概念をNews Headline EAに実装し、ボラティリティの高い状況下での複数通貨ペア取引における効率向上を実現しました。

この機能の目的は、単にすべてのペアを同時に取引可能にすることではなく、相関を統計的な指標として活用し、より明確かつ根拠のある意思決定を支援することにあります。ペア間の値動きを分析することで、トレーダーは自分の戦略に沿った有利な機会に集中し、重複したリスクや過度なエクスポージャを回避できます。

相関分析の応用にはさらなる可能性がありますが、現時点での実装だけでも大きな前進です。下図の通り、表示される相関値を参考にしながら、同時に取引するペアを選択できるようになっています。

相関関係を利用して取引するペアを絞り込む

図2:相関関係を利用して同時に取引するペアを決定する

この概念は、さらに多くの戦略へと発展させる可能性を持っており、MQL5での実装も十分に可能です。すべてのソースコードおよび主要コンポーネントは添付ファイル一覧の表に含まれており、一部ドキュメント化もおこなっています。ディスカッションへの参加は大歓迎です。MQL5ツールをより実践的で勝ちやすい戦略へと進化させていくために、ぜひ皆様のご意見、アイデア、ご提案をお寄せください。


重要な学び

学び 説明
目的に沿った統合 相関は独立したシグナルではなく、取引判断を支援する機能として扱うべきです。チェックボックス、マーカー、推奨表示といったUIやワークフローに相関を組み込むことで、ニュース主導の多銘柄取引において統計情報を実用的なアクションへ結びつけることができます。
生価格ではなく収益率を使用する 生の価格ではなく期間ごとの収益率(価格変化率)に基づいて相関を計算します。こうすることでスケール差を排除できます。収益率を使うことでレベル効果を避けられ、価格帯の異なる銘柄間でもピアソン相関が意味を持つようになります。
ピアソン相関の実装 ピアソン係数は丁寧に実装する必要があります。平均、共分散(対となる偏差の合計)を求め、標準偏差の積で正規化します。ゼロ分散の場合は除算エラーを避ける保護処理が必要です。
計算期間のトレードオフ 相関ウィンドウの長さは慎重に選ぶ必要があります。短いウィンドウは反応が速い反面ノイズが多く、長いウィンドウは滑らかですが反応が遅れます。ユーザーが応答性と安定性を調整できるよう、計算期間は実行時パラメータとして提供します。
ラグと先行・追随(キャッチアップ)戦略検出 相互相関計算時に小さなプラス/マイナスのラグをテストし、先行銘柄を特定します。最大絶対相関が得られたラグが先行/遅行関係を示し、先行銘柄の候補に利用できます。
意思決定のための閾値 ペアを「相関あり」と判断するための調整可能な閾値を用います。過剰マーキングを防ぎ、ユーザーが感度(通常0.5〜0.8程度)をコントロールできます。
コンパクトで邪魔にならない可視化 相関は既存の通貨ペアラベル付近に小さなカラー付きドットと数値で表示し、「Correlation」のようなタイトルを付けます。コンパクトなマーカーは、情報を提供しつつチャートや取引操作を妨げません。
UI要素へのアンカー配置 相関マーカーは既存UI(例:通貨ペアチェックボックス)に相対配置することで、チャートリサイズやパネル変更後も整列が維持されます。レイアウト変更時には位置を更新します。
相関でエクスポージャーを減らす 複数ペアを同時に建てる際、冗長なペアを相関でフィルタリングします。高相関のペアで同方向のポジションを取ることを避ければ、高インパクトイベント時の総合リスクを抑えられます。
実行時調整の公開 timeframe、window、maxlag、thresholdを入力として公開し、ユーザーが実行時に設定を調整できるようにします。これにより市場環境やユーザーの好みに応じた柔軟な調整が可能になります。
タイマー駆動の更新とパフォーマンス 相関マーカーは制御されたタイマーで再計算および再描画し、最新情報を維持しつつCPU負荷とAPI呼び出しを抑えます。更新頻度と計算負荷のバランスが重要です。
欠損データや退化データの安全な処理 履歴不足やゼロ分散の系列を検出し、そのペアをスキップまたは保守的に扱います。危険な計算をおこなわず安全なデフォルトを返すことで、異なるブローカーや銘柄構成でもEAの堅牢性を保ちます。
初期化解除時のオブジェクトクリーンアップ 初期化解除t時に相関マーカーやタイトル、推奨ラベルをすべて削除し、チャートにオブジェクトが残らないようにします。EAを削除または再読み込みしたあともチャートが整った状態に保たれます。
モジュール式のヘッダ中心設計 相関ロジックと描画を再利用可能なヘッダ/クラスにカプセル化します(例:取引ボタンユーティリティの拡張)。これによりEAコードは簡潔になり、テストと再利用性が向上します。
リアルタイム機能にはライブチャートテストを優先する 相関やUI操作はリアルタイムのティックやUI配置に依存するため、ストラテジーテスターよりライブチャートでのテストが適しています。テスターではこれらの挙動が完全再現されません。

添付ファイル

以下の表は、相関機能を追加するために更新されたソースファイルを一覧にしたものです。各行には、ファイル名、(可能であれば)現在のファイルバージョン、そして変更内容の簡潔な説明が含まれています。説明は主に、相関計算、マーカー/UI更新、公開APIの拡張、そしてメインEAとの統合ポイントに焦点を当てています。
ファイル名 バージョン 説明
TradingButtons.mqh 1.01  相関ユーティリティを含むように取引UIヘッダを拡張しました。ピアソン相関とラグ付き相互相関の計算、設定可能なパラメータ(timeframe、window、maxLag、threshold)、およびタイマー駆動による効率的な更新を追加しています。

視覚面の変更としては、コンパクトな相関マーカー(小さな色付きドット+数値)、ブロック上部の「Correlation」タイトル、わずかな配置調整(少し下方へ移動し、右側へシフト)、そして既存ラベルの横にドットと値のみが表示されるよう、重複銘柄名の削除が含まれています。

EA向けには、SetCorrelationParams、UpdateCorrelationMarkers、CreatePairCheckboxes、HandleChartEvent、Deinit(クリーンアップ)といった公開API関数を提供しています。不完全なデータへの対処もおこない、初期化解除時には作成したすべてのオブジェクトを確実に削除します。
NewsHeadlineEA.mq5 1.15 カレンダー、ニュース、AIインサイト、TradingButtons UI、そしてミニチャートタイルを統合するメインEAです。相関用の入力値をTradingButtonsヘッダへ渡すよう更新し、初期化時、タイマー実行時、チャート変更イベント時にUpdateCorrelationMarkersを呼び出すようにしました。また、取引コントロールやCTMと重ならないよう、相関UIの配置も調整しています。

さらに、複数通貨のミニチャート表示のためにChartMiniTilesを統合し、取引UI用にチャート上部の領域を確保しています。初期化解除時には相関マーカーとCTMタイルを確実にクリーンアップするようにしています。
ChartMiniTiles.mqh 1.0 メインチャート内に多銘柄チャートを埋め込むための、再利用可能なミニチャートタイルクラスです。 

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/19343

添付されたファイル |
TradingButtons.mqh (94.86 KB)
NewsHeadlineEA.mq5 (70.54 KB)
ChartMiniTiles.mqh (38.65 KB)
平均足を使ったプロフェッショナルな取引システムの構築(第2回):EAの開発 平均足を使ったプロフェッショナルな取引システムの構築(第2回):EAの開発
本記事では、MQL5を用いてプロフェッショナルな平均足ベースのエキスパートアドバイザー(EA)を開発する方法について解説します。入力パラメータ、列挙型、インジケーター、グローバル変数の設定方法から、コアとなる売買ロジックの実装までを順を追って説明します。また、開発したEAを金(ゴールド)でバックテストして、正しく動作するかどうかを検証する方法も学べます。
MQL5でのAI搭載取引システムの構築(第1回):AI API向けJSON処理の実装 MQL5でのAI搭載取引システムの構築(第1回):AI API向けJSON処理の実装
本記事では、AI API連携のためのデータ交換を扱うJSON解析フレームワークをMQL5で開発します。特に、JSON構造を処理するためのクラスに焦点を当てています。JSONデータのシリアライズ(出力用)およびデシリアライズ(入力用)メソッドを実装し、文字列、数値、オブジェクトなどの各データ型をサポートします。これにより、ChatGPTのようなAIサービスとMQL5間で正確にデータをやり取りでき、将来的なAI駆動型取引システム構築に向けた基盤を提供します。
プライスアクション分析ツールキットの開発(第41回):MQL5で統計的価格レベルEAを構築する プライスアクション分析ツールキットの開発(第41回):MQL5で統計的価格レベルEAを構築する
統計は常に金融分析の中心にあります。統計とは、データを収集・分析・解釈・提示し、意味のある情報に変換する学問です。これをローソク足に応用すると、価格の生データを測定可能な洞察に圧縮できます。特定期間における市場の中心傾向、分布、広がりを把握できれば、どれほど有益でしょうか。本記事では、統計的手法を用いてローソク足データを明確で実行可能なシグナルに変換する方法を紹介します。
MQL5入門(第21回):ハーモニックパターン検出の自動化 MQL5入門(第21回):ハーモニックパターン検出の自動化
MetaTrader 5でMQL5を使ってガートリーハーモニックパターンを検出して表示する方法を学びます。この記事では、スイングポイントの特定からフィボナッチ比率の適用、チャート上へのパターン描画までの手順を順を追って解説し、視覚的に確認できる形で表示する方法を紹介します。