
Candlestick Trend Constraintモデルの構築(第9回):マルチ戦略エキスパートアドバイザー(II)
内容
- はじめに
- ドンチャンチャネルとは
- MetaTrader 5でドンチャンチャネルにアクセスする
- MetaEditorでドンチャンチャネルのソースコードにアクセスする
- Trend Constraint EAにおけるドンチャンチャネル戦略の実装
- テストと結果
- 結論
はじめに
20世紀にリチャード・ドンチャンは、金融市場の研究を通じてトレンドフォロー戦略を確立し、後にそれがドンチャンチャネル(ドンチャンチャネル)へと発展しました。彼の研究については以前の記事で簡単に触れましたが、今回は彼の理論に基づいた戦略の実装に焦点を当てていきます。さまざまな情報源によると、ドンチャンチャネルはその枠組みの中で複数の戦略を包含していると考えられています。ドンチャンチャネルに関する豊富な文献は、この理論が現代の取引においても依然として有効であることを示唆しています。ドンチャンチャネル戦略を統合することで、Trend Constraint EAの機会を拡大し、収益性と多様な市場環境への適応力を高めることを目指します。
ドンチャンチャネルに基づく有名な戦略には、ブレイクアウト戦略、クロール戦略、ミーンリバージョン戦略などがあります。Rayner Teoのような著名なトレーダーも、これらのチャネルを効果的に活用する方法を学ぶための教育コンテンツを提供しています。
ンチャンチャネルはMetaTrader 5プラットフォームで無料のインジケーターとして利用可能であり、この点が本プロジェクトにおいて大きな利点となります。このアクセスを活用することで、インジケーターのソースコードを利用し、その構造に対するより深い理解を得ることができ、Trend Constraint EAへの適用が容易になります。コードが複雑化していく中で、私たちは新しい戦略を独立して開発し、メインプログラムに統合する前にその調整をおこないます。今後のセグメントでは、理論の理解をさらに深め、アルゴリズムを一層強化していきます。
ドンチャンチャネルとは
ドンチャンチャネルは、テクニカル分析における指標で、上バンド、中バンド、下バンドの3本の線から成り、価格の動きの中で高値と安値を示すために使用されます。前述のように、トレンドフォロー取引の先駆者であるRichard Donchianによる功績が大きいです。以下は、3本のラインの概要です。- 上バンド:この線は、指定した期間(例えば過去20期間)の最高値を示します。
- 下バンド:この線は、同じ指定期間の最低値を示します。
- ミドルバンド:上バンドと下バンドの平均として計算されることが多く、基準点として使われることがあります。
ドンチャンチャネルライン
MetaTrader 5でドンチャンチャネルにアクセスする
ドンチャンチャネルには、通常、下の画像に示すように、ナビゲーターウィンドウの[Indicators]フォルダからアクセスします。
MetaTrader 5ナビゲーター
アクセスしたら、下の画像のように、ドンチャンチャネルを使用するチャート上にドラッグできます。この例では、Volatility 150 (1s)指数のデフォルトチャンネル設定を使用しています。
MetaTrader 5のチャートにドンチャンチャネルを追加する
このインジケーターをチャートに適用する理由は、プライスアクションとチャンネルの関係を分析することです。これは、アルゴリズム開発を始める前に、エントリールールを理解する助けとなります。次に、MetaEditor 5でインジケーターのソースコードにアクセスする方法を解説します。
MetaEditorでドンチャンチャネルのソースコードにアクセスする
MetaEditor 5でソースファイルを編集するには、ナビゲータウィンドウを開き、MetaTrader 5プラットフォームと同様に[Free Indicators]タブからインジケーターを見つけます。ただし、ここで扱うのはコンパイル済みバージョンではなく、ソースファイルである点が重要な違いです。ファイルをダブルクリックするとコードが表示されます。手順をより分かりやすくするため、以下の画像も併せてご参照ください。
MetaEditorでDonchain Channelのソースコードにアクセスする
Trend Constraint EAにおけるドンチャンチャネル戦略の実装
新しいツールを取り入れる際のルールを確立し、議論を形成する指針となるパラメータを明確にすることは非常に重要です。私たちは常に、最初に制約条件を定義します。例えば、D1のローソク足が強気の場合のみ買い、D1のローソク足が弱気の場合のみ売る、といった具合です。このルールを念頭に置きつつ、まずは制約条件を特定した上で、市場のトレンドに沿ったチャンネルのセットアップから得られる注文機会を模索します。 具体的には、強気のD1シナリオにおいて以下に注目します。 - 価格がチャンネルの下限にタッチした場合、高い勝率で買い注文を発生させる。
- 中程度の勝率を伴うミドルラインでのリバウンドを狙う。
- 勝率は低いものの、上限のブレイクアウトを活用する。
これら3つの戦略について、以下の画像で説明しています。
ドンチャンチャンネル戦略
このプレゼンテーションでは、ブレイクアウト手法を使用します。これは、市場価格がチャンネルの外側の境界を超えて終値した場合の状況を監視するものです。それでは、インジケーターのソースコードを確認し、関連するバッファを特定していきましょう。
インジケーターソースコードのプレビュー
デフォルトのドンチャンチャネルインジケーターは、MetaTrader 5プラットフォームで利用可能です。MetaEditor 5では、先に説明した方法で直接アクセスすることもできます。//+------------------------------------------------------------------+ //| Donchian Channel.mq5 | //| Copyright 2009-2024, MetaQuotes Ltd | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2009-2024, MetaQuotes Ltd" #property link "http://www.mql5.com" #property description "Donchian Channel" //--- #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #property indicator_type1 DRAW_LINE #property indicator_color1 clrBlue #property indicator_type2 DRAW_LINE #property indicator_color2 clrGray #property indicator_type3 DRAW_LINE #property indicator_color3 clrRed //--- labels #property indicator_label1 "Upper Donchian" #property indicator_label2 "Middle Donchian" #property indicator_label3 "Lower Donchian" //--- input parameter input int InpDonchianPeriod=20; // period of the channel input bool InpShowLabel =true; // show price of the level //--- indicator buffers double ExtUpBuffer[]; double ExtMdBuffer[]; double ExtDnBuffer[]; //--- unique prefix to identify indicator objects string ExtPrefixUniq; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- define buffers SetIndexBuffer(0, ExtUpBuffer); SetIndexBuffer(1, ExtMdBuffer); SetIndexBuffer(2, ExtDnBuffer); //--- set a 1-bar offset for each line PlotIndexSetInteger(0, PLOT_SHIFT, 1); PlotIndexSetInteger(1, PLOT_SHIFT, 1); PlotIndexSetInteger(2, PLOT_SHIFT, 1); //--- indicator name IndicatorSetString(INDICATOR_SHORTNAME, "Donchian Channel"); //--- number of digits of indicator value IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- prepare prefix for objects string number=StringFormat("%I64d", GetTickCount64()); ExtPrefixUniq=StringSubstr(number, StringLen(number)-4); ExtPrefixUniq=ExtPrefixUniq+"_DN"; Print("Indicator \"Donchian Channels\" started, prefix=", ExtPrefixUniq); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { //--- if the indicator has previously been calculated, start from the bar preceding the last one int start=prev_calculated-1; //--- if this is the first calculation of the indicator, then move by InpDonchianPeriod bars form the beginning if(prev_calculated==0) start=InpDonchianPeriod+1; //--- calculate levels for all bars in a loop for(int i=start; i<rates_total; i++) { //--- get max/min values for the last InpDonchianPeriod bars int highest_bar_index=ArrayMaximum(high, i-InpDonchianPeriod+1, InpDonchianPeriod); int lowest_bar_index=ArrayMinimum(low, i-InpDonchianPeriod+1, InpDonchianPeriod);; double highest=high[highest_bar_index]; double lowest=low[lowest_bar_index]; //--- write values into buffers ExtUpBuffer[i]=highest; ExtDnBuffer[i]=lowest; ExtMdBuffer[i]=(highest+lowest)/2; } //--- draw labels on levels if(InpShowLabel) { ShowPriceLevels(time[rates_total-1], rates_total-1); ChartRedraw(); } //--- succesfully calculated return(rates_total); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- delete all our graphical objects after use Print("Indicator \"Donchian Channels\" stopped, delete all objects with prefix=", ExtPrefixUniq); ObjectsDeleteAll(0, ExtPrefixUniq, 0, OBJ_ARROW_RIGHT_PRICE); ChartRedraw(0); } //+------------------------------------------------------------------+ //| Show prices' levels | //+------------------------------------------------------------------+ void ShowPriceLevels(datetime time, int last_index) { ShowRightPrice(ExtPrefixUniq+"_UP", time, ExtUpBuffer[last_index], clrBlue); ShowRightPrice(ExtPrefixUniq+"_MD", time, ExtMdBuffer[last_index], clrGray); ShowRightPrice(ExtPrefixUniq+"_Dn", time, ExtDnBuffer[last_index], clrRed); } //+------------------------------------------------------------------+ //| Create or Update "Right Price Label" object | //+------------------------------------------------------------------+ bool ShowRightPrice(const string name, datetime time, double price, color clr) { if(!ObjectCreate(0, name, OBJ_ARROW_RIGHT_PRICE, 0, time, price)) { ObjectMove(0, name, 0, time, price); return(false); } //--- make the label size adaptive long scale=2; if(!ChartGetInteger(0, CHART_SCALE, 0, scale)) { //--- output an error message to the Experts journal Print(__FUNCTION__+", ChartGetInteger(CHART_SCALE) failed, error = ", GetLastError()); } int width=scale>1 ? 2:1; // if chart scale > 1, then label size = 2 ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_BACK, false); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_SELECTED, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, name, OBJPROP_ZORDER, 0); return(true); } //+------------------------------------------------------------------+
上記のカスタムコードは、ドンチャンチャンネルを実装しています。このコードでは、指定した期間内の最高値を表す上側のチャンネルライン、同じ期間内の最安値を表す下側のチャンネルライン、そして上側と下側のラインの平均を計算した中央ラインの3つのラインを算出し、表示します。このインジケーターは、潜在的なブレイクアウトポイントを視覚化するよう設計されており、チャンネル期間やチャート上に価格ラベルを表示するオプションなど、カスタマイズ可能な入力パラメータを備えています。コードには、インジケーターバッファやプロパティを設定するための初期化関数、チャート上の各バーに対応するチャンネルラインを更新する計算ループ、グラフィカルオブジェクトおよび価格ラベルを管理するための関数が含まれています。全体として、このインジケーターはトレーダーに、過去の価格水準に基づいてトレンドや潜在的な取引機会を特定するためのツールを提供します。
簡単に理解できるよう、上記のコードスニペットを分割し、次のセクションでさらなる開発を進めるために使用するバッファを特定していきます。
バッファの宣言
ExtUpBuffer[]、ExtMdBuffer[]、ExtDnBuffer[]の3つのバッファが宣言されており、それぞれ上、中、下のドンチャンチャネルの値を格納します。
double ExtUpBuffer[]; double ExtMdBuffer[]; double ExtDnBuffer[];
OnInitでのバッファ設定
SetIndexBuffer関数はチャートのプロット(線)をバッファにリンクし、チャート上に描画更新できるようにします。
SetIndexBuffer(0, ExtUpBuffer); SetIndexBuffer(1, ExtMdBuffer); SetIndexBuffer(2, ExtDnBuffer);
OnCalculateでのバッファ値計算
このコードは、定義された期間の最高値、最安値、平均値を計算し、各バーのそれぞれのバッファに格納します。
for(int i=start; i<rates_total; i++) { //--- calculate highest and lowest for the Donchian period int highest_bar_index = ArrayMaximum(high, i-InpDonchianPeriod+1, InpDonchianPeriod); int lowest_bar_index = ArrayMinimum(low, i-InpDonchianPeriod+1, InpDonchianPeriod); double highest = high[highest_bar_index]; double lowest = low[lowest_bar_index]; //--- assign values to buffers ExtUpBuffer[i] = highest; ExtDnBuffer[i] = lowest; ExtMdBuffer[i] = (highest + lowest) / 2; }
買いシグナルを生成する際、この戦略では上側バッファ(ExtUpBuffer)を使用し、価格が上側のドンチャンラインを上回ってクローズした場合に買いをトリガーします。逆に、売りシグナルは下側バッファ(ExtDnBuffer)を使用し、価格が下側のドンチャンラインを下回ってクローズした場合にトリガーされます。さらに、ミドルチャンネル(ExtMdBuffer)はフィルタとして機能し、価格がミドルチャンネルの上にある場合にのみ買い取引を許可することで、より強い上昇トレンドを示すよう戦略を洗練させることが可能です。この内容を踏まえ、EAの開発を進める準備が整ったと確信しています。
コード開発
ドンチャンチャネルがビルトインインジケーターとして利用可能であるため、インジケーターのバッファに焦点を当て、取引実行のシグナルを生成するEAを開発するプロセスが簡素化されます。前述の通り、分かりやすさを重視して、まずドンチャンチャネルに基づいたEAを開発し、その後Trend Constraint EAと統合を進めます。本日は、ドンチャンチャンネルを用いたブレイクアウト戦略に焦点を当てます。ブレイクアウトの条件はシンプルで、価格がチャンネルの極端なバンドを超えて引けた際に発生します。さまざまな戦略については、先ほどの画像で詳しく説明していますので、そちらも併せてご参照ください。
最初のステップとして、下図のようにMetaEditor 5で新規ファイルを作成します。このブレイクアウト戦略に主眼を置くため、ファイル名は「BreakoutEA」としました。
MetaEditorで新しいEAプログラムを開始する
私は、このEA構築プロセスを大きく5つのセグメントに分割しています。最初にEAを起動する際は、基本的なテンプレートから作業を開始し、他の部分にはチェックを入れません。そのテンプレートの下で、最終的に統合される重要な構成要素について説明します。
この基本テンプレートには、#property strictディレクティブのような必須プロパティが含まれています。このディレクティブは、コンパイラがデータ型を正しく使用していることを保証し、型の不一致による潜在的なプログラミングエラーを防ぐのに役立ちます。さらに重要な点として、取引業務を効率的に管理するためのツールを提供する取引ライブラリも含まれています。これらの手順は、開発プロセスの強固な基盤を築く上で不可欠です。
//+------------------------------------------------------------------+ //| BreakoutEA.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/ja/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/ja/users/billionaire2024/seller" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
1. エキスパートアドバイザーの初期化
初期化セグメントでは、EAの入力パラメータが定義されます。これらのパラメータを使用することで、EAを自分の好みに合わせて設定することができます。重要な入力には、ドンチャンチャネルの期間、リスクとリターンの比率、取引のロットサイズ、ストップロスとテイクプロフィットのピップ値が含まれます。
// Input parameters input int InpDonchianPeriod = 20; // Period for Donchian Channel input double RiskRewardRatio = 1.5; // Risk-to-reward ratio input double LotSize = 0.1; // Default lot size for trading input double pipsToStopLoss = 15; // Stop loss in pips input double pipsToTakeProfit = 30; // Take profit in pips // Indicator handle storage int handle; string indicatorKey; // Expert initialization function int OnInit() { // Create a unique key for the indicator based on the symbol and period indicatorKey = StringFormat("%s_%d", Symbol(), InpDonchianPeriod); // Load the Donchian Channel indicator handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); // Check if the indicator loaded successfully if (handle == INVALID_HANDLE) { Print("Failed to load the indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
取引銘柄と定義された期間に基づいて一意のキーが作成され、インジケーターの各インスタンスを確実に区別できるようにします。iCustom()関数は、MetaTraderディレクトリ内のパス(Free Indicators\\Donchian Channel) を指定して、Donchian Channelインジケーターをロードするために使用されます。ロードに失敗した場合(INVALID_HANDLEで示される)、エラーメッセージが表示され、初期化に失敗します。その結果、必要なインジケーターデータがない状態では、EAの実行が停止します。インジケーターはルートフォルダには存在しないため、保存場所を正確に指定することが重要です。保存場所を指定しない場合、以下のようなエラーが発生します。ほとんどの場合、インジケーターのロードに失敗すると、EAは正常に実行されません。
//Typical Journal log when the EA fails to locate an indicator in the root indicators storage. 2024.10.20 08:49:04.117 2022.01.01 00:00:00 cannot load custom indicator 'Donchian Channel' [4802] 2024.10.20 08:49:04.118 2022.01.01 00:00:00 indicator create error in 'DonchianEA.mq5' (1,1) 2024.10.20 08:49:04.118 OnInit critical error 2024.10.20 08:49:04.118 tester stopped because OnInit failed
2. クリーンアップと初期化
クリーンアップセグメントは、EAが使用したリソースを解放する役割を担います。これは、EAが削除される際やMetaTrader 5がシャットダウンする際に呼び出されるOnDeinit()関数内で実行されます。この関数では、IndicatorRelease()を使用してインジケーターハンドルが確実に解放されることを確認します。リソースを徹底的にクリーンアップすることは、メモリリークを防ぎ、プラットフォーム全体のパフォーマンスを維持するために非常に重要です。
// Expert deinitialization function void OnDeinit(const int reason) { // Release the indicator handle to free up resources IndicatorRelease(handle); }
3. 主な実行ロジック
主な実行ロジックはOnTick()関数にあり、市場のティックまたは価格変動があるたびにトリガーされます。この関数では、PositionsTotal()関数を使用して、現在開かれているポジションがあるかどうかを確認します。ポジションが開かれていない場合、プログラムは別の関数を呼び出して取引条件を評価します。この構造により、一度に複数の取引が開かれることを防ぎ、過剰取引を避けることができます。
// Main execution function with block-based control void OnTick() { // Check if any positions are currently open if (PositionsTotal() == 0) { CheckTradingConditions(); } }
4. 取引条件の評価
このセグメントでは、EAは市場状況をドンチャンチャネルの上下バンドに照らし合わせてチェックします。インジケーターバッファのサイズは、最新のデータに対応するように変更されます。CopyBuffer()関数は、ドンチャンチャネルから最新の値を取得します。
// Check trading conditions based on indicator buffers void CheckTradingConditions() { double ExtUpBuffer[], ExtDnBuffer[]; // Buffers for upper and lower Donchian bands // Resize buffers to hold the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading indicator buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Buy condition: Closing price is above the upper Donchian band if (closePrice > ExtUpBuffer[1]) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBuy(LotSize, stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band if (closePrice < ExtDnBuffer[1]) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenSell(LotSize, stopLoss, takeProfit); } }
現在の終値を取得します。これは取引シグナルを評価するために重要です。取引条件は、終値が上限バンドを超えれば買い注文が発注され、下限バンドを下回れば売り注文が発注されるように定義されています。ストップロスとテイクプロフィットのレベルは、ユーザーが定義したピップ値に基づいて計算され、リスクを効果的に管理します。
5. 発注関数
注文発注関数は、売買取引の実行を担当します。各関数はCTradeクラスのメソッドを使用して取引を試みるため、取引管理が簡素化されます。取引の実行後、プログラムは注文が成功したかどうかを確認します。失敗した場合は、エラーメッセージが表示され、トレーダーに失敗を通知します。これらの関数は取引ロジックをカプセル化し、前述の条件に基づいて注文を発注するための明確なインターフェイスを提供します。
// Open a buy order void OpenBuy(double lotSize, double stopLoss, double takeProfit) { // Attempt to open a buy order if (trade.Buy(lotSize, Symbol(), 0, stopLoss, takeProfit, "Buy Order")) { Print("Buy order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open buy order. Error: ", GetLastError()); } } // Open a sell order void OpenSell(double lotSize, double stopLoss, double takeProfit) { // Attempt to open a sell order if (trade.Sell(lotSize, Symbol(), 0, stopLoss, takeProfit, "Sell Order")) { Print("Sell order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open sell order. Error: ", GetLastError()); } }
私たちのドンチャンチャネルBreakout EAは、現在、完全に開発され、準備万端です。
//+----------------------------------------------------------------------+ //| BreakoutEA.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/ja/users/billionaire2024/seller | //+----------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/ja/users/billionaire2024/seller" #property version "1.00" #property strict #include <Trade\Trade.mqh> // Include the trade library // Input parameters input int InpDonchianPeriod = 20; // Period for Donchian Channel input double RiskRewardRatio = 1.5; // Risk-to-reward ratio input double LotSize = 0.1; // Default lot size for trading input double pipsToStopLoss = 15; // Stop loss in pips input double pipsToTakeProfit = 30; // Take profit in pips // Indicator handle storage int handle; string indicatorKey; double ExtUpBuffer[]; // Upper Donchian buffer double ExtDnBuffer[]; // Lower Donchian buffer // Trade instance CTrade trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { indicatorKey = StringFormat("%s_%d", Symbol(), InpDonchianPeriod); // Create a unique key for the indicator handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); if (handle == INVALID_HANDLE) { Print("Failed to load the indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release the indicator handle IndicatorRelease(handle); } //+------------------------------------------------------------------+ //| Main execution function with block-based control | //+------------------------------------------------------------------+ void OnTick() { // Check if any positions are currently open if (PositionsTotal() == 0) { CheckTradingConditions(); } } //+------------------------------------------------------------------+ //| Check trading conditions based on indicator buffers | //+------------------------------------------------------------------+ void CheckTradingConditions() { // Resize buffers to get the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading indicator buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Buy condition: Closing price is above the upper Donchian band if (closePrice > ExtUpBuffer[1]) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBuy(LotSize, stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band if (closePrice < ExtDnBuffer[1]) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenSell(LotSize, stopLoss, takeProfit); } } //+------------------------------------------------------------------+ //| Open a buy order | //+------------------------------------------------------------------+ void OpenBuy(double lotSize, double stopLoss, double takeProfit) { if (trade.Buy(lotSize, Symbol(), 0, stopLoss, takeProfit, "Buy Order")) { Print("Buy order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open buy order. Error: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Open a sell order | //+------------------------------------------------------------------+ void OpenSell(double lotSize, double stopLoss, double takeProfit) { if (trade.Sell(lotSize, Symbol(), 0, stopLoss, takeProfit, "Sell Order")) { Print("Sell order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open sell order. Error: ", GetLastError()); } } //+------------------------------------------------------------------+
メインのTrend Constraintコードに組み込む前に、Breakout EAをテストしてみましょう。全体的な目的に沿った制約ロジックをまだ実装していないため、これは最終版ではないことに注意することが重要です。
チャートにBreakout EAを追加する
EAリスト上で右クリックし、[Test]を選択してテスターウィンドウを開きます。そこから、テスト用のBreakout EAを選択して設定することができます。以下のパフォーマンスをご覧ください。
Breakout EAがストラテジーテスターで機能する
素晴らしいことです。注文を成功させることができました。この基盤を活用して、収益性を高め、不必要な取引を排除することができます。これにより、次の段階の重要性が際立ちます。次の段階では、より確率の低い取引を排除するために制約を組み込むことが求められます。Trend Constraint EAへの組み込み
2つのEAコードを統合するには、双方の機能を組み合わせる必要があり、共有されている機能は最終的なEAの主要な部分となります。さらに、それぞれのEAが持つ独自の機能によって、統合されたコードの全体的なサイズと機能が拡張されます。例えば、Trend Constraint EAとBreakout EAの両方に共通するプロパティがあり、それらを1つのプログラムに組み込みます。以下のコードスニペットを参照してください。
// We merge it to one #property strict #include <Trade\Trade.mqh> // Include the trade library
さて、組み込みましょう。 2つ以上の戦略を1つのEAに統合する際、全体的な取引ロジックの中心となる主な機能には、OnInit()、OnTick()、およびポジション管理機能(OpenBuy()やOpenSell()など)が含まれます。これらの関数はEAの核となり、それぞれインジケーターの初期化、リアルタイム市場分析、注文発注を処理します。
一方で、Trend Constraint EAのDonchian Channelブレイクアウト戦略と移動平均線付きRSIトレンドフォロー戦略の機能は、OnTick()関数内で個別の条件として組み込まれ、既存のプログラムを拡張します。このEAは、ドンチャンチャネルからのブレイクアウトシグナルとRSIおよび移動平均からのトレンドシグナルを同時に評価するため、市場の状況に対してより包括的に反応することが可能になります。
これらの独立した機能を統合することで、EAは意思決定能力を強化し、さまざまな市場ダイナミクスに適応する、より強固な取引戦略が実現します。
1. 初期化関数
初期化関数(OnInit())は、EAが採用する両方の取引戦略に必要なインジケーターを設定するため、非常に重要です。この関数は、EAが最初にロードされる際に呼び出され、取引操作を開始する前にすべての重要なコンポーネントが準備できていることを確認します。RSI(相対力指数)およびドンチャンチャネルインジケーターを初期化します。これらのインジケーターのいずれかが適切に初期化できなかった場合、関数はエラーステータスを返し、取引システムの実行を停止し、潜在的な市場リスクを回避します。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize RSI handle rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE); if (rsi_handle == INVALID_HANDLE) { Print("Failed to create RSI indicator handle"); return INIT_FAILED; } // Create a handle for the Donchian Channel handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); if (handle == INVALID_HANDLE) { Print("Failed to load the Donchian Channel indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
2. 主な実行ロジック
主な実行ロジックはOnTick()関数で処理され、この関数は市場のティックが発生するたびに呼び出されます。この関数はEAの中核として機能し、変化する市場環境に応じて様々な取引戦略を評価する役割を担います。トレンドフォロー戦略とブレイクアウト戦略をチェックする関数を順番に呼び出します。さらに、期限切れの注文のチェックも含まれており、EAは期限切れのポジションが有効なまま残らないように管理し、効果的にリスクをコントロールすることができます。
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Execute both strategies independently on each tick CheckTrendConstraintTrading(); CheckBreakoutTrading(); CheckOrderExpiration(); // Check for expired Trend Following orders }
3. モジュール式取引条件関数
トレンドフォロー戦略
この関数は、意思決定を進める前に、現在のポジションの有無を確認します。ポジションがない場合、現在のRSI値を取得し、市場のトレンドを判断するために短期および長期の移動平均を計算します。市場が上昇トレンドにあり、RSIが売られ過ぎの状態を示す場合、買い注文を出すことがあります。逆に、相場が下降トレンドにあり、RSIが買われ過ぎの状態を示す場合、売り注文を出す可能性があります。既存のポジションがある場合は、トレーリングストップ機能を使って管理します。
//+------------------------------------------------------------------+ //| Check and execute Trend Constraint EA trading logic | //+------------------------------------------------------------------+ void CheckTrendConstraintTrading() { // Check if there are any positions open if (PositionsTotal() == 0) { // Get RSI value double rsi_value; double rsi_values[]; if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0) { Print("Failed to get RSI value"); return; } rsi_value = rsi_values[0]; // Calculate moving averages double ma_short = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE); double ma_long = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE); // Determine trend direction bool is_uptrend = ma_short > ma_long; bool is_downtrend = ma_short < ma_long; // Check for buy conditions if (is_uptrend && rsi_value < RSI_Oversold) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); double stopLossPrice = currentPrice - StopLoss * _Point; double takeProfitPrice = currentPrice + TakeProfit * _Point; // Attempt to open a Buy order if (trade.Buy(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Buy") > 0) { Print("Trend Following Buy order placed."); } else { Print("Error placing Trend Following Buy order: ", GetLastError()); } } // Check for sell conditions else if (is_downtrend && rsi_value > RSI_Overbought) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double stopLossPrice = currentPrice + StopLoss * _Point; double takeProfitPrice = currentPrice - TakeProfit * _Point; // Attempt to open a Sell order if (trade.Sell(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Sell") > 0) { Print("Trend Following Sell order placed."); } else { Print("Error placing Trend Following Sell order: ", GetLastError()); } } } else { // Implement Trailing Stop for open positions TrailingStopLogic(); } }
ブレイクアウト戦略関数
この関数は、ドンチャンチャネルインジケーターに基づいてブレイクアウト条件を評価するように設計されています。最新のデータを取り込むために必要なバッファサイズを変更し、潜在的なブレイクアウトの機会を確認します。この戦略では、特定の価格水準を超えることを探し、これが実現すると、市場での大きな価格変動を示唆する可能性があります。条件が満たされると、EAはそれに従って注文を実行します。
/+-------------------------------------------------------------------+ //| Check and execute Breakout EA trading logic | //+------------------------------------------------------------------+ void CheckBreakoutTrading() { // Resize buffers to get the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading Donchian Channel buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Get the daily open and close for the previous day double lastOpen = iOpen(Symbol(), PERIOD_D1, 1); double lastClose = iClose(Symbol(), PERIOD_D1, 1); // Determine if the last day was bullish or bearish bool isBullishDay = lastClose > lastOpen; // Bullish if close > open bool isBearishDay = lastClose < lastOpen; // Bearish if close < open // Check if there are any open positions before executing breakout strategy if (PositionsTotal() == 0) // Only proceed if no positions are open { // Buy condition: Closing price is above the upper Donchian band on a bullish day if (closePrice > ExtUpBuffer[1] && isBullishDay) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBreakoutBuyOrder(stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band on a bearish day if (closePrice < ExtDnBuffer[1] && isBearishDay) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenBreakoutSellOrder(stopLoss, takeProfit); } } }
ブレイクアウト取引の確認
この関数は、市場の状況を分析するために、ドンチャンチャネルインジケーターから最新の値を取得します。現在のローソク足の終値と、前日の始値および終値を取得し、これらの価格を比較することで、前日が強気か弱気かを判断します。その後、ブレイクアウト戦略を実行する前に、ポジションが開かれているかどうかを確認します。ポジションがない場合、買い注文(強気の日で終値が上限バンドを上回る場合)または売り注文(弱気の日で終値が下限バンドを下回る場合)を出す条件を確認します。注文を出す前に、それぞれの取引の損切りと利食いのレベルを計算します。
//+------------------------------------------------------------------+ //| Open a buy order for the Breakout strategy | //+------------------------------------------------------------------+ void OpenBreakoutBuyOrder(double stopLoss, double takeProfit) { if (trade.Buy(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Buy")) { Print("Breakout Buy order placed."); } else { Print("Error placing Breakout Buy order: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Open a sell order for the Breakout strategy | //+------------------------------------------------------------------+ void OpenBreakoutSellOrder(double stopLoss, double takeProfit) { if (trade.Sell(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Sell")) { Print("Breakout Sell order placed."); } else { Print("Error placing Breakout Sell order: ", GetLastError()); } }
4. 注文有効期限チェック
CheckOrderExpiration関数は、すべての未決済ポジションを確認し、指定された有効期限を超えたポジションを特定してクローズします。この機能は、新鮮な取引環境を維持し、リスクを効果的に管理し、古いポジションが戦略的に望ましい期間よりも長く開いたままになるのを防ぐために非常に重要です。この関数では、各ポジションのマジックナンバーをチェックして、それがトレンドフォロー戦略の一部であるかどうかを確認し、現在時刻とポジションのオープン時刻を比較して、ポジションをクローズする必要があるかどうかを判断します。
//+------------------------------------------------------------------+ //| Check for expired Trend Following orders | //+------------------------------------------------------------------+ void CheckOrderExpiration() { for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (PositionSelectByTicket(ticket)) { long magicNumber = PositionGetInteger(POSITION_MAGIC); // Check if it's a Trend Following position if (magicNumber == MagicNumber) { datetime openTime = (datetime)PositionGetInteger(POSITION_TIME); if (TimeCurrent() - openTime >= OrderLifetime) { // Attempt to close the position if (trade.PositionClose(ticket)) { Print("Trend Following position closed due to expiration."); } else { Print("Error closing position due to expiration: ", GetLastError()); } } } } } }
5. TrailingStopLogic
TrailingStopLogicメソッドは、トレーリングストップのルールに従って損切りレベルを調整することで、既存のポジションを管理します。ロングポジションの場合、現在の価格がトレーリングストップの閾値を超えた際にストップロスを上に移動させます。ショートポジションの場合、条件が整うとストップロスを引き下げます。このアプローチは、ストップロスを有利な価格変動に追随させることで利益を確保し、相場が反転した際の損失リスクを軽減します。
//+------------------------------------------------------------------+ //| Manage trailing stops for open positions | //+------------------------------------------------------------------+ void TrailingStopLogic() { for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (PositionSelectByTicket(ticket)) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); double stopLoss = PositionGetDouble(POSITION_SL); // Update stop loss for long positions if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { if (currentPrice - stopLoss > TrailingStop * _Point || stopLoss == 0) { trade.PositionModify(ticket, currentPrice - TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } // Update stop loss for short positions else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { if (stopLoss - currentPrice > TrailingStop * _Point || stopLoss == 0) { trade.PositionModify(ticket, currentPrice + TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } } } }
6. クリーンアップ関数
OnDeinit()関数は、EAがチャートから削除された際に実行されるクリーンアップルーチンです。この関数は、割り当てられたリソースやインジケーターハンドルの解放を処理し、メモリリークや不正な参照が残らないことを保証します。EAが適切に初期化解除されたことを確認し、この操作をログに記録します。
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release indicators and handles IndicatorRelease(rsi_handle); IndicatorRelease(handle); Print("Expert deinitialized."); }
テストと結果
下の画像は、新しい機能が正常に動作している私たちのストラテジーテスターの結果です。
Trend Constraint EA:ドンチャンチャネルブレイクアウトとトレンドフォロー戦略
結論
このディスカッションでは、1つのEAに複数の戦略を組み込むことで、さまざまな市場環境に適応するという課題を取り上げました。当初、ブレイクアウトプロセスを効果的に管理するためのミニBreakout EAを開発し、それをメインのTrend Constraint EAに統合しました。この統合により、ブレイクアウト戦略をより上位の時間枠の市場センチメント、特にD1ローソク足分析に合わせることができ、過剰な取引執行を減少させることができました。
ストラテジーテスターで実行された各取引には、採用された戦略が明確に注釈され、その基礎となる仕組みを理解する上で透明性と明瞭性が提供されました。複雑な問題に効果的に取り組むためには、それを管理可能な小さな構成要素に分解し、ひとつひとつ段階を踏んで対処することが重要です。このアプローチを採ることで、ミニEAを開発し、それを統合するという形で戦略を1つにまとめることができました。
私たちの実装は可能性を示していますが、まだ改善の余地があり、未完成の状態です。このEAは教育ツールとして、異なる戦略がどのように連動し、より良い取引結果を生むかを示しています。提供されたEAを試してみて、ご自分の取引戦略に合うように修正することをお勧めします。アルゴリズム取引における複数の戦略を組み合わせる可能性を探求するためには、皆様からのフィードバックが不可欠です。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16137





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