
MQL5 クックブック-ピボットトレーディングシグナル
イントロダクション
これまでの記事では、トレードシグナルを生成するインジケーターに関して扱ってきました。 今回は、ピボット-反転レベル (ポイント) を考察しましょう。 もう一度標準ライブラリを適用します。 まず、反転のインジケーターを検討し、基本的な戦略を決定し、最終的に改善手段を模索します。
ただし、読者がトレードシグナルジェネレーターを開発するための CExpertSignal の基本クラスに精通していることを前提としています。
1. ピボット (反転レベル) インジケーター
この戦略に、潜在的な反転レベルをプロットするインジケーターを使用します。 プロットは、グラフィカルな構造によってのみ実行されます。 グラフィカルオブジェクトは適用されません。 このアプローチの主な利点は、最適化モードでインジケーターを参照できることです。 一方、グラフィカル構造は、インジケータバッファを超えることはできません。
レベルは、様々な方法でカウントすることができます。 このテーマの詳細については、「ピボットポイント分析に基づくトレーディング戦略」を参照してください。
まず、標準的なアプローチを考えてみましょう。 (次の方程式で定義されます):
RES は1番のレジスタンスレベルで、SUP は第1のサポートレベルです。 メインの反転レベル (PP)が一つ、抵抗レベル (RES) とサポートレベル (SUP) が6つずつあります。
したがって、視覚的に表示されるインジケーターは、異なる価格でプロットした水平レベルのセットのように見えます。 初めて起動した場合、インジケーターは現在の日足レベルのみを描画します (図1)。
図1.ピボットインジケーター: 現在の日足のプロットする
ここでは、インジケーターコードブロックを調べてみましょう。
新しい日足ができると、すべてのレベルをカウントする必要があります。
//---新しい日足 Rif(gNewDay.isNewBar(today)) { PrintFormat("New day: %s",TimeToString(today)); //---価格を正規化 double d_high=NormalizeDouble(daily_rates[0].high,_Digits); double d_low=NormalizeDouble(daily_rates[0].low,_Digits); double d_close=NormalizeDouble(daily_rates[0].close,_Digits); //---価格を保存 gYesterdayHigh=d_high; gYesterdayLow=d_low; gYesterdayClose=d_close; //--- 1) pivot: PP = (HIGH + LOW + CLOSE) / 3 gPivotVal=NormalizeDouble((gYesterdayHigh+gYesterdayLow+gYesterdayClose)/3.,_Digits); //--- 4) RES1.0 = 2*PP - LOW gResVal_1_0=NormalizeDouble(2.*gPivotVal-gYesterdayLow,_Digits); //--- 5) SUP1.0 = 2*PP – HIGH gSupVal_1_0=NormalizeDouble(2.*gPivotVal-gYesterdayHigh,_Digits); //--- 8) RES2.0 = PP + (HIGH -LOW) gResVal_2_0=NormalizeDouble(gPivotVal+(gYesterdayHigh-gYesterdayLow),_Digits); //--- 9) SUP2.0 = PP - (HIGH – LOW) gSupVal_2_0=NormalizeDouble(gPivotVal-(gYesterdayHigh-gYesterdayLow),_Digits); //--- 12) RES3.0 = 2*PP + (HIGH – 2*LOW) gResVal_3_0=NormalizeDouble(2.*gPivotVal+(gYesterdayHigh-2.*gYesterdayLow),_Digits); //--- 13) SUP3.0 = 2*PP - (2*HIGH – LOW) gSupVal_3_0=NormalizeDouble(2.*gPivotVal-(2.*gYesterdayHigh-gYesterdayLow),_Digits); //--- 2) RES0.5 = (PP + RES1.0) / 2 gResVal_0_5=NormalizeDouble((gPivotVal+gResVal_1_0)/2.,_Digits); //--- 3) SUP0.5 = (PP + SUP1.0) / 2 gSupVal_0_5=NormalizeDouble((gPivotVal+gSupVal_1_0)/2.,_Digits); //--- 6) RES1.5 = (RES1.0 + RES2.0) / 2 gResVal_1_5=NormalizeDouble((gResVal_1_0+gResVal_2_0)/2.,_Digits); //--- 7) SUP1.5 = (SUP1.0 + SUP2.0) / 2 gSupVal_1_5=NormalizeDouble((gSupVal_1_0+gSupVal_2_0)/2.,_Digits); //--- 10) RES2.5 = (RES2.0 + RES3.0) / 2 gResVal_2_5=NormalizeDouble((gResVal_2_0+gResVal_3_0)/2.,_Digits); //--- 11) SUP2.5 = (SUP2.0 + SUP3.0) / 2 gSupVal_2_5=NormalizeDouble((gSupVal_2_0+gSupVal_3_0)/2.,_Digits); //--- current day start bar gDayStart=today; //--- find the start bar of the active TF //---時系列 for(int bar=0;bar<rates_total;bar++) { //--- selected bar time datetime curr_bar_time=time[bar]; user_date.DateTime(curr_bar_time); //--- selected bar day datetime curr_bar_time_of_day=user_date.DateOfDay(); //--- if the current bar was the day before if(curr_bar_time_of_day<gDayStart) { //--- save the start bar gBarStart=bar-1; break; } } //---ローカルカウンタをリセットします。 prev_calc=0; }
赤色 は、レベルが再計算される文字列の強調表示です。 次に、現在のタイムフレームのバーを、プロットレベルの開始点として使用する必要があります。 この値は、 gBarStart変数によって定義されます。 SUserDateTimeカスタム構造 ( cdatetime構造体) は、検索時に日付と時刻の操作に使用します。
さて、現在のタイムフレームバーのバッファ値を埋めるために設計されたブロックに着目してみましょう。
//---新しいバーがアクティブな TF にある場合 if(gNewMinute.isNewBar(time[0])) { //---計算が実行されるまでの足 int bar_limit=gBarStart; //---最初ではない場合 if(prev_calc>0) bar_limit=rates_total-prev_calc; //---バッファを計算する for(int bar=0;bar<=bar_limit;bar++) { //---1) ピボット gBuffers[0].data[bar]=gPivotVal; //---2) res 0.5 if(gToPlotBuffer[1]) gBuffers[1].data[bar]=gResVal_0_5; //---3) sup 0.5 if(gToPlotBuffer[2]) gBuffers[2].data[bar]=gSupVal_0_5; //---4) res 1.0 if(gToPlotBuffer[3]) RgBuffers[3].data[bar]=gResVal_1_0; //---5) sup 1.0 if(gToPlotBuffer[4]) gBuffers[4].data[bar]=gSupVal_1_0; //---6) res 1.5 if(gToPlotBuffer[5]) gBuffers[5].data[bar]=gResVal_1_5; //---7) 1.5 sup if(gToPlotBuffer[6]) gBuffers[6].data[bar]=gSupVal_1_5; //---8) res 2.0 if(gToPlotBuffer[7]) gBuffers[7].data[bar]=gResVal_2_0; //---9) sup 2.0 if(gToPlotBuffer[8]) gBuffers[8].data[bar]=gSupVal_2_0; //---10) res 2.5 if(gToPlotBuffer[9]) gBuffers[9].data[bar]=gResVal_2_5; //---11) sup 2.5 if(gToPlotBuffer[10]) gBuffers[10].data[bar]=gSupVal_2_5; //---12) res 3.0 if(gToPlotBuffer[11]) gBuffers[11].data[bar]=gResVal_3_0; //---13) sup 3.0 if(gToPlotBuffer[12]) gBuffers[12].data[bar]=gSupVal_3_0; } }
バッファの計算は、インジケーターが開始されるチャートに新しいバーが表示されたときに開始します。 黄色は、バッファの計算対象となるバー番号の定義を強調表示します。 計算されたバーのローカルカウンターが使われます。 新しい日の初めにprev_calculatedがゼロにリセットされないため、リセットが必要です。
ピボットインジケータのコードは、 URPivots.mq5にあります。
2. 基本戦略
説明したインジケーターに基づいて簡単な基本戦略を作成しましょう。 オープン・シグナルは、中央のピボットを基準とした始値の位置に依存します。 ピボットレベルに触れる価格は、シグナルの確認になります。
EURUSD M15 チャート (図2) は、中央のピボットの下の1日 (2015) オープンレベルを表示します。 しかし、その日のうちに、価格が上向きにピボットレベルに触れました。 その場合、売りシグナルです。 ストップロスも利確も得られない場合は、次の日の初めに相場の決済が実行されます。
図2. 基本戦略: 売りシグナル
ストップレベルは、ピボットインジケーターの反転レベルにバインドされます。 $1.18153 の中間の抵抗レベルの Res 0.5 はストップロスとして役立ちます。 $1.17301 でのメインサポートレベル Sup 1.0 は、テイクプロフィットとして使用します。 1月14日以降のトレードに戻ります。 その間に、基本的な戦略の本質を形成するため、コードを見てみましょう。
2.1 CSignalPivots signal class
価格ダイナミクスと反転レベルのインジケーターに基づいて形成された様々なパターンからシグナルを生成するクラスを作成してみましょう。
//+------------------------------------------------------------------+ //| Class CSignalPivots | //目的: ピボットに基づくトレードシグナルのクラス。 | //| CExpertSignal class descendant. | //+------------------------------------------------------------------+ class CSignalPivots : public CExpertSignal { //---= = = データメンバー = = =--- protected: CiCustom m_pivots; //"ピボット" インジケータオブジェクト //---調節可能な変数 bool m_to_plot_minor; //セカンダリレベルをプロットする double m_pnt_near; // tolerance //---推定 double m_pivot_val; //ピボット値 double m_daily_open_pr; //現在の日足の始値 CisNewBar m_day_new_bar; //日足の TF の新しいバー //---相場パターン //---1) パターン 0 "pp レベルの最初のタッチ" (トップで買い、ボトムで売り) int m_pattern_0; // weight bool m_pattern_0_done; // sign that a pattern is over //---= = = メソッド = = =--- public: //---コンストラクタ/デストラクタ void CSignalPivots(void); void ~CSignalPivots(void){}; //---調節可能な変数を設定するメソッド void ToPlotMinor(const bool _to_plot) {m_to_plot_minor=_to_plot;} void PointsNear(const uint _near_pips); //---相場モデルの「ウエイト」を調整するメソッド void Pattern_0(int _val) {m_pattern_0=_val;m_pattern_0_done=false;} //---設定の検証メソッド virtual bool ValidationSettings(void); //---インジケーターと時系列を作成するメソッド virtual bool InitIndicators(CIndicators *indicators); //---相場モデルが生成されているかどうかをチェックするメソッド virtual int LongCondition(void); virtual int ShortCondition(void); virtual double Direction(void); //---相場参入のレベルを検出するためのメソッド virtual bool OpenLongParams(double &price,double &sl,double &tp,datetime &expiration); virtual bool OpenShortParams(double &price,double &sl,double &tp,datetime &expiration); //--- protected: //---インジケーターの初期化メソッド bool InitCustomIndicator(CIndicators *indicators); //---ピボットレベルの値を取得する double Pivot(void) {return(m_pivots.GetData(0,0));} //---メイン抵抗レベルの値を取得します。 double MajorResistance(uint _ind); //---2番目の抵抗レベルの値を取得する double MinorResistance(uint _ind); //---メインサポートレベルの値を取得する double MajorSupport(uint _ind); //---二番目のサポートレベルの値を取得する double MinorSupport(uint _ind); }; //+------------------------------------------------------------------+
以前に記事「MQL5 クックブック-移動チャネルのトレードシグナル」でそのアプローチを使用したことがあります。価格がラインゾーンに落ちるときラインに触れる価格が確認されます。 m_pnt_nearデータメンバは、反転レベルの許容値を設定します。
クラスによって提供されるシグナルパターンは、最も重要な役割を果たします。 基本クラスは、1つのパターンを持つことです。 ウエイト (m_pattern_0) とは別に、 (m_pattern_0_done) 内に完了プロパティもあります。
CExpertSignal基本シグナルクラスは、仮想メソッドが豊富です。 派生クラスの微調整ができます。
特に、トレードレベルを計算するためのOpenLongParams ()およびOpenShortParams ()メソッドを再定義しました。
最初のメソッドのコード (トレードレベルの値を定義する) を調べてみましょう。
//+------------------------------------------------------------------+ //トレードレベルを定義する | //+------------------------------------------------------------------+ bool CSignalPivots::OpenLongParams(double &price,double &sl,double &tp,datetime &expiration) { bool params_set=false; sl=tp=WRONG_VALUE; //---パターン0が考慮されている場合 if(IS_PATTERN_USAGE(0)) //--- パターン0が考慮されていない場合 if(!m_pattern_0_done) { //--- Open price - market double base_price=m_symbol.Ask(); price=m_symbol.NormalizePrice(base_price-m_price_level*PriceLevelUnit()); //--- sl price - Sup0.5 level sl=this.MinorSupport(0); if(sl==DBL_MAX) return false; //---sl が設定されている場合 sl=m_symbol.NormalizePrice(sl); //--- tp price - Res1.0 level tp=this.MajorResistance(0); if(tp==DBL_MAX) return false; //---tpが設定されている場合 tp=m_symbol.NormalizePrice(tp); expiration+=m_expiration*PeriodSeconds(m_period); //---価格が設定されている場合 params_set=true; //--- pattern complete m_pattern_0_done=true; } //--- return params_set; } //+------------------------------------------------------------------+
MinorSupport ()を使用して、最初のセカンダリサポートレベルの値として、ストップロスが計算されます。 テイクプロフィットは、MajorResistance ()を使用して、最初のメインレジスタンスレベルで設定されています。 売りの場合、MinorResistance ()およびMajorSupport ()に従って置き換えられます。
カスタムシグナルをメインにして、トレーディングレベルを定義するためのメソッドが適切に動作できるようにします。 親クラスのトレードレベルを定義するメソッドは次のようになります。
//+------------------------------------------------------------------+ //買いのレベルを検出する | //+------------------------------------------------------------------+ bool CExpertSignal::OpenLongParams(double &price,double &sl,double &tp,datetime &expiration) { CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL; //--- if(general==NULL) { //---基本価格が明示的に指定されていない場合は、現在の相場価格 double base_price=(m_base_price==0.0) ? m_symbol.Ask() : m_base_price; price =m_symbol.NormalizePrice(base_price-m_price_level*PriceLevelUnit()); sl =(m_stop_level==0.0) ? 0.0 : m_symbol.NormalizePrice(price-m_stop_level*PriceLevelUnit()); tp =(m_take_level==0.0) ? 0.0 : m_symbol.NormalizePrice(price+m_take_level*PriceLevelUnit()); expiration+=m_expiration*PeriodSeconds(m_period); return(true); } //--- return(general.OpenLongParams(price,sl,tp,expiration)); } //+------------------------------------------------------------------+
メインシグナルインデックスが設定されていない場合、デフォルト値を受け取ります。 この問題を回避するには、シグナルを初期化するときに 次のように設定します。
//---CSignalPivots フィルタ CSignalPivots *filter0=new CSignalPivots; if(filter0==NULL) { //---エラー PrintFormat(__FUNCTION__+": error creating filter0"); return INIT_FAILED; } signal.AddFilter(filter0); signal.General(0);
買い条件のメソッドは次のとおりです。
//+------------------------------------------------------------------+ //| Check the buy condition | //+------------------------------------------------------------------+ int CSignalPivots::LongCondition(void) { int result=0; //---パターン0が考慮されていない場合 if(IS_PATTERN_USAGE(0)) //--- パターン0が考慮されていない場合 if(!m_pattern_0_done) //---日足がピボットの上にある場合 if(m_daily_open_pr>m_pivot_val) { //---現在のバーの最安値 double last_low=m_low.GetData(1); //--- if the price is received if((last_low>WRONG_VALUE) && (last_low<DBL_MAX)) //--- if there was a touch from above (considering the tolerance) if(last_low<=(m_pivot_val+m_pnt_near)) { result=m_pattern_0; //--- to the Journal Print("\n---== The price touches the pivot level from above ==---"); PrintFormat("Price: %0."+IntegerToString(m_symbol.Digits())+"f",last_low); PrintFormat("Pivot: %0."+IntegerToString(m_symbol.Digits())+"f",m_pivot_val); PrintFormat("Tolerance: %0."+IntegerToString(m_symbol.Digits())+"f",m_pnt_near); } } //--- return result; } //+------------------------------------------------------------------+
last_low < = (m_pivot_val + m_pnt_near)を考慮して、上からタッチをチェックするのは簡単です。
"重み付け" 方向を定義するためのDirection() メソッドは、基本パターンが完全である場合チェックします。
//+------------------------------------------------------------------+ //| Define the "weighted" direction | //+------------------------------------------------------------------+ double CSignalPivots::Direction(void) { double result=0.; //--- receive daily history data MqlRates daily_rates[]; if(CopyRates(_Symbol,PERIOD_D1,0,1,daily_rates)<0) return 0.; //---パターン0が完了した場合 if(m_pattern_0_done) { //---新しい日足を確認する if(m_day_new_bar.isNewBar(daily_rates[0].time)) { //---パターン補完フラグをリセット。 m_pattern_0_done=false; return 0.; } } //--- パターン0が考慮されていない場合 else { //---日足の始値 if(m_daily_open_pr!=daily_rates[0].open) m_daily_open_pr=daily_rates[0].open; //---ピボット double curr_pivot_val=this.Pivot(); if(curr_pivot_val<DBL_MAX) if(m_pivot_val!=curr_pivot_val) m_pivot_val=curr_pivot_val; } //---結果 result=m_weight*(this.LongCondition()-this.ShortCondition()); //--- return result; } //+------------------------------------------------------------------+
決済シグナルについては、親クラスメソッドCloseLongParams ()とCloseShortParams ()を再定義します。 サンプルの買いブロックコード:
//+------------------------------------------------------------------+ //買いのトレードレベルを定義する | //+------------------------------------------------------------------+ bool CSignalPivots::CloseLongParams(double &price) { price=0.; //---パターン0が考慮されている場合 if(IS_PATTERN_USAGE(0)) //--- パターン0が考慮されていない場合 if(!m_pattern_0_done) { price=m_symbol.Bid(); //--- to the Journal Print("\n---== Signal to close buy ==---"); PrintFormat("Market price: %0."+IntegerToString(m_symbol.Digits())+"f",price); return true; } //---結果を返す return false; } //+------------------------------------------------------------------+
EAのコードでは、決済シグナルのしきい値をゼロにリセットする必要があります。
その場合、方向チェックは行われません。
//+------------------------------------------------------------------+ //ロングポジションを閉じるためのシグナルを生成する | //+------------------------------------------------------------------+ bool CExpertSignal::CheckCloseLong(double &price) { bool result =false; //---"禁止" シグナル if(m_direction==EMPTY_VALUE) return(false); //---しきい値を超えた場合のチェック if(-m_direction>=m_threshold_close) { //---シグナルがある result=true; //---決済のレベルを取得 if(!CloseLongParams(price)) result=false; } //--- zeroize the base price m_base_price=0.0; //---結果を返す return(result); } //+------------------------------------------------------------------+
ここで問題が発生します。この場合、どうやって決済シグナルをチェックしたらよいでしょうか。 最初に、(Procesing()の) ポジションが存在するかチェックされ、次にm_pattern_0_doneプロパティ ( CloseLongParams ()およびCloseShortParams () メソッド) を使用します。 パターン0が不完全なときに EA がポジションを検出するとすぐに、決済します。 これは、日足の初めに発生します。
ここでは、 CSignalPivotsシグナルクラスの基本について検討しました。 次に、戦略クラスについて考察しましょう。
2.2 CPivotsExpert トレーディング戦略クラス
派生戦略クラスは、チャネルの移動と似ています。 最初の違いは、分単位のトレードモードではなく、ティックの代わりに使用されています。 ヒストリーで戦略をテストすることができます。 次に、決済の確認があります。 すでに EA は、ポジションを閉じることができます。
ハンドラメソッドは次のようになります。
//+------------------------------------------------------------------+ //Main module | //+------------------------------------------------------------------+ bool CPivotsExpert::Processing(void) { //---新しい分足 if(!m_minute_new_bar.isNewBar()) return false; //---計算方向 m_signal.SetDirection(); //---ポジションがない場合 if(!this.SelectPosition()) { //---ポジションオープニングモジュール if(this.CheckOpen()) return true; } //---ポジションがある場合 else { //---ポジションの決済モジュール if(this.CheckClose()) return true; } //---トレードオペレーションがない場合 return false; } //+------------------------------------------------------------------+
以上です。 これで、基本的な戦略を起動することができます。 コードは、 URBasePivotsTrader.mq5にあります。
図3. 基本戦略: 売り
2015年1月14日の日に戻りましょう。 この場合、このモデルは完全に働きました。 ピボットでショートポジションを保有し、メインサポートレベル sup 1.0 で決済しました。
EURUSD M15、07.01.2013 から07.01.2017で行われました:
- Entry signal threshold, [0...100] = 10;
- Weight, [0...1.0] = 1,0;
- Fixed volume = 0,1;
- Tolerance, points = 15.
着実な結果と戦略のトレードがわかりました。 負の結果 (図4)。
図4. eurusd: 2013-2016 の最初の基本戦略の結果
結果から判断すると、すべて間違っていました。 売りシグナルで買い、買いシグナルで売る必要がありました。 しかし、本当にそうでしょうか? 確認しましょう。 基本的な戦略を開発し、シグナルを変更する必要があります。 この場合、買い条件は次のようになります。
//+------------------------------------------------------------------+ //| Check condition for selling | //+------------------------------------------------------------------+ int CSignalPivots::LongCondition(void) { int result=0; //---パターン0が考慮されていない場合 if(IS_PATTERN_USAGE(0)) //--- パターン0が考慮されていない場合 if(!m_pattern_0_done) //--- if a day has opened below the pivot if(m_daily_open_pr<m_pivot_val) { //---現在のバーの最大値 double last_high=m_high.GetData(1); //--- if the price is received if((last_high>WRONG_VALUE) && (last_high<DBL_MAX)) //--- if there was a touch from above (considering the tolerance) if(last_high>=(m_pivot_val-m_pnt_near)) { result=m_pattern_0; //--- to the Journal Print("\n---== The price touches the pivot level from below ==---"); PrintFormat("Price: %0."+IntegerToString(m_symbol.Digits())+"f",last_high); PrintFormat("Pivot: %0."+IntegerToString(m_symbol.Digits())+"f",m_pivot_val); PrintFormat("Tolerance: %0."+IntegerToString(m_symbol.Digits())+"f",m_pnt_near); } } //--- return result; } //+------------------------------------------------------------------+
別の戦略を実行し、結果を得ましょう。
図5. : 2013-2016 の2番目の基本戦略の 結果
明らかに、最初の結果のミラーリングにはなりませんでした。 おそらく、この理由は、ストップロスとテイクプロフィット値です。 その上、トレードの間に作動するストップレベル無しのポジションは新しい日のタイミングで決済します。
基本的な戦略の2番目のバージョンを変更するので、ストップロスレベルは、オープンレートから遠くに配置されるようにしてみましょう。メインサポートレベル Sup 1.0 の前に、中間のレジスタンスレベルの Res 0.5 によって制限されています。 売りでは、ストップロスが Res 1.0 に配置されていますが、一方で利確値は Sup 0.5 にあります。
この場合、買いのトレードレベルは次のように定義されます。
//+------------------------------------------------------------------+ //| Define trade levels for buying | //+------------------------------------------------------------------+ bool CSignalPivots::OpenLongParams(double &price,double &sl,double &tp,datetime &expiration) { bool params_set=false; sl=tp=WRONG_VALUE; //---パターン0が考慮されている場合 if(IS_PATTERN_USAGE(0)) //--- パターン0が考慮されていない場合 if(!m_pattern_0_done) { //--- Open price - market double base_price=m_symbol.Ask(); price=m_symbol.NormalizePrice(base_price-m_price_level*PriceLevelUnit()); //--- sl price - Sup1.0 level sl=this.MajorSupport(0); if(sl==DBL_MAX) return false; //---sl が設定されている場合 sl=m_symbol.NormalizePrice(sl); //--- tp price - Res0.5 level tp=this.MinorResistance(0); if(tp==DBL_MAX) return false; //---tpが設定されている場合 tp=m_symbol.NormalizePrice(tp); expiration+=m_expiration*PeriodSeconds(m_period); //---価格が設定されている場合 params_set=true; //--- pattern complete m_pattern_0_done=true; } //--- return params_set; } //+------------------------------------------------------------------+
3番目の結果は次のとおりです。
図6. 2013-2016 の3番目の基本戦略の eurusd: 結果
この図は、最初のバージョンと多少似ています。 一見すると、Grailが発見されているようです。 しかし、以下の落とし穴があります。
3. ロバストネス
図6をよく見ると、バランス曲線が不均一になっていることがわかります。 バランスが着実に利益を積み上げていくセグメントがありました。 また、ドローダウンのセグメントだけでなく、バランス曲線が右に移動している箇所もあります。
一般的には、戦略がロバストネスを欠いているなどと言うことができます。 これを改善することは可能でしょうか? やってみましょう。
3.1 トレンドインジケーター
私見では、上記のトレードルールは、相場での方向性の動きがある場合よくなります。 この戦略は、EURUSDが少しずつ下がっていたとき2014-初期2015で 最高の結果を示しました。
レンジを避けるために、トレードを許可するフィルタが必要です。 トレンドを決定する材料がたくさんあります。 mql5.com の [Articles] セクションでも見つけることができます。 個人的には、"MQL5 のトレンドを見つけるメソッド" が一番好きです。 トレンドを検索する際に便利で、より重要で、普遍的メソッドです。
私は同じようなインジケーターMaTrendCatcherを開発しました。 短期MAと長期MAを比較します。 両者の差がプラスになれば、そのトレンドは強気です。 インジケーターヒストグラムバーは1に等しくなります。 その差がネガティブなら、そのトレンドは弱気です。 足はマイナス 1 (図7) と等しくなります。
図7. MaTrendCatcher トレンドインジケーター
その上、移動平均間の差が前の足に対して相対的に増加すれば、足は緑になります。逆の場合、赤くなります。
インジケーターに追加された別の関数: MAs の違いが軽微である場合、バーは表示されません。 バーが非表示になっている差の値は、「カットオフ、pp」のインジケーターパラメータによって異なります (図8)。
図8. MaTrendCatcher トレンドインジケーターの小さな差の非表示
では、 MaTrendCatcherインジケーターを使用して、フィルター処理を行いましょう。
このインジケーターを適用するには、プロジェクトファイルのコードに変更を施す必要があります。 EA の最後のバージョンは、Model フォルダに保存されることに注意してください。
この戦略に、"ウエイト" 方向の計算値を取得する必要があります。 したがって、基本シグナルクラスからカスタムクラスが必要です。
そして、新しいモデルは、反転レベルの更新されたシグナルクラス-モデル 1 "トレンド-レンジ-カウンタートレンド" に表示されます。
本質的に、モデル0を補完します。 そのため、サブパターンと呼ぶことができるでしょう。 このコードは少し後に扱います。
さて、買い条件の検証は次のようになります。
//+------------------------------------------------------------------+ //| Check the buy condition | //+------------------------------------------------------------------+ int CSignalPivots::LongCondition(void) { int result=0; //---パターン0が考慮されていない場合 if(IS_PATTERN_USAGE(0)) //--- パターン0が考慮されていない場合 if(!m_pattern_0_done) { m_is_signal=false; //--- if a day has opened below the pivot if(m_daily_open_pr<m_pivot_val) { //---過去のバーの最大価格 double last_high=m_high.GetData(1); //--- if the price is received if(last_high>WRONG_VALUE && last_high<DBL_MAX) //--- if there was a touch from above (considering the tolerance) if(last_high>=(m_pivot_val-m_pnt_near)) { result=m_pattern_0; m_is_signal=true; //--- to the Journal this.Print(last_high,ORDER_TYPE_BUY); } } //---パターン1が考慮されている場合 if(IS_PATTERN_USAGE(1)) { //---過去のバーに強気のトレンドがあった場合 if(m_trend_val>0. && m_trend_val!=EMPTY_VALUE) { //---加速度があれば if(m_trend_color==0. && m_trend_color!=EMPTY_VALUE) result+=(m_pattern_1+m_speedup_allowance); //---加速度がない場合 else result+=(m_pattern_1-m_speedup_allowance); } } } //--- return result; }
緑ブロックは、サブパターンが適用される場所を示します。
計算の背後にある考え方は次のとおりです。サブパターンを考慮せずに相場参入を行うと、シグナルの結果はパターン0の重みと等しくなります。 サブパターンが考慮されている場合は、次のオプションを使用できます。
- 加速度 (トレンドおよび加速のボーナス) のトレンドの方向にエントリー;
- 加速度なしでトレンドの方向にエントリー (トレンドボーナスと加速ペナルティ);
- 加速度 (反対と加速ペナルティ)のトレンドに対してエントリー;
- 加速度 (反対ペナルティと加速度ボーナス) とのトレンドに対してエントリー。
このアプローチは弱いシグナルへの反応を避けます。 シグナルウェイトがしきい値を克服すると、トレードボリュームサイズに影響します。 ピボットクラスは、 CPivotsExpert:: LotCoefficient ()メソッドを備えています。
//+------------------------------------------------------------------+ //Lot ratio | //+------------------------------------------------------------------+ double CPivotsExpert::LotCoefficient(void) { double lot_coeff=1.; //---一般的なシグナル CExpertUserSignal *ptr_signal=this.Signal(); if(CheckPointer(ptr_signal)==POINTER_DYNAMIC) { double dir_val=ptr_signal.GetDirection(); lot_coeff=NormalizeDouble(MathAbs(dir_val/100.),2); } //--- return lot_coeff; } //+------------------------------------------------------------------+
例えば、シグナルが120の場合、最初のボリュームは1.2で調整され、70の場合は、0.7 によって調整されます。
この比率を適用するには、OpenLong () および OpenShort () メソッドを再定義する必要があります。 たとえば、buy メソッドは次のように表されます。
//+------------------------------------------------------------------+ //| Long position open or limit/stop order set | //+------------------------------------------------------------------+ bool CPivotsExpert::OpenLong(double price,double sl,double tp) { if(price==EMPTY_VALUE) return(false); //--- get lot for open double lot_coeff=this.LotCoefficient(); double lot=LotOpenLong(price,sl); lot=this.NormalLot(lot_coeff*lot); //---ロットをチェック lot=LotCheck(lot,price,ORDER_TYPE_BUY); if(lot==0.0) return(false); //--- return(m_trade.Buy(lot,price,sl,tp)); } //+------------------------------------------------------------------+
ロットサイズの動的な形成の考えは非常に簡単です。: 強いシグナルは、それだけリスクが大きい。
3.2 レンジサイズ
反転レベル (ピボット) は、相場のボラティリティを示します。 このような日にトレードを避けるために、"Width limit, pp" パラメータが導入されています。 制限を超えない場合は、パターン 0 (サブパターンとともに) と見なされます。 この制限は、Direction()メソッド本体で検証されます。 コードの一部です:
//---制限が設定されている場合 if(m_wid_limit>0.) { //---推定上限 double norm_upper_limit=m_symbol.NormalizePrice(m_wid_limit+m_pivot_val); //---実際の上限 double res1_val=this.MajorResistance(0); if(res1_val>WRONG_VALUE && res1_val<DBL_MAX) { //---制限を超えていない場合 if(res1_val<norm_upper_limit) { //---パターン0が完了 m_pattern_0_done=true; //--- to the Journal Print("\n---== Upper limit not exceeded ==---"); PrintFormat("Estimated: %0."+IntegerToString(m_symbol.Digits())+"f",norm_upper_limit); PrintFormat("Actual: %0."+IntegerToString(m_symbol.Digits())+"f",res1_val); //--- return 0.; } } //---推定下限 double norm_lower_limit=m_symbol.NormalizePrice(m_pivot_val-m_wid_limit); //---実際の下限 double sup1_val=this.MajorSupport(0); if(sup1_val>WRONG_VALUE && sup1_val<DBL_MAX) { //---制限を超えていない場合 if(norm_lower_limit<sup1_val) { //---パターン0が完了 m_pattern_0_done=true; //--- to the Journal Print("\n---== Lower limit not exceeded ==---"); PrintFormat("Estimated: %0."+IntegerToString(m_symbol.Digits())+"f",norm_lower_limit); PrintFormat("Actual: %0."+IntegerToString(m_symbol.Digits())+"f",sup1_val); //--- return 0.; } } }
シグナルが範囲幅の検証にクリアしない場合、ジャーナルに次のエントリが表示されます。
2015.08.19 00:01:00 ---== Upper limit not exceeded ==--- 2015.08.19 00:01:00 Estimated: 1.10745 2015.08.19 00:01:00 Actual: 1.10719この場合、シグナルは有効まで26ポイントを欠いています。
最適化モードでテスターを起動します。 次の最適化パラメータを使用しましょう:
- "Width limit, pp";
- "Tolerance, pp";
- "Fast МА";
- "Slow МА";
- "カットオフ、pp"。
収益性の面で最も成功した結果は、次のようになります。
図9. eurusd: 2013-2016 のフィルタを使用した戦略の結果
予想通り、一部のシグナルが選別されました。 バランスカーブがスムーズになりました。
しかし、改善点もあります。 チャート上で見たように、この戦略は、狭い範囲でバランス曲線が変動するセグメントを生成します。 最適化結果は、 UREURUSD_model.xmlファイルにあります。
他のシンボルの結果を見てみましょう。
usdjpy に最適な動作を図10に表示します。
図10. フィルタを使用した 戦略の 結果:usdjpy
さて、ゴールドを見てみましょう。 最良の結果を図11に示します。
図11. xauusd: 2013-2016 のフィルタを使用した戦略結果
この期間中、貴金属は狭いレンジでトレードされたので、戦略は肯定的な結果をもたらしませんでした。
GBP については、図12にベストランが表示されています。
図12. gbpusd: 2013-2016 フィルタを使用した戦略結果
GBP はトレンドでよいトレードになりました。 しかし、2015で、最終的な結果を台無しにしました。
一般的に、この戦略は、トレンドの中で最高になります。
結論
トレーディング戦略の開発は、いくつかのステージで構成されます。 初期段階では、トレーディングアイデアを策定します。 ほとんどの場合、コードの形式で形式化してからテスターでチェックする必要があるような仮説です。 何度も調整し、テストプロセス中にこのような仮説を絞り込む必要があります。 これらは開発者の標準的なタスクです。 ここでは、同じアプローチを使用して、ピボット戦略をコーディングします。 私の考えでは、 OOPはタスクを大幅に簡略化します。
最適化モードのすべてのテストは、 MQL5 クラウドネットワークで実施されました。 クラウド技術は、迅速かつ非コストのメソッドで戦略の効率性を評価することができました。
ファイルの場所
戦略ファイルを1つのピボットフォルダに配置すると便利です。 インジケーターファイル (URPivots.ex5 および URMaTrendCatcher.ex5) を、コンパイル後に%MQL5\Indicators インジケーターフォルダに移動します。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2853





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