
ダイナミックマルチペアEAの形成(第1回):通貨相関と逆相関
はじめに
勝率やプロフィットファクターが高い堅実な取引戦略やシステムがある場合、相関のある通貨ペアや逆相関の通貨ペアに取引を分散することで、全体的なパフォーマンスを向上させることができます。本記事では、複数の通貨ペア間の相関関係および逆相関関係を識別するシステムを開発し、トレーダーがこれらの関係を活用して取引機会を最大化する方法をご紹介します。
例えば、非農業部門雇用者数(NFP)発表のような主要な取引イベントでは、市場があらかじめ決まった方向に急速に動く傾向があります。このようなシナリオでは、主要な通貨ペアを1つ指定することで、複数ペアでの執行を効率化できます。この主要ペアで発生した取引は、他のペアとの相関・逆相関関係を基に、他のペアでの対応取引を決定します。このアプローチにより、インパクトの大きい市場イベントにおいて取引の効率性と一貫性を大幅に向上させることが可能です。
カバーする内容
- 通貨ペアの変更・修正機能
- 他の通貨ペアへのシグナルプロバイダとなる通貨ペアのインデックス
- 取引するベース通貨とクォート通貨の決定
相関関係は、取引において通貨ペア同士の値動きの関連性を指します。正の相関関係にある2つの通貨ペアは同じ方向に動きやすく、例えば、GBPUSDとEURUSDは正の相関関係にあることが多く、GBPUSDが上昇すると、EURUSDも上昇しやすくなります。これは、どちらのペアも共通して米ドルをクォート通貨としているため、米ドルの強弱が同様に影響するからです。
一方で、逆相関関係は、2つの通貨ペアが逆方向に動く際に見られます。典型的な例としてGBPUSDとUSDCADの関係が挙げられ、GBPUSDが上昇(強気)すると、USDCADは下降(弱気)する傾向があります。これは、GBPUSDでは米ドルがクォート通貨である一方、USDCADでは米ドルがベース通貨であることが原因です。米ドルが弱まると、GBPUSDは上昇し、USDCADは下落する傾向が生まれます。
複数の通貨ペアを同時に扱えるダイナミックなマルチペアEAを開発します。このシステムは、取引戦略に従って通貨ペアを入力、変更、修正できるようにすることで、柔軟性を提供します。このシステムの主な特徴は、主要通貨ペアまたは「メイン」通貨ペアを定義できることです。この通貨ペアは他の通貨ペアのシグナルプロバイダとして機能します。
システムの中核は、取引通貨ペアのリストを動的に調整する機能です。トレーダーは取引戦略に組み込むペアを簡単にカスタマイズし、様々な市場状況や取引プランに適応できます。EAは異なる通貨ペアの入力を受け付け、ユーザーは必要に応じて通貨ペアを追加、削除、または切り替えることができます。
最も革新的な点は、主要通貨ペアの指定です。このペアは活発に取引されているだけでなく、他のペアのシグナルを生成する基準点としても機能します。このメインペアを監視することで、EAは売買シグナル(買いか売りか)を識別し、選択した相関ペアまたは逆相関ペアに適用します。
また、通貨ペア間の相関関係の強さに基づく動的な調整にも対応しています。例えば、主要通貨ペアで強い強気シグナルが検出された場合、EAは自動的に、歴史的に同じ方向に動くペアで対応する取引を建てることができます。逆に、通常メインペアと逆の動きをするペアについては、EAは反対のポジションを建て、潜在的な市場の変動に対して効果的にヘッジすることができます。
形成
#include <Trade\Trade.mqh>
CTrade trade;
MetaTrader 5の取引ライブラリをインポートして CTradeクラスのインスタンスを作成し、注文の開始、終了、変更などの取引操作を管理できるようにします。
int handles[];
この配列は、複数の通貨ペアにわたるテクニカル分析指標を追跡するために必要なさまざまな指標またはオブジェクトのハンドルを格納するために使用されます。
MqlTick previousTick, currentTick;
これらの変数は銘柄価格のティックデータを格納します。previousTickは最後のティックデータを保持し、currentTickは現在のティックデータを保持します。
inputstring Symbols = "XAUUSD, GBPUSD, USDCAD, USDJPY"; inputstring Base_Quote = "USD"; inputint Currecy_Main = 0;
これらの入力により、ユーザーはEAをカスタマイズできます。
- Symbols:EAが監視し、取引する通貨ペアのカンマ区切りリスト
- Base-Quote:相関関係を決定するための通貨
- Currency-Main:シグナル生成に使用する主要通貨ペアを指定するインデックス
string symb_List[]; string Formatted_Symbs[]; int Num_symbs = 0;
- symb-List:処理する銘柄の生のリストを保持する配列
- Formatted-Symbs:処理された銘柄を格納する配列
- Num-symb:入力Symbolsを解析した後に使用する銘柄の総数を保持する
intOnInit(){ string sprtr = ","; ushort usprtr; usprtr = StringGetCharacter(sprtr, 0); StringSplit(Symbols, usprtr, symb_List); Num_symbs = ArraySize(symb_List); ArrayResize(Formatted_Symbs, Num_symbs);
OnInit関数はEAが読み込まれたときに一度だけ呼び出され、初期値と構成を設定します。次に、入力文字列を分割するための区切り文字(sprtr)をカンマに定義します。関数StringGetCharacter()は、区切り文字をStringSplit()関数に必要なushort(unsigned short)に変換します。StringSplit()関数はSymbols入力(カンマ区切りの文字列)を個々の銘柄の配列に分割します。配列Symb-List[]は解析された銘柄を保持します。Formatted-Symbs[]配列は解析された銘柄の数に合わせてサイズが変更されます。この配列は、取引ロジックに必要な書式設定や調整の追加など、さらなる処理のために使用します。
for(int i = 0; i < Num_symbs; i++){ Formatted_Symbs[i] = symb_List[i]; }
銘柄の数をループし、銘柄をsymb-List[]配列からFormatted-Symbs[]配列に移します。この段階では、追加の書式設定はおこなわれません。
ArrayResize(handles, ArraySize(Formatted_Symbs));
ここでは、配列handles[]のサイズを配列Formatted-Symbs[]のサイズに合わせて変更しています。handles[]の各要素は、対応する銘柄のRSIハンドルを保持します。
for(int i = 0; i < ArraySize(Formatted_Symbs); i++){ handles[i] = iRSI(Formatted_Symbs[i], PERIOD_CURRENT, 14, PRICE_CLOSE); }
このループは、各銘柄のRSI指標ハンドルを初期化します。
void OnTick(){ if(isNewBar()){ for(int i = 0; i < ArraySize(Formatted_Symbs); i++){ Sig_trade(Formatted_Symbs[Currecy_Main], handles[Currecy_Main]); } } }
ここでは、まず新しいバーがあるかどうかを確認し、次にシグナルを発生させる主要通貨のインデックスを検出するためにforループを作成します。次に、取引ロジックを実行するSig-trade()関数を呼び出します。この関数は、銘柄の文字列パラメータとRSIハンドルの整数パラメータを受け取ります。
void Sig_trade(string symb, int handler){ double rsi[]; CopyBuffer(handler, MAIN_LINE, 1, 1, rsi); bool RSIBuy = rsi[0] < 30; bool RSISell = rsi[0] > 70; // Check if the current symbol is a base USD pair bool isBaseUSD = StringSubstr(symb, 0, 3) == Base_Quote; bool isQuoteUSD = StringSubstr(symb, 3, 3) == Base_Quote; string Bcurr = SymbolInfoString(symb, SYMBOL_CURRENCY_BASE); string Qcurr = SymbolInfoString(symb, SYMBOL_CURRENCY_PROFIT);
かなり単純な戦略を使用します。RSI戦略を使用して、RSIが30 レベルを下回ったときに買い、70レベルを上回ったときに売ります。
- StringSubstr(symb, 0, 3) == Base-Quote:通貨ペアの銘柄(symb)の最初の3文字を抽出し、それらが「USD」と等しいかどうかを確認して、銘柄ペアのベース通貨がUSDかどうかを判定します。
- StringSubstr(symb, 3, 3) == Base-Quote:通貨ペア銘柄(symb)の4番目の位置から始まる3つ文字を抽出し、それらが「USD」に等しいかどうかを確認し、銘柄ペアのクォート通貨がUSDかどうかを判定します。
- Bcurr = SymbolInfoString(symb, SYMBOL-CURRENCY-BASE):銘柄のベース通貨(ペアの最初の通貨)を取得し、変数Bcurrに格納します。
- Qcurr = SymbolInfoString(symb, SYMBOL-CURRENCY-PROFIT):銘柄のクォート通貨(ペアの2番目の通貨)を取得し、変数Qcurrに格納します。
for(int i = PositionsTotal() - 1; i >= 0; i--){ ulong posTicket = PositionGetTicket(i); if(PositionGetString(POSITION_SYMBOL) == symb){ if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ if(RSISell){ trade.PositionClose(posTicket); } RSIBuy = false; }elseif(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ if(RSIBuy){ trade.PositionClose(posTicket); } RSISell = false; } } }
すべてのポジションを管理するために、すべてのポジションを反復処理します。PositionsTotal()関数は現在建っているポジションの総数を返します。ループは最後の位置(PositionsTotal() - 1)から開始し、後方に反復します。ループ中にポジションのリストを変更する際の問題を回避するため、後方反復を使用しています。関数PositionGetTicket()を使って、インデックスiのポジションのチケット番号を取得します。チケットは、特定のポジションをクローズまたは変更するための一意の識別子です。
次に、PositionGetString()関数を使って、現在のポジション銘柄を取得します。そこから、この銘柄をsymb(分析対象の銘柄)と比較します。それらが一致すれば、そのポジションは関連性があります。次に、PositionGetInteger()関数を使って、現在のポジションが買い注文かどうかを確認します。ポジションが買いであり、RSIが売りを示唆している場合(RSISellがtrue)、ポジションはtrade.PositionClose(posTicket)を使用してクローズされます。その後、「RSIBuy = false」で代入し、現在のシグナルが売りを示しているため、それ以上買い取引が成立しないようにします。
売りポジションについても同様で、現在のポジションが売り注文かどうかを確認します。ポジションが売りで、RSIが買いシグナルを示唆している場合(RSIBuyがtrue)、ポジションはtrade.PositionClose(posTicket)を使って決済されます。また、シグナルが買いポジションのものであるため、新規の売り取引が建てられないように、「RSISell = false」を代入します。ストップロスやテイクプロフィットは使わないので、上記のコードを使ってすべてのポジションを管理します。そのため、コードはRSIのみに依存してポジションをオープンしたりクローズしたりします。
for(int i = 0; i < ArraySize(Formatted_Symbs); i++){ string currSymb = Formatted_Symbs[i]; // Get base and quote currencies for the looped symbol string currBaseCurr = SymbolInfoString(currSymb, SYMBOL_CURRENCY_BASE); string currQuoteCurr = SymbolInfoString(currSymb, SYMBOL_CURRENCY_PROFIT);
すべての通貨ペアで実際に取引を開始するには、Formatted-Symbs[]配列のサイズ(要素数)をループします。ループはFormatted-Symbs配列の各要素(通貨ペア)を繰り返し処理します。Formatted-Symbs配列の変数currSymbにチャート上の現在の銘柄を格納します。この銘柄は、通貨ペアに関する関連情報を取得するために使用されます。
if(RSIBuy){ if(currQuoteCurr == Base_Quote){ trade.PositionOpen(currSymb, ORDER_TYPE_BUY, volume, currentTick.ask, NULL, NULL, "Correlation"); } if(currBaseCurr == Base_Quote){ trade.PositionOpen(currSymb, ORDER_TYPE_SELL, volume, currentTick.bid, NULL, NULL, "Correlation"); } }
ここでは、RSI指標が買いシグナルを発生した場合(通常、RSIの値が一定のしきい値を下回り、売られすぎの状態を示している場合)のみを確認して実行します。次に、現在の銘柄のクォート通貨が、指定されたBase-Quote通貨と一致するかどうかを確認します。一致すれば、その通貨ペアの買い注文が発注されます。ロジックは、通貨ペアを買うときは、ベース通貨を買い、クォート通貨を売るというものです。従って、取引戦略が、気配値があなたの「ベース気配値」であるペアに強気であれば、そのペアを買うことになります。
現在の銘柄のベース通貨が指定されたBase-Qoute通貨と一致するかどうかを確認します。一致すれば、その通貨ペアの売り注文が出されます。この理由は、もし戦略が、ベース通貨が指定された基準相場であるペアに強気であれば、そのペアを売るということです。ペアを売ることは、事実上、ベース通貨を売り、クォート通貨を買うことを意味します。
if(RSISell){ if(currBaseCurr == Base_Quote){ trade.PositionOpen(currSymb, ORDER_TYPE_SELL, volume, currentTick.bid, NULL, NULL, "Correlation"); } if(currQuoteCurr == Base_Quote){ trade.PositionOpen(currSymb, ORDER_TYPE_BUY, volume, currentTick.ask, NULL, NULL, "Correlation"); } }
ここでは、RSIの売りシグナルが検出されたときのロジックを扱います。このブロックは、RSI指標が売りシグナルを発生した場合(通常、RSIがある閾値を上回り、買われすぎの状態を示す場合)に実行されます。「currBaseCurr==Base-Quote」では、現在の銘柄のベース通貨が指定されたBase-Quoteと一致するかどうかを確認します。一致すれば、その通貨ペアの売り注文が執行されます。ベース通貨が指定されたBase-Quoteであるペアを弱気にしている場合でも、ロジックは同じです。そして、そのペアを売ります。そのペアを売るということは、ベース通貨を売り、クォート通貨を買うということです。
次に、現在の銘柄のクォート通貨が指定されたBase-Quote通貨と一致するかどうかを確認します。一致すれば、その通貨ペアの買い注文が出されます。その理由は、もし戦略が、クォート通貨が指定されたBase-Quoteであるペアで弱気であれば、そのペアで買い取引を開くことになるからです。なぜなら、そのペアを買うには、ベース通貨を買い、クォート通貨を売る必要があるからです。
bool isNewBar() { //--- memorize the time of opening of the last bar in the static variable staticdatetime last_time=0; //--- current time datetime lastbar_time= (datetime) SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE); //--- if it is the first call of the function if(last_time==0) { //--- set the time and exit last_time=lastbar_time; return(false); } //--- if the time differs if(last_time!=lastbar_time) { //--- memorize the time and return true last_time=lastbar_time; return(true); } //--- if we passed to this line, then the bar is not new; return false return(false); }
上記は、EAが複数の注文を実行しないようにするためのisNewBar関数です。
ストラテジーテスターでの結果
上記のテスト結果に基づき、システムが指定された通貨相関と逆相関に従って正常に取引を開いていることが確認できます。前述したように、買いシグナルが発生し、メイン通貨がクォートUSDペアである場合、Formatted-Symbs配列内のクォートUSDペアはすべて買い注文を実行し、ベースUSDペアは売り注文を実行します。この動作は期待された機能と一致しており、このシステムが私たちの目指した相関ロジックを効果的に実装していることを示しています。
システムを効果的かつ正しく使用するためには、同じペアをベースにしたグループ分けを考慮することが極めて重要です。例えば、EURUSD、EURGBP、EURJPYのようなEURベースのペアを取引する場合、Base-Quote入力としてEURを設定すると、シグナルが検出されたときにベース通貨が取引ロジックをガイドするようになります。このアプローチは、USDEURのような他の通貨グループ、またはブローカーが許可する他のカスタムペアに適用することができ、システムのロジックが取引戦略に正しく整合していることを保証します。
システムは動的で、希望のペアをインデックスするだけでシグナルを提供または生成する主要通貨ペアを簡単に選択することができます。シグナル生成にはRSI指標が使用され、RSIバッファとハンドルはすべての通貨ペアで適用されるため、この柔軟性は特に有用です。これにより、選択された主要ペアが、システム内の他の相関ペアの取引決定を効果的に推進することが保証されます。
結論
まとめると、RSI指標に基づいて売買シグナルを処理し、さまざまな相関・逆相関の通貨ペアに適用する動的なマルチペアEAの開発をおこないました。このEAでは、カスタマイズ可能な通貨ペアの入力が可能で、メイン通貨ペアを指定することで、他の通貨ペアの取引判断に活用します。ベース通貨とクォート通貨の分析により、相関ロジックを駆使し、市場全体のトレンドに沿った取引を自動的に開始します。
ダイナミックマルチペアEAは、相関戦略と逆相関戦略を体系的に活用することで、より安定した取引成果を実現できます。このアプローチは、シグナルの自動伝達により取引効率を最適化するだけでなく、相関および逆相関のロジックを取引システムに組み込むことで、トレーダーが通貨ペア間の相関関係を最大限に活用することを可能にします。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/15378





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索