
MQL5 クックブック:トリプルスクリーン戦略に基づくトレーディングシステムに対するフレームワーク作成
はじめに
トレーディングシステムの開発を検索するとき、多くのトレーダーは Dr. Alexander Elder博士によって紹介されている「トリプルスクリーン」戦略について聴いたことがあるにちがいありません。インターネットでその戦略に否定的な判定をする人は多くいるものです。しかし数多くの人が収益を得るのにそれが役立つと考えています。その2つの意見のどちらかを信用する必要はありません。すべてはまず確認が必要です。プログラミングを学習しているなら、バックテストによってトレーディング戦略のパフォーマンスを確認することができるので、すべてはあなたの手の中です。
本稿では MQL5で「リプルスクリーン」戦略に基づくトレーディングシステムに対するフレームワークを作成します。Expert Advisor を一から作成することはしません。代わりに、先行記事 "MQL5 Cookbook: Using Indicators to Set Trading Conditions in Expert Advisors" のプログラムを変更するだけとします。よって本稿は既製プログラムのパターンを簡単に変更する方法もお伝えします。
先行記事からの Expert Advisor はすでにストップロス/テイクプロフィット/トレーリングストップレベル、ポジションボリュームの増加、逆シグナルでのポジションリバースを有効/無効にする機能を備えています。必要な関数はすべて所定箇所に設定されています。よってわれわれのタスクはその他オプションの追加と外部関数をいくつか変更することで外部パラメータリストを変更することが中心です。
説明用に移動平均インディケータを用いて作成されるタイムフレーム3種類でシグナルを作成します。そののち、作成したフレームワークで実験を続ける際コードをわずかに変更することでその他あらゆるインディケータを取り入れることができるようになります。また各画面に対しタイムフレームを設定する機能も実装します。インディケータ起案に影響を与えるパラメータがゼロ値であれば、これは対応する画面が使用されないことを表します。すなわち、システムは1~2のタイムフレームを持つように設定することができるのです。
スタート前に先行記事から借用する Expert Advisor のファイルを持つフォルダをコピーし、その名前を変えます。
Expert Advisor の作成
それでは外部パラメータから始めましょう。描きは更新済みリストのコードです。新しい行を抜粋します。タイムフレームは列挙タイプ ENUM_TIMEFRAMES を使って宣言されています。ドロップダウンリストから任意のタイムフレームを選択することができます。
//--- External parameters of the Expert Advisor sinput long MagicNumber=777; // Magic number sinput int Deviation=10; // Slippage //--- input ENUM_TIMEFRAMES Screen01TimeFrame=PERIOD_W1; // Time frame of the first screen input int Screen01IndicatorPeriod=14; // Indicator period of the first screen //--- input ENUM_TIMEFRAMES Screen02TimeFrame=PERIOD_D1; // Time frame of the second screen input int Screen02IndicatorPeriod=24; // Indicator period of the second screen //--- input ENUM_TIMEFRAMES Screen03TimeFrame=PERIOD_H4; // Time frame of the third screen input int Screen03IndicatorPeriod=44; // Indicator period of the third screen //--- input double Lot=0.1; // Lot input double VolumeIncrease=0.1; // Position volume increase input double VolumeIncreaseStep=10; // Step for position volume increase input double StopLoss=50; // Stop Loss input double TakeProfit=100; // Take Profit input double TrailingStop=10; // Trailing Stop input bool Reverse=true; // Position reversal sinput bool ShowInfoPanel=true; // Display of the info panel
IndicatorSegments パラメータは AllowedNumberOfSegments 変数、CorrectInputParameters() 関数と共に例からは除外します。この条件のご興味のある方はご自身で実装してみることができます。またこの Expert Advisor はインディケータを1件だけ採用するためEnums.mqh ファイルのインディケータの列挙も除外します。
それぞれのタイムフレームには個別のインディケータがあるため、各インディケータのハンドルを取得するには個別の変数が必要となります。
//--- Indicator handles int Screen01IndicatorHandle=INVALID_HANDLE; // Indicator handle on the first screen int Screen02IndicatorHandle=INVALID_HANDLE; // Indicator handle on the second screen int Screen03IndicatorHandle=INVALID_HANDLE; // Indicator handle on the third screen
新規バーは最小タイムフレームを用いて確認されます。外部パラメータに最小タイムフレームを設定するとき、一定の順序に従う必要はありません。すなわち最大、中間、最小などです。逆順と基本的に任意の順序でいけます。よって指定の全タイムフレームから最小タイムフレームを特定する関数が必要です。
Expert Advisor は1種類、2種類、3種類のタイムフレームで処理するよう設定することができるため、最小タイムフレームを判断する際すべてのオプションが考慮される必要があります。GetMinimumTimeframe() 関数のコードの場合は以下です。
//+------------------------------------------------------------------+ //| Determining the minimum time frame for the new bar check | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES GetMinimumTimeframe(ENUM_TIMEFRAMES timeframe1,int period1, ENUM_TIMEFRAMES timeframe2,int period2, ENUM_TIMEFRAMES timeframe3,int period3) { //--- Default minimum time frame value ENUM_TIMEFRAMES timeframe_min=PERIOD_CURRENT; //--- Convert time frame values to seconds for calculations int t1= PeriodSeconds(timeframe1); int t2= PeriodSeconds(timeframe2); int t3= PeriodSeconds(timeframe3); //--- Check for incorrect period values if(period1<=0 && period2<=0 && period3<=0) return(timeframe_min); //--- Conditions for a single time frame if(period1>0 && period2<=0 && period3<=0) return(timeframe1); if(period2>0 && period1<=0 && period3<=0) return(timeframe2); if(period3>0 && period1<=0 && period2<=0) return(timeframe3); //--- Conditions for two time frames if(period1>0 && period2>0 && period3<=0) { timeframe_min=(MathMin(t1,t2)==t1) ? timeframe1 : timeframe2; return(timeframe_min); } if(period1>0 && period3>0 && period2<=0) { timeframe_min=(MathMin(t1,t3)==t1) ? timeframe1 : timeframe3; return(timeframe_min); } if(period2>0 && period3>0 && period1<=0) { timeframe_min=(MathMin(t2,t3)==t2) ? timeframe2 : timeframe3; return(timeframe_min); } //--- Conditions for three time frames if(period1>0 && period2>0 && period3>0) { timeframe_min=(int)MathMin(t1,t2)==t1 ? timeframe1 : timeframe2; int t_min=PeriodSeconds(timeframe_min); timeframe_min=(int)MathMin(t_min,t3)==t_min ? timeframe_min : timeframe3; return(timeframe_min); } return(WRONG_VALUE); }
最小タイムフレーム値を保存するためにもう一つ別のグローバルスコープ変数を作成します。
//--- Variable for determining the minimum time frame ENUM_TIMEFRAMES MinimumTimeframe=WRONG_VALUE;
OnInit() 関数内で Expert Advisor が初期化される場合GetMinimumTimeframe() 関数が呼ばれる必要があります。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Determine the minimum time frame for the new bar check MinimumTimeframe=GetMinimumTimeframe(Screen01TimeFrame,Screen01IndicatorPeriod, Screen02TimeFrame,Screen02IndicatorPeriod, Screen03TimeFrame,Screen03IndicatorPeriod); //--- Get indicator handles GetIndicatorHandles(); //--- Initialize the new bar CheckNewBar(); //--- Get the properties GetPositionProperties(P_ALL); //--- Set the info panel SetInfoPanel(); //--- return(0); }
それからMinimumTimeframe 変数値が関数 CheckNewBar() および GetBarsData() で使用されます。
以下がGetIndicatorHandle() 関数の今の表記です。器官およびタイムフレームが各インディケータに対して指定されています。
//+------------------------------------------------------------------+ //| Getting indicator handles | //+------------------------------------------------------------------+ void GetIndicatorHandles() { //--- Get handles of the indicators specified in the parameters if(Screen01IndicatorPeriod>0) Screen01IndicatorHandle=iMA(_Symbol,Screen01TimeFrame,Screen01IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE); if(Screen02IndicatorPeriod>0) Screen02IndicatorHandle=iMA(_Symbol,Screen02TimeFrame,Screen02IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE); if(Screen03IndicatorPeriod>0) Screen03IndicatorHandle=iMA(_Symbol,Screen03TimeFrame,Screen03IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE); //--- If the indicator handle for the first time frame could not be obtained if(Screen01IndicatorHandle==INVALID_HANDLE) Print("Failed to get the indicator handle for Screen 1!"); //--- If the indicator handle for the second time frame could not be obtained if(Screen01IndicatorHandle==INVALID_HANDLE) Print("Failed to get the indicator handle for Screen 2!"); //--- If the indicator handle for the third time frame could not be obtained if(Screen01IndicatorHandle==INVALID_HANDLE) Print("Failed to get the indicator handle for Screen 3!"); }
またインディケータ値を取得するために配列を追加する必要があります(各タイムフレーム値に個別に)。
//--- Arrays for values of the indicators double indicator_buffer1[]; double indicator_buffer2[]; double indicator_buffer3[];
インディケータ値を取得するため GetIndicatorsData() 関数記述は以下です。取得したハンドルは正確性がチェックされ、すべてが問題なければインディケータ値が配列に書き込まれます。
//+------------------------------------------------------------------+ //| Getting indicator values | //+------------------------------------------------------------------+ bool GetIndicatorsData() { //--- Number of indicator buffer values for determining the trading signal int NumberOfValues=3; //--- If indicator handles have not been obtained if((Screen01IndicatorPeriod>0 && Screen01IndicatorHandle==INVALID_HANDLE) || (Screen02IndicatorPeriod>0 && Screen02IndicatorHandle==INVALID_HANDLE) || (Screen03IndicatorPeriod>0 && Screen03IndicatorHandle==INVALID_HANDLE)) //--- try to get them again GetIndicatorHandles(); //--- If the time frame of the first screen is used and the indicator handle has been obtained if(Screen01TimeFrame>0 && Screen01IndicatorHandle!=INVALID_HANDLE) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); //--- Get indicator values if(CopyBuffer(Screen01IndicatorHandle,0,0,NumberOfValues,indicator_buffer1)<NumberOfValues) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); //--- return(false); } } //--- If the time frame of the second screen is used and the indicator handle has been obtained if(Screen02TimeFrame>0 && Screen02IndicatorHandle!=INVALID_HANDLE) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer2,true); //--- Get indicator values if(CopyBuffer(Screen02IndicatorHandle,0,0,NumberOfValues,indicator_buffer2)<NumberOfValues) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer2 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); //--- return(false); } } //--- If the time frame of the third screen is used and the indicator handle has been obtained if(Screen03TimeFrame>0 && Screen03IndicatorHandle!=INVALID_HANDLE) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer3,true); //--- Get indicator values if(CopyBuffer(Screen03IndicatorHandle,0,0,NumberOfValues,indicator_buffer3)<NumberOfValues) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer3 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); //--- return(false); } } //--- return(true); }
関数 GetTradingSignal() および GetSignal() は対象タスクに応じて変更する必要があります。以下は検討する関数のコードです。
//+------------------------------------------------------------------+ //| Determining trading signals | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetTradingSignal() { //--- If there is no position if(!pos.exists) { //--- A Sell signal if(GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- A Buy signal if(GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); } //--- If the position exists if(pos.exists) { //--- Get the position type GetPositionProperties(P_TYPE); //--- Get the last deal price GetPositionProperties(P_PRICE_LAST_DEAL); //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } //--- No signal return(WRONG_VALUE); }
ちょうど最小タイムフレームで決めたように GetSignal() 関数はポジションオープンの条件に関する外部パラメータ状態の可能性ある全変数うを考慮します。以下がその関数コードです。
//+------------------------------------------------------------------+ //| Checking the condition and returning a signal | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetSignal() { //--- A SELL SIGNAL: the current value of the indicators on completed bars is lower than on the previous bars //--- Conditions for a single time frame if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod<=0 && Screen03IndicatorPeriod<=0) { if(indicator_buffer1[1]<indicator_buffer1[2]) return(ORDER_TYPE_SELL); } if(Screen01IndicatorPeriod<=0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod<=0) { if(indicator_buffer2[1]<indicator_buffer2[2]) return(ORDER_TYPE_SELL); } //--- if(Screen01IndicatorPeriod<=0 && Screen02IndicatorPeriod<=0 && Screen03IndicatorPeriod>0) { if(indicator_buffer3[1]<indicator_buffer3[2]) return(ORDER_TYPE_SELL); } //--- Conditions for two time frames if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod<=0) { if(indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer2[1]<indicator_buffer2[2]) return(ORDER_TYPE_SELL); } if(Screen01IndicatorPeriod<=0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod>0) { if(indicator_buffer2[1]<indicator_buffer2[2] && indicator_buffer3[1]<indicator_buffer3[2]) return(ORDER_TYPE_SELL); } if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod<=0 && Screen03IndicatorPeriod>0) { if(indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer3[1]<indicator_buffer3[2]) return(ORDER_TYPE_SELL); } //--- Conditions for three time frames if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod>0) { if(indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer2[1]<indicator_buffer2[2] && indicator_buffer3[1]<indicator_buffer3[2] ) return(ORDER_TYPE_SELL); } //--- A BUY SIGNAL: the current value of the indicators on completed bars is higher than on the previous bars //--- Conditions for a single time frame if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod<=0 && Screen03IndicatorPeriod<=0) { if(indicator_buffer1[1]>indicator_buffer1[2]) return(ORDER_TYPE_BUY); } if(Screen01IndicatorPeriod<=0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod<=0) { if(indicator_buffer2[1]>indicator_buffer2[2]) return(ORDER_TYPE_BUY); } if(Screen01IndicatorPeriod<=0 && Screen02IndicatorPeriod<=0 && Screen03IndicatorPeriod>0) { if(indicator_buffer3[1]>indicator_buffer3[2]) return(ORDER_TYPE_BUY); } //--- Conditions for two time frames if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod<=0) { if(indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer2[1]>indicator_buffer2[2]) return(ORDER_TYPE_BUY); } if(Screen01IndicatorPeriod<=0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod>0) { if(indicator_buffer2[1]>indicator_buffer2[2] && indicator_buffer3[1]>indicator_buffer3[2]) return(ORDER_TYPE_BUY); } if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod<=0 && Screen03IndicatorPeriod>0) { if(indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer3[1]>indicator_buffer3[2]) return(ORDER_TYPE_BUY); } //--- Conditions for three time frames if(Screen01IndicatorPeriod>0 && Screen02IndicatorPeriod>0 && Screen03IndicatorPeriod>0) { if(indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer2[1]>indicator_buffer2[2] && indicator_buffer3[1]>indicator_buffer3[2] ) return(ORDER_TYPE_BUY); } //--- No signal return(WRONG_VALUE); }
ここでは関数 OnInit() および OnDeinit() にちょっとした変更を加えるだけです。加えた変更は下記コードの強調表示箇所で確認できます。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Determine the minimum time frame for the new bar check MinimumTimeframe=GetMinimumTimeframe(Screen01TimeFrame,Screen01IndicatorPeriod, Screen02TimeFrame,Screen02IndicatorPeriod, Screen03TimeFrame,Screen03IndicatorPeriod); //--- Get indicator handles GetIndicatorHandles(); //--- Initialize the new bar CheckNewBar(); //--- Get the properties GetPositionProperties(P_ALL); //--- Set the info panel SetInfoPanel(); //--- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Print the deinitialization reason to the journal Print(GetDeinitReasonText(reason)); //--- When deleting from the chart if(reason==REASON_REMOVE) { //--- Delete all objects relating to the info panel from the chart DeleteInfoPanel(); //--- Delete the indicator handles IndicatorRelease(Screen01IndicatorHandle); IndicatorRelease(Screen02IndicatorHandle); IndicatorRelease(Screen03IndicatorHandle); } }
トリプルスクリーン戦略に基づくトレーディングシステムに対するフレームワークは準備できました。インディケータを変更するか別の条件を追加するなどして、必要に応じそれはいつでも変更可能です。
パラメータの最適化と Expert Advisorの検証
パラメータの最適化に進み、結果を確認します。ストラテジーテスタは以下に示されているように設定します(3種類のタイムフレームで一番低いものをかなラス指定します)。
図1 ストラレジーテスタ設定
最適化するための Expert Advisor パラメータは以下のように設定済みです。タイムフレームは最適化用に設定されますが、私はマニュアルで設定するのを好みます。
図2 Expert Advisorの設定
デュアルコアプロセッサの場合、最適化は約30分で完了します。最適化グラフ は以下に提供されています。
図3 最適化グラフ
最大残高検証結果は最大リカバリーファクタ検証結果よりも低いドローダウンを示しています。これは最大残高検証結果がデモ目的で使用されているためです。
図4 最大残高検証結果
図5 最大残高検証グラフ
おわりに
本稿はメインの関数が利用可能であれば Expert Advisor がひじょうに迅速に変更されることを実践的に示しました。シグナルブロックとインディケータを変更するだけで新しいトレーディングシステムを手に入れることができるのです。本稿にはみなさんの自習用に本稿に述べられているExpert Advisor のコードを持つダウンロードできるアーカイブが添付されています。また入力パラメータ設定のある設定ファイルも添付されています。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/647





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