English Русский 中文 Español Deutsch Português
80-20 トレード戦略

80-20 トレード戦略

MetaTrader 5トレーディングシステム | 27 12月 2016, 08:43
2 327 0
Alexander Puzanov
Alexander Puzanov

イントロダクション

80-20' は、リンダラッシュクとローレンス · コナーズによる著書 Street Smarts: 高確率短期トレーディング戦略に記載されているトレード戦略 (TS) の 1 つです。以前の記事で説明した方法と同様です。また、偽のブレイク アウトおよびロールバックから利益を得ることに焦点を当てています。しかし、今回は、前日を含む 短い間隔で価格の動きを分析します。トレードシステムの有効期間は比較的短いです。

最初の目標は、80-20 トレーディング戦略シグナルモジュールを mql5を使用して説明します。その後は、以前の記事で開発した基本的なトレードロボットをわずかに編集したバージョンにこのモジュールを接続します。それ以外にも、 マニュアルでのインジケーターの開発に同じモジュールを使用します。

少し高度なプログラマを目指している方に最適です。したがって、主な目的以外にも手続き型プログラミングからオブジェクト指向に移行するために設計されています。コードにはフィーチャ クラスがありません。代わりに、完全にマスターし易い構造を実装します。

記事の別の目的として、ラシュキとコナーズ、まだ有効であるを確認するためのツールを開発しました。最新の履歴データに基づいたEA のテストは、この記事の最後に掲載されています。


80-20' トレードシステム

著者の名は、ジョージ テイラーで、テイラートレード手法を執筆しました。これは、スティーブ ・ ムーアと同様、コンピューター解析による分析を利用しています。トレード戦略の本質は次のようにシンプルに説明することができます: 前日の始値終値が反対の日の範囲内にある場合、今日に向けて反転の確率は非常に高い。前日の始値と終値が、レンジの境界に近い。逆転が、当日 (前日のロウソク足を閉じる前ではない) を始まります。買いのルールは次のとおりです。

1. 上部20% で日の始値が始まり、下20%で終値を迎えたか確認します。

2. 昨日の安値を今日の安値がブレイクするかどうか少なくとも 5ティック待ちます。

3. 昨日の下端に買いの予約オーダーを配置します。

4. 予約注文がトリガーしたら、最初のストップロスをその日の安値に設定します。

5. 得られた利益を保護するためにトレーリング ストップを使用します。

売りエントリールールは類似していますが、昨日のバーはブル型である必要があります。買いオーダーの場合は足の上部に位置し、ストップロスは今日の高値に配置します。

もう一つの重要な点は、閉じた日足のサイズです。リンダラッシュクによると、大スケールとして十分と言えるのは、平均サイズ以上のはずです。しかし、彼女は毎日の平均範囲を計算するとき、日数を考慮する必要がありますが、その日数を指定していません。

TS が日中のトレード専用に設計されていることを念頭に置いて、M15 チャートを使用します。

シグナルブロックと戦略に従ったインジケーターは次のとおりです。また、インジケーター操作結果とスクリーン ショットを見ることができます。レベルのトレードパターンにリンクされているシステム ルールに対応するパターンを示しています。

M5 期間:

80-20 TS パターン

このパターン分析は、買いの保留オーダーを配置することになります。適切なトレードレベルが M1 の期間に見てとれます。

80-20 TS パターン: レベルのトレード

M5 期間の反対トレード方向での同様のパターン:

80-20 TS パターン

トレードレベル (M1 期間):

80-20 TS パターン: レベルのトレード

 

シグナルモジュール

カスタムの TS に追加する新しいオプションを説明するために、テイクプロフィト レベルを追加してみましょう。ポジションを閉じる際、元のバージョンは、トレーリング ストップを使用するだけです。カスタムの最小のブレイク アウトのレベル (TS_8020_Extremum_Break) にしたがって利益を得ましょう。TS_8020_Take_Profit_Ratioのカスタム比率で乗算されます。

Fe_Get_Entry_Signalシグナルモジュールの主な関数の次の要素が必要になります: 現在のシグナルの状態が (ストップロスとテイクプロフィト) 計算されるエントリーレベルと決済レベル、昨日の範囲を指定します。すべてのレベルは、シグナルの戻りステータスを使用して、関数に渡される変数へのリンクを介して受け取られます。

enum ENUM_ENTRY_SIGNAL {  //エントリー シグナルのリスト
  ENTRY_BUY,              //買いシグナル
  ENTRY_SELL,             //売りシグナル
  ENTRY_NONE,             //シグナルなし
  ENTRY_UNKNOWN           //未定義の状態
};

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal( //D1 2つのロウソク足のパターン解析
  datetime  t_Time,          //現在の時刻
  double&    d_Entry_Level,  //エントリー レベル (変数にリンク)
  double&    d_SL,           //StopLoss レベル (変数にリンク)
  double&    d_TP,           //有効期限のレベル (変数にリンク)
  double&    d_Range_High,   //パターンの 1st の高値 (変数にリンク)
  double&    d_Range_Low     //パターンの 1 st の安値 (変数にリンク)
) {}

シグナルを検出すると、D1 の期間の最後の 2 つのバーを分析する必要があります。最初から始めましょう。TS の条件を満たしていない場合、2番目のバーをチェックする必要はありません。2つの条件があります。

1. 足のサイズが ( TS_8020_D1_Average_Periodカスタム設定により設定) 最後の XX 日間の平均値を超えないサイズ (高低差)

2. 足の範囲の反対側の 20%に始値と終値がある

条件を満たしている場合、高値低値を使用するため保存する必要があります。最初のバーは、一日の内でパラメータを変更せず、関数を呼び出すたびにチェックするポイントはありません。静的変数に格納してみましょう。

//設定
input uint  TS_8020_D1_Average_Period = 20;  //80-20: 毎日の平均範囲を計算するための日数
input uint  TS_8020_Extremum_Break = 50;     //80-20: 最小のブレイク アウト ポイント単位で昨日の極値


static ENUM_ENTRY_SIGNAL se_Possible_Signal = ENTRY_UNKNOWN; //パターンの最初のシグナルの方向バー
static double
  //ティック刻みの計算されたレベルを格納するための変数
  sd_Entry_Level = 0,
  sd_SL = 0, sd_TP = 0,
  sd_Range_High = 0, sd_Range_Low = 0
;


//チェック パターンの D1 のバー最初:
if(se_Possible_Signal == ENTRY_UNKNOWN) { //まだ遂行されなかった
  st_Last_D1_Bar = t_Curr_D1_Bar; //1 st バーはこの日変更されません
  
  //毎日の範囲の平均
  double d_Average_Bar_Range = fd_Average_Bar_Range(TS_8020_D1_Average_Period, PERIOD_D1, t_Time);
  
  if(ma_Rates[0].high — ma_Rates[0].low <= d_Average_Bar_Range) {
    //1 st バーが十分な大きさ
    se_Possible_Signal = ENTRY_NONE; //今日はシグナルなし
    return(se_Possible_Signal);
  }
  
  double d_20_Percents = 0.2 * (ma_Rates[0].high — ma_Rates[0].low); //昨日の範囲の 20%
  if((
      //弱気バー:
      ma_Rates[0].open > ma_Rates[0].high — d_20_Percents //バーの上部の 20%
      &&
      ma_Rates[0].close < ma_Rates[0].low + d_20_Percents //下の 20% で終了
    ) || (
      //強気:
      ma_Rates[0].close > ma_Rates[0].high — d_20_Percents //バー上部の 20% で決済
      &&
      ma_Rates[0].open < ma_Rates[0].low + d_20_Percents //始値が下の 20%
  )) {
    //条件に対応する 1 st バー
    //今日のトレード方向パターンの 1 st バーを定義:
    se_Possible_Signal = ma_Rates[0].open > ma_Rates[0].close ?ENTRY_BUY: ENTRY_SELL;
    //エントリー レベル:
    sd_Entry_Level = d_Entry_Level = se_Possible_Signal == ENTRY_BUY ?ma_Rates[0].low : ma_Rates[0].high;
    //バーの範囲のボーダー パターンの 1 st:
    sd_Range_High = d_Range_High = ma_Rates[0].high;
    sd_Range_Low = d_Range_Low = ma_Rates[0].low;
  } else {
    //1 st バーの始値/終値が条件に一致しない
    se_Possible_Signal = ENTRY_NONE; //今日はシグナルなし
    return(se_Possible_Signal);
  }
}

平均バーを定義するための関数の一覧は、指定した time 関数で指定された期間の初めにバーの指定した数の内です。

double fd_Average_Bar_Range(    //平均を計算するバーのサイズ
  int i_Bars_Limit,             //考慮するバーの数
  ENUM_TIMEFRAMES e_TF = PERIOD_CURRENT,  //時間枠
  datetime t_Time = WRONG_VALUE  //いつ計算を開始するか
) {
  double d_Average_Range = 0; //変数の値を合計
  if(i_Bars_Limit < 1) return(d_Average_Range);
  
  MqlRates ma_Rates[]; //情報配列
  
  //指定されたヒストリ間隔からバー情報を得る:
  if(t_Time == WRONG_VALUE) t_Time = TimeCurrent();
  int i_Price_Bars = CopyRates(_Symbol, e_TF, t_Time, i_Bars_Limit, ma_Rates);
  
  if(i_Price_Bars == WRONG_VALUE) { //CopyRates 関数エラーを処理
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyRates: error #%u", __FUNCTION__, _LastError);
    return(d_Average_Range);
  }
  
  if(i_Price_Bars < i_Bars_Limit) { //CopyRates 関数が、必要なデータ量を取得していない
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyRates: copied %u bars of %u", __FUNCTION__, i_Price_Bars, i_Bars_Limit);
  }
  
  //範囲の合計:
  int i_Bar = i_Price_Bars;
  while(i_Bar-- > 0)
    d_Average_Range += ma_Rates[i_Bar].high — ma_Rates[i_Bar].low;
  
  //平均値:
  return(d_Average_Range / double(i_Price_Bars));
}

パターンの 2 つ目 (現在) のバーの条件があります。-1 つは (TS_8020_Extremum_Break) の設定で指定されているべきではない昨日の範囲の枠線のブレイク アウト。このレベルに達すると、すぐに保留中のオーダーのシグナルが表示されます。

//D1 のパターンの 2 nd (現在) バーをチェック:
if(se_Possible_Signal == ENTRY_BUY) {
  sd_SL = d_SL = ma_Rates[1].low; //StopLoss-今日の高値
  if(TS_8020_Take_Profit_Ratio > 0) sd_TP = d_TP = d_Entry_Level + _Point * TS_8020_Extremum_Break * TS_8020_Take_Profit_Ratio; //有効期限
  return(
    //下方ブレイク アウトは自明でしょうか?
    ma_Rates[1].close < ma_Rates[0].low — _Point * TS_8020_Extremum_Break ?
ENTRY_BUY: ENTRY_NONE
  );
}

if(se_Possible_Signal == ENTRY_SELL) {
  sd_SL = d_SL = ma_Rates[1].high; //StopLoss-今日の安値
  if(TS_8020_Take_Profit_Ratio > 0) sd_TP = d_TP = d_Entry_Level — _Point * TS_8020_Extremum_Break * TS_8020_Take_Profit_Ratio; //有効期限
  return(
    //上方ブレイク アウトは明らかでしょうか?
    ma_Rates[1].close > ma_Rates[0].high + _Point * TS_8020_Extremum_Break ?
    ENTRY_SELL : ENTRY_NONE
  );
}

(Fe_Get_Entry_Signalfd_Average_Bar_Range) 上記 2 つの関数と mqh ライブラリファイルのシグナルを受信に関連するカスタム設定を保存します。完全なリストは、以下に添付されます。Signal_80 20.mqh ファイルの名前を指定してターミナル データ フォルダー (MQL5\Include\Expert\Signal) の適切なディレクトリに配置します。 


マニュアルでのトレードインジケーター

EA と同じようにインジケーターは、上記シグナルモジュールを使用します。保留中のオーダーのポジションシグナルを受信して、トレーダーに通知し、計算されるレベルを提供し、ストップロスとテイクプロフィト レベルをオーダーします。ユーザーは、通知メソッドを選択できます-標準ポップアップウィンドウ、電子メール通知またはプッシュ通知。このような任意の組み合わせを一度に選択することが可能です。

別のインジケーターの目的は、80-20 TS によるトレード履歴レイアウトです。インジケーターは、システムの基準に対応する日足を強調表示し、計算されたトレードレベルをプロットします。状況がどのように時間をかけていったか、ラインが表示されます。わかりやすくするため、次をやってみましょう: 価格がシグナルラインにタッチしたとき、予約注文ラインに置き換える。保留オーダーをアクティブにしたとき、そのラインはストップロスとテイクプロフィトのラインに置き換えられます。このラインは、価格がその1つに触れたときに中断されます。(オーダーが終了) このトレードシステムのルールの効率性を評価し、改善できる部分を定義しやすくなります。

バッファと表示パラメータを宣言することをから始めましょう。まず、 は垂直領域 (DRAW_FILLING) のエントリーと 2 つのバッファを宣言する必要があります。最初の 1 つは、上から分離して 範囲の 20%を強調し、もう一つは本格的な日足、前日の範囲バーを強調することです。その後、複数の色のシグナル線、保留オーダー線 (DRAW_COLOR_LINE) は 2 つのバッファを宣言します。その色は、トレードの方向に依存します。(DRAW_LINE) 残りの色 (プロフトを取るとストップ損失) があり、ターミナルに割り当てられたのと同じ標準的な色を使用します。シンプルなラインを除いて、すべての選択した表示タイプは、2 つのバッファを必要とします。従ってコードは次のようになります。

#propertyindicator_chart_window
#propertyindicator_buffers10
#propertyindicator_plots 6

#property indicator_label1  "1 st bar of the pattern"
#property indicator_type1 DRAW_FILLING
#property indicator_color1 clrDeepPinkclrDodgerBlue
#property indicator_width1 1

#property indicator_label2  "1 st bar of the pattern"
#property indicator_type2 DRAW_FILLING
#property indicator_color2 clrDeepPinkclrDodgerBlue
#property indicator_width2 1

#property indicator_label3  "Signal level"
#property indicator_type3 DRAW_COLOR_LINE
#property indicator_style3 STYLE_SOLID
#property indicator_color3 clrDeepPinkclrDodgerBlue
#property indicator_width3 2

#property indicator_label4  "Entry level"
#property indicator_type4 DRAW_COLOR_LINE
#property indicator_style4 STYLE_DASHDOT
#property indicator_color4 clrDeepPinkclrDodgerBlue
#property indicator_width4 2

#property indicator_label5  "Stop Loss"
#property indicator_type5 DRAW_LINE
#property indicator_style5 STYLE_DASHDOTDOT
#property indicator_color5 clrCrimson
#property indicator_width5 1

#property indicator_label6  "Take Profit"
#property indicator_type6 DRAW_LINE
#property indicator_style6 STYLE_DASHDOTDOT
#property indicator_color6 clrLime
#property indicator_width6 1

日のパターンの最初のバーを無効にする関数を持つトレーダーシグナルの通知オプションを選択し、履歴レイアウトを制限しましょう。シグナルモジュールからすべてのトレードシステムの設定も含まれます。これを行うには、予めいくつか使用される、インジケーターで不要の場合でも、モジュールで使用される変数を列挙する必要があります。

#include <Expert\Signal\Signal_80-20.mqh> //80-20' TS シグナルモジュール

input bool    Show_Outer = true;      //パターン1st バー: フルレンジを表示?
input bool    Show_Inner = true;      //パターン1stバー: 内部領域を表示?
input bool    Alert_Popup = true;     //アラート: ポップアップ ウィンドウを表示?
input bool    Alert_Email = false;    //アラート: メールを送信?
input string  Alert_Email_Subj = "";  //アラート: メールの件名
input bool    Alert_Push = true;      //アラート: プッシュ通知を送る?

input uint  Bars_Limit = 2000;  //(現在の TF バー) で履歴レイアウトの深さ



ENUM_LOG_LEVEL  Log_Level = LOG_LEVEL_NONE;  //ログ モード
double
  buff_1st_Bar_Outer[], buff_1st_Bar_Outer_Zero[], //パターン1stバーの完全な範囲のプロットにバッファ
  buff_1st_Bar_Inner[], buff_1st_Bar_Inner_Zero[], //パターン1stバー内部の 60% のプロットのバッファ
  buff_Signal[], buff_Signal_Color[], //シグナル線バッファ
  buff_Entry[], buff_Entry_Color[], //保留中のオーダーのラインのバッファ
  buff_SL[], buff_TP[], //StopLoss とTakeProfitラインのバッファ
  gd_Extremum_Break = 0 //通貨価格の TS_8020_Extremum_Break
;
int
  gi_D1_Average_Period = 1, //TS_8020_D1_Average_Period の値を修正
  gi_Min_Bars = WRONG_VALUE //足の数を必要最低限再計算
;



int OnInit() {
  //エントリーした TS_8020_D1_Average_Period パラメータを確認してください:
  gi_D1_Average_Period = int(fmin(1, TS_8020_D1_Average_Period));
  //シンボルの価格からポイントへの変換:
  gd_Extremum_Break = TS_8020_Extremum_Break * _Point;
  //必要最低限再計算な足の数 = 現在のTFの足の数
  gi_Min_Bars = int(86400 / PeriodSeconds());
  
  //インジケーター バッファの対象:
  
  //1stバーの完全な四角形
SetIndexBuffer(0buff_1st_Bar_Outer、 INDICATOR_DATA)
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0);
SetIndexBuffer(1buff_1st_Bar_Outer_Zero、 INDICATOR_DATA)
  
  //1 st バーの内側の領域の長方形
SetIndexBuffer(2, buff_1st_Bar_Inner, INDICATOR_DATA);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0);
SetIndexBuffer(3, buff_1st_Bar_Inner_Zero, INDICATOR_DATA);
  
  //シグナル線
SetIndexBuffer(4, buff_Signal, INDICATOR_DATA);
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, 0);
SetIndexBuffer(5, buff_Signal_Color, INDICATOR_COLOR_INDEX);
  
  //保留中のオーダーの配置線
SetIndexBuffer(6buff_Entry、 INDICATOR_DATA)
PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, 0);
SetIndexBuffer(7, buff_Entry_Color, INDICATOR_COLOR_INDEX);
  
  //SL ライン
SetIndexBuffer(8buff_SL、 INDICATOR_DATA)
PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, 0);
  
  //TP ライン
SetIndexBuffer(9, buff_TP, INDICATOR_DATA);
PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, 0);
  
IndicatorSetInteger(INDICATOR_DIGITS_Digits);
IndicatorSetString(INDICATOR_SHORTNAME, "80 20 TS");
  
  return(INIT_SUCCEEDED);
}

組み込みの OnCalculate 関数のメイン プログラムのコードを配置し、シグナルモジュールから関数を使用してシグナルを探して、過去から現在のタイム フレームのバーを反復処理するループを手配します。初期値を使用して必要な変数を初期化します。ユーザー定義の履歴の制限 (Bars_Limit) を考慮した最初の計算の最も古いループ バーを定義してみましょう。 (最後のバーではなく) 2つのバーからの再計算パターンは、現在の期間に関係なく D1 グラフに属します。

その上、いわゆるファントムを保護する必要があります: 再初期化中にインジケーター バッファは行っていないので、切り替えの時間枠やシンボル関連の塗りつぶされた領域が画面に残りません。バッファのクリアは、インジケーター初期化後最初の OnCalculate 関数呼び出しにバインドする必要があります。ただし、prev_calculated変数を定義するための呼び出しは、最初の 1 つ"チェックサムを変更する場合も関数の最初の呼び出し時にだけではなくゼロを含めることができる場合です。prev_calculated 変数を設定することによって影響を受けない構造を作成することによって、この問題を解決しましょう。この構造は、インジケーターで頻繁に使用されるデータ処理です。

-OnCalculate 関数の最初に起動。

-チェックサムを変更するときにゼロに設定されていない集計バーのカウンター

-チェックサムを変更するフラグ

-新しいバーの先頭のフラグ

-現在の開始時刻バー。

すべてのデータを組み合わせた構造は、グローバル レベルで宣言することです。収集したり、任意の組み込みまたはカスタム関数へのデータを表示することができるはずです。名前はブラウニーとしましょう。インジケータ コードの末尾に配置することができます。Go_Brownieという名前の単一のグローバルな型構造体オブジェクトがそこにも宣言されます。

struct BROWNIE {                //ブラウニー: 構造を格納して、グローバル レベルでデータを処理
  datetime  t_Last_Bar_Time;    //最後の処理されたバーの時間
  int        i_Prew_Calculated; //計算されたバーの数
  bool      b_First_Run;        //最初の起動フラグ
  bool      b_History_Updated;  //ヒストリー更新フラグ
  bool      b_Is_New_Bar;       //新しいフラグを開くバー
  
  BROWNIE() { //コンス トラクター
    //デフォルト値:
    t_Last_Bar_Time = 0;
    i_Prew_Calculated = WRONG_VALUE;
    b_First_Run = b_Is_New_Bar = true;
    b_History_Updated = false;
  }
  
  void f_Reset(bool b_Reset_First_Run = true) { //変数をゼロに設定
    //デフォルト値:
    t_Last_Bar_Time = 0;
    i_Prew_Calculated = WRONG_VALUE;
    if(b_Reset_First_Run) b_First_Run = true; //アクセス許可がある場合はゼロに設定
    b_Is_New_Bar = true;
    b_History_Updated = false;
  }
  
  void f_Update(int i_New_Prew_Calculated = WRONG_VALUE) { //変数を更新
    //OnCalculate 組み込み関数の最初の呼び出しのフラグ
    if(b_First_Run && i_Prew_Calculated > 0) b_First_Run = false;
    
    //新しいバー?
    datetime t_This_Bar_Time = TimeCurrent() - TimeCurrent() % PeriodSeconds();
    b_Is_New_Bar = t_Last_Bar_Time == t_This_Bar_Time;
    
    //現在の更新時間バー?
    if(b_Is_New_Bar) t_Last_Bar_Time = t_This_Bar_Time;
    
    if(i_New_Prew_Calculated > -1) {
      //ヒストリーに変更はありますか?
      b_History_Updated = i_New_Prew_Calculated == 0 && i_Prew_Calculated > WRONG_VALUE;
      
      //OnCalculate 1 st 呼び出しの場合 prew_calculated を使用
      if(i_Prew_Calculated == WRONG_VALUE) i_Prew_Calculated = i_New_Prew_Calculated;
      //更新履歴がない場合
      else if(i_New_Prew_Calculated > 0) i_Prew_Calculated = i_New_Prew_Calculated;
    }
  }
};
BROWNIE go_Brownie;

インジケーター初期化イベントのブラウニーをお知らせしましょう。

void OnDeinit(const int reason) {
  go_Brownie.f_Reset(); //ブラウニーを知らせる
}

カスタム関数またはクラスは価格、ボリュームまたは現在のバーの拡散値 (始値、高値、安値、終値、tick_volume、ボリューム)が 必要である場合に、ブラウニーが格納するデータの量は拡張できます。OnCalculate 関数から既製のデータを使用してコピー機能 (コピー、CopyHigh 等または CopyRates) 時系列を用いたのではなく、ブラウニーを介してそれらを渡すと便利です。これは CPU リソースを節約し、言語機能のエラーの処理をする必要があります。

主要インジケーター関数に戻りましょう。変数を宣言して、 go_Brownie構造体を使用して配列を準備するのは、以下の通りです。

go_Brownie.f_Update(prev_calculated); //ブラウニーにデータを供給

int
  i_Period_Bar = 0, //補助カウンター
  i_Current_TF_Bar = rates_total - int(Bars_Limit) //現在の TF のインデックス バーでループの開始
;
static datetime st_Last_D1_Bar = 0; //D1 バー (パターンの 2 nd バー) の最後の処理されたバーの時間
static int si_1st_Bar_of_Day = 0; //現在の日のインデックスの最初のバー

if(go_Brownie.b_First_Run) { // 1st起動の場合
  //再初期化中にバッファをクリア:
ArrayInitialize(buff_1st_Bar_Inner, 0);ArrayInitialize(buff_1st_Bar_Inner_Zero, 0);
ArrayInitialize(buff_1st_Bar_Outer, 0);ArrayInitialize(buff_1st_Bar_Outer_Zero, 0);
ArrayInitialize(buff_Entry, 0);ArrayInitialize(buff_Entry_Color, 0);
ArrayInitialize(buff_Signal, 0);ArrayInitialize(buff_Signal_Color, 0);
ArrayInitialize(buff_TP, 0);
ArrayInitialize(buff_SL, 0);
  st_Last_D1_Bar = 0;
  si_1st_Bar_of_Day = 0;
} else { //1st起動ではありません
  datetime t_Time = TimeCurrent();
  //前の日から - 最小値の再計算:
  i_Current_TF_Bar = rates_total - Bars(_Symbol, PERIOD_CURRENT, t_Time - t_Time % 86400, t_Time) - 1;
}
ENUM_ENTRY_SIGNAL e_Signal = ENTRY_UNKNOWN; //シグナル
double
  d_SL = WRONG_VALUE, //SL レベル
  d_TP = WRONG_VALUE, //TP レベル
  d_Entry_Level = WRONG_VALUE, //エントリー レベル
  d_Range_High = WRONG_VALUE, d_Range_Low = WRONG_VALUE //ボーダー パターンの 1 st バーの範囲
;
datetime
  t_Curr_D1_Bar = 0, //現在のD1 バー時間 (パターンの 2 nd バー)
  t_D1_Bar_To_Fill = 0 //D1バー時間 (パターンの 1 st バー) いっぱい
;

//初期の再計算バーのインデックスは許容範囲内かどうかを確認:
i_Current_TF_Bar = int(fmax(0, fmin(i_Current_TF_Bar, rates_total - gi_Min_Bars)));

while(++i_Current_TF_Bar < rates_total && !IsStopped()) { //現在の TF バー反復
  //メイン プログラム ループがここに位置する、
}

現在の期間バーを反復処理するときに信号の存在を確認します。

e_Signal = fe_Get_Entry_Signal(Time[i_Current_TF_Bar], d_Entry_Level, d_SL, d_TP, d_Range_High, d_Range_Low);
if(e_Signal > 1) continue; //日中はバーが属するシグナルなし

シグナルが新しい一日の最初のバー上にある場合、日足の範囲がいっぱいである必要があります。Datetime 型のt_D1_Bar_To_Fill変数の値は、フラグとして使用されます。WRONG_VALUE に等しいと、このバーは必要ありません。シグナル線は、同じの最初のバーに開始すべきで、レイアウトの前の日の最後のバーに広げてみましょう。強気と弱気のバーのラインと塗りつぶしの色と同様に、シグナル線の計算が異なるため、2 つの同じようなブロックにします。

t_Curr_D1_Bar = Time[i_Current_TF_Bar] — Time[i_Current_TF_Bar] % 86400; //バーが属する日の開始
if(st_Last_D1_Bar < t_Curr_D1_Bar) { //新しい日のバー
  t_D1_Bar_To_Fill = Time[i_Current_TF_Bar — 1] — Time[i_Current_TF_Bar — 1] % 86400;
  si_1st_Bar_of_Day = i_Current_TF_Bar;
}
else t_D1_Bar_To_Fill = WRONG_VALUE; //前日のバー、必要な新しいものがない
st_Last_D1_Bar = t_Curr_D1_Bar; //注意してください

if(t_D1_Bar_To_Fill != WRONG_VALUE) { //新しい D1 バー
  //前の日の D1 バー:
  i_Period_Bar = i_Current_TF_Bar;
  if(d_Entry_Level < d_Range_High) { //D1 弱気バー
    if(Show_Outer) while(--i_Period_Bar > 0) { //完全な範囲
      if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
      buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_Low;
      buff_1st_Bar_Outer[i_Period_Bar] = d_Range_High;
    }
    if(Show_Inner) { //内側のエリア
      i_Period_Bar = i_Current_TF_Bar;
一方(-i_Period_Bar > 0){
        if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
        buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_Low + 0.2 * (d_Range_High — d_Range_Low);
        buff_1st_Bar_Inner[i_Period_Bar] = d_Range_High — 0.2 * (d_Range_High — d_Range_Low);
      }
    }
    //前日の最後のバーからシグナル線の開始
    buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar — 1] = d_Range_Low — gd_Extremum_Break;
    buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar — 1] = 0;
  } else { //強気の D1 バー
    if(Show_Outer) while(--i_Period_Bar > 0) { //完全な範囲
      if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
      buff_1st_Bar_Outer_Zero[i_Period_Bar] = d_Range_High;
      buff_1st_Bar_Outer[i_Period_Bar] = d_Range_Low;
    }
    if(Show_Inner) { //内側のエリア
      i_Period_Bar = i_Current_TF_Bar;
一方(-i_Period_Bar > 0){
        if(Time[i_Period_Bar] < t_D1_Bar_To_Fill) break;
        buff_1st_Bar_Inner_Zero[i_Period_Bar] = d_Range_High — 0.2 * (d_Range_High — d_Range_Low);
        buff_1st_Bar_Inner[i_Period_Bar] = d_Range_Low + 0.2 * (d_Range_High — d_Range_Low);
      }
    }
    //前日の最後のバーからシグナル線の開始
    buff_Signal[i_Current_TF_Bar] = buff_Signal[i_Current_TF_Bar — 1] = d_Range_High + gd_Extremum_Break;
    buff_Signal_Color[i_Current_TF_Bar] = buff_Signal_Color[i_Current_TF_Bar — 1] = 1;
  }
} else continue;

残りのすべてのレイアウト線は現在のタイム フレームのバー反復処理ループの中にプロットします。すでに述べたように、シグナル線は価格が触れたバーで終了する必要があります。保留中のオーダーである必要があり、同じバーで価格との接触が発生します。ストップロスとテイクプロフィトのラインを同じバーで開始する必要があります。その価格に触れる1つでパターンのレイアウトを終えます。

//足によってシグナル線が交差する:
i_Period_Bar = i_Current_TF_Bar;
if(d_Entry_Level < d_Range_High) { //弱気の D1 バー
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Signal[i_Period_Bar] = d_Range_Low — gd_Extremum_Break;
    buff_Signal_Color[i_Period_Bar] = 0;
    if(d_Range_Low — gd_Extremum_Break >= Low[i_Period_Bar]) break;
  }
} else { //強気の D1 バー
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Signal[i_Period_Bar] = d_Range_High + gd_Extremum_Break;
    buff_Signal_Color[i_Period_Bar] = 1;
    if(d_Range_High + gd_Extremum_Break <= High[i_Period_Bar]) break;
  }
}

//エントリバーによって交差:
if(d_Entry_Level < d_Range_High) { //弱気の D1 バー
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Entry[i_Period_Bar] = d_Range_Low;
    buff_Entry_Color[i_Period_Bar] = 0;
    if(d_Range_Low <= High[i_Period_Bar]) {
      if(buff_Entry[i_Period_Bar — 1] == 0.) {
        //開始および単一の足で終了、過去1つの足で拡張
        buff_Entry[i_Period_Bar — 1] = d_Range_Low;
        buff_Entry_Color[i_Period_Bar — 1] = 0;
      }
      break;
    }
  }
} else { //強気の D1 バー
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_Entry[i_Period_Bar] = d_Range_High;
    buff_Entry_Color[i_Period_Bar] = 1;
    if(d_Range_High >= Low[i_Period_Bar]) {
      if(buff_Entry[i_Period_Bar — 1] == 0.) {
        //開始および単一の足で終了、過去1つの足で拡張
        buff_Entry[i_Period_Bar — 1] = d_Range_High;
        buff_Entry_Color[i_Period_Bar — 1] = 1;
      }
      break;
    }
  }
}

//TP と SL ラインまで 1 つはバーによって交差する:
if(d_Entry_Level < d_Range_High) { //弱気の D1 バー
  //SL は一日の初めから安値に等しい:
  d_SL = Low[ArrayMinimum(Low, si_1st_Bar_of_Day, i_Period_Bar — si_1st_Bar_of_Day)];
  
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_SL[i_Period_Bar] = d_SL;
    buff_TP[i_Period_Bar] = d_TP;
    if(d_TP <= High[i_Period_Bar] || d_SL >= Low[i_Period_Bar]) {
      if(buff_SL[i_Period_Bar — 1] == 0.) {
        //開始および単一の足で終了、過去1つの足で拡張
        buff_SL[i_Period_Bar — 1] = d_SL;
        buff_TP[i_Period_Bar — 1] = d_TP;
      }
      break;
    }
  }
} else { //強気の D1 バー
  //SLが一日の初め以来、高値と等しい:
  d_SL = High[ArrayMaximum(High, si_1st_Bar_of_Day, i_Period_Bar — si_1st_Bar_of_Day)];
  
  while(++i_Period_Bar < rates_total) {
    if(Time[i_Period_Bar] > t_Curr_D1_Bar + 86399) break;
    buff_SL[i_Period_Bar] = d_SL;
    buff_TP[i_Period_Bar] = d_TP;
    if(d_SL <= High[i_Period_Bar] || d_TP >= Low[i_Period_Bar]) {
      if(buff_SL[i_Period_Bar — 1] == 0.) {
        //開始および単一の足で終了、過去1 つの足で拡張
        buff_SL[i_Period_Bar — 1] = d_SL;
        buff_TP[i_Period_Bar — 1] = d_TP;
      }
      break;
    }
  }
}

f_Do_Alertシグナル通知関数の呼び出しコードを配置。実際には、このインジケーターに関与するものと比べてわずかに広い機会がカスタム設定にこのオプションを追加することができることを意味し、ファイルで作業することができます。買いの別のファイルを選択した売買シグナルの場合も同じです。関数一覧:

void f_Do_Alert(                  //シグナル通知を送信するための関数
  string  s_Message,              //アラートメッセージ
  bool    b_Alert = true,         //ポップアップ ウィンドウを表示するか?
  bool    b_Sound = false,        //サウンド ファイルを再生?
  bool    b_Email = false,        //メール?
  bool    b_Notification = false, //プッシュ通知を送る?
  string  s_Email_Subject = "",   //電子メールの件名
  string  s_Sound = "alert.wav"   //サウンド ファイル
) {
  static string ss_Prev_Message = "there was silence"; //前のアラートメッセージ
  static datetime st_Prev_Time; //以前のアラート時間
  datetime t_This_Bar_Time = TimeCurrent() — PeriodSeconds() % PeriodSeconds(); //現在の時間
  
  if(ss_Prev_Message != s_Message || st_Prev_Time != t_This_Bar_Time) {
    //このバーで別の1st
    
    //注意してください:
    ss_Prev_Message = s_Message;
    st_Prev_Time = t_This_Bar_Time;
    
    //メッセージ文字列を形成:
    s_Message = StringFormat("%s | %s | %s | %s",
      TimeToString(TimeLocal(), TIME_SECONDS), //ローカル時間
      _Symbol, //シンボル
      StringSubstr(EnumToString(ENUM_TIMEFRAMES(_Period)), 7), //TF
      s_Message //メッセージ
    );
    
    //通知シグナルをアクティブにする:
    if(b_Alert) Alert(s_Message);
    if(b_Email) SendMail(s_Email_Subject + " " + _Symbol, s_Message);
    if(b_Notification) SendNotification(s_Message);
    if(b_Sound) PlaySound(s_Sound);
  }
}

関数を呼び出し、OnCalculate イベント ハンドラーの完了前にプログラム本体にあるテキストの形成の必要性をチェックするためのコード:

//アラート
i_Period_Bar = rates_total — 1; //現在のバー

if(Alert_Popup + Alert_Email + Alert_Push == 0) return(rates_total); //すべてを無効に
if(buff_Signal[i_Period_Bar] == 0) return(rates_total); //何もまだ (または既に) キャッチなし
if(
buff_Signal [i_Period_Bar] >[i_Period_Bar]
  ||
buff_Signal [i_Period_Bar] <[i_Period_Bar]
) return(rates_total); //シグナル線に触れない

//メッセージ テキスト:
string s_Message = StringFormat("TS 80-20: needed %s @ %s, TP: %s, SL: %s",
  buff_Signal_Color[i_Period_Bar] > 0 ?"BuyStop" : "SellStop",
DoubleToString(d_Entry_Level、 _Digits)、
DoubleToString(d_TP、 _Digits)、
DoubleToString(d_SL、 _Digits)
);
//通知:
f_Do_Alert (s_Message、Alert_Popup、 falseAlert_Email、Alert_Push、Alert_Email_Subj);

return(rates_total); //OnCalculate 操作を完了

インジケーターの完全なソース コードは、添付ファイル (TS_80-20.mq5) にあります。システムによると、トレードのレイアウトは最高のチャートで見られます。

インジケーターがバーを使用することに注意してください。つまり、価格は単一の横棒に線分 (たとえば、ストップロスとテイクプロフィトのライン) を交差し、最初の交差を定義できません。他の不確実性は開始および最後のラインが一致することないという事実に由来します。それ以外の場合、DRAW_LINE と DRAW_COLOR_LINE 型のバッファから行シンプルに非表示になります。関数レイアウトの精度の低下が、まだかなり明確にあります。


80-20' トレード戦略をテストするためのEA

著書の通り、戦略をテストするための基本的な高確率短期トレーディング戦略は、最初の記事で詳しく記述されていた。2つの重要な変更を挿入してみましょう。まず、シグナルモジュールはトレードレベルで計算を設定するが妥当であり、インジケーターで使用されます。これはすでに上記で行っています。シグナルの状態から離れて、 fe_Get_Entry_Signal関数は、オーダー、ストップロス、テイクプロフィト レベルを返します。したがって、関数からレベルを受け入れるための変数を追加する前のEAのバージョンのコードの該当部分を削除し、関数呼び出し自体を編集します。古いものと新しいブロックのリストは、添付ファイル (文字列 128-141)にあります。

前の2つは、このTSとは異なり短期的な傾向を扱うという事実によるものです。ロールバックが一日一回が発生し、繰り返される可能性が高いことを想定しています。つまり、ロボットが既存のシグナルの次の日までの時間のすべての残りの部分を無視して1つだけエントリを作成します。特別なフラグを使用することで、実装する最も簡単な方法です。プログラムの bool 型の静的またはグローバル変数。EA操作が何らかの理由で中断された場合、(ターミナルを決済、グラフ等から EA を削除) フラグの値も失われます。したがって、今のシグナルが以前にアクティブ化されたかどうかをチェックが必要です。これを行うには、今日のトレード履歴を分析し、プログラムではなくターミナルのグローバル変数に、最後のエントリの日付を格納します。お知らせを実装するよりはるかに簡単に、2 番目のオプションを使用します。

「1 日あたり 1回のエントリ」 を管理する関数にオプションとロボットの開始バージョン ID を設定し、ターミナルレベルのグローバル変数を使用する必要があります。

input bool  One_Trade = false;    //1 日あたり1ポジション
input uint  Magic_Number = 2016;  //EA マジック ナンバー

「1日あたり1エントリ」オプションに必要な変数を、プログラムのグローバル変数定義ブロックに追加しましょう。OnInit 関数で初期化します。

string
  gs_Prefix //(スーパー) グローバル変数の識別子
;
bool
  gb_Position_Today = false,
  gb_Pending_Today = false
;

intOnInit() {

...

  //(スーパー) グローバル変数名のプレフィックスを作成:
  gs_Prefix = StringFormat("SSB %s %u %s", _Symbol, Magic_Number, MQLInfoInteger(MQL_TESTER) ?"t" : "");
  
  //相場で保留中のオーダーの作業ロボットは?
  gb_Position_Today = int(GlobalVariableGet(gs_Prefix + "Last_Position_Date")) == TimeCurrent() — TimeCurrent() % 86400;
  gb_Pending_Today = int(GlobalVariableGet(gs_Prefix + "Last_Pending_Date")) == TimeCurrent() — TimeCurrent() % 86400;

...
}

ロボットは、グローバル変数および今日のシグナルが既に処理されている場合、定義する開始時刻と書かれた時間を比較して値を読み取ります。2つの場所の変数に書き込まれる保留オーダーのインストール コード (強調追加) に適切なブロックを追加してみましょう。

if(i_Try != -10) { //保留中のオーダーに失敗しました
  if(Log_Level > LOG_LEVEL_NONE) Print("Pending order placing error");
  //現在の価格からの距離が十分ではない:(
  if(Log_Level > LOG_LEVEL_ERR)
    PrintFormat("Pending order cannot be placed at the %s level. Bid: %s Ask: %s StopLevel: %s",
      DoubleToString(d_Entry_Level, _Digits),
      DoubleToString(go_Tick.bid, _Digits),
      DoubleToString(go_Tick.ask, _Digits),
      DoubleToString(gd_Stop_Level, _Digits)
    );
} else { //管理
  //フラグを更新する:
  GlobalVariableSet( //ターミナルのグローバル変数に
    gs_Prefix + "Last_Pending_Date",
    TimeCurrent() — TimeCurrent() % 86400
  );
  gb_Pending_Today = true; //プログラムのグローバル変数に
}

  2番目のブロックは、新しくオープンしたポジションを定義するコードの後に配置されます。

if(PositionSelect(_Symbol)) { //オープン ポジションがある
        if(PositionGetDouble(POSITION_SL) == 0.) { //新しいポジション
                
                if(!gb_Position_Today) { //今日1つめのポジション
                        //フラグを更新:
                        GlobalVariableSet( //ターミナルのグローバル変数に
                                gs_Prefix + "Last_Position_Date",
                                TimeCurrent() — TimeCurrent() % 86400
                        );
                        gb_Position_Today = true; //プログラムのグローバル変数に
                }
...

以前の EAの唯一の重要な変更です。新しいバージョンのソース コードは、以下に添付されています。

 

戦略バックテスト

トレードシステムの実行可能性を示すためには、グラフ上で検出されたパターンを使用します。したがって、今日の相場の状況でその妥当性を確認する必要があります。テストにおいて、最も人気のある外国為替ペア EURUSD、USDJPY のペアと、金属の一つを選択しました-XAUUSD。ラシュキとコナーズは4桁を使用していたので、インデントを増加しました。トレーリングのパラメータに関するガイダンスがあるので、日の時間枠とインストゥルメントのボラティリティに最も適切と思われるものを選びました。同じルールに追加を取る利益計算アルゴリズムに適用する-その計算の比率は深い最適化なしに恣意的に選ばれました。

元のルールで5 年間 EURUSD ヒストリー上のテストしたときのバランスのグラフ:

EURUSD D1 5年

同じ設定とテイクプロフィット:

EURUSD D1 5年

5年の USDJPY ヒストリーの元のルールをテストするときのバランスのグラフ:

USDJPY D1 5年

同じ設定とテイクプロフィット:

USDJPY D1 5年

過去4年間の日のゴールドのルールをテストしたときのバランスのグラフ:

XAUUSD D1 4年

各テストで使用されるロボットの設定のデータは、完全なレポートを含む添付のアーカイブにあります。 


結論

リンダラッシュクとローレンス · コナーズによる著書「ストリートスマート:高確率短期トレード戦略」における80-20戦略について紹介しました。ただし、元のルールを少しを拡張しています。(ロボットとインジケータ)ツールは、今日の相場で TS 関連性に関するトレーダーを支援します。私の個人的な見解ではありますが、TSには深刻なアップグレードが必要です。この記事でシグナルモジュールと同様に、適切なロボットとインジケーターのコードの開発に詳細なコメントを作ることを試みました。アップグレードを行う人々を助けることになるでしょう。ルールを変更することも可能です。トラッキング、パラメータ、トレーディングシグナルの検出と同様、システムに合うものを見つけることができます。 


MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2785

添付されたファイル |
Reports.zip (607.29 KB)
MQL5.zip (123.59 KB)
トレーダーのライフハック: テストの比較レポート トレーダーのライフハック: テストの比較レポート
この記事では、EAを4 つの異なるトレードでテストします。4つのテストにおけるレポートの最終的な比較は、オンラインストアでの商品のような表にします。追加として、各シンボルの分布図が自動的に作成されます。
「タートルスープ」トレードシステムと ' タートル スープ プラス一 ' 「タートルスープ」トレードシステムと ' タートル スープ プラス一 '
この記事では、2つのトレードシステム「タートルスープ」と「タートル スープ プラスワン'のルールについて扱います。リンダ ・ ブラッドフォード ・ ラシュキ と ローレンス a. コナーズによる 高確率短期のトレード戦略です。この戦略は、かなり人気があります。15~20年間の相場の動きに基づいてを開発したものです。
不変なジグザグ 不変なジグザグ
ジグザグは、MT5のユーザーの間で人気の高いインジケーターです。この記事では、ジグザグのさまざまなパターンを作成する可能性について分析します。この結果はEAの開発に有用であるばかりでなく、その関数を拡張する不変なインジケーターとなりえます。
MQL5をプログラミングの基礎: ターミナルのグローバル変数 MQL5をプログラミングの基礎: ターミナルのグローバル変数
ターミナルのグローバル変数は、高度で信頼性の高いEAを開発するために欠かせないツールです。グローバル変数なしで MQL5で EA の開発をすることは想像を絶します。