English Русский 中文 Español Deutsch Português
「タートルスープ」トレードシステムと ' タートル スープ プラス一 '

「タートルスープ」トレードシステムと ' タートル スープ プラス一 '

MetaTrader 5 | 7 12月 2016, 13:08
3 126 0
Alexander Puzanov
Alexander Puzanov

  1. イントロダクション
  2. 「タートルスープ」トレードシステムと ' タートル スープ プラス一 '
  3. チャネル パラメータの定義
  4. シグナル生成関数
  5. TS テストの基本的なEA
  6. 戦略バックテスト
  7. 結論

イントロダクション

Street Smarts の著者: 34 年のトレード経験に基づいて、リンダラッシュクコナーズとローレンスは、高確率短期トレード戦略で成功しました。豊富な経験は銀行、ヘッジファンド、証券会社、コンサルティング会社のポジションと同様、トレードされています。安定性と収益性の高いトレードには、1つのトレード戦略 (TS)が必要だと考られています。しかし、本には、ほぼ 2 つのダース近いTSのバリエーションがあります。各グループは、動作安定した価格の動作パターンの 1 つと相場サイクルの特定の段階を指します。

この戦略は、かなり人気があります。15~20年間の相場の動きに基づいてを開発したものです。。よって、この記事では 2 つの目標-本に記載されている最初のトレード戦略で mql5 を実施して、MT5 戦略テスターを使用してその有効性を評価します。MetaQuotes' デモ サーバーで利用できる最近の履歴を使用します。

コードを書くとき、少し初心者向けのアドバイスをします。したがって、記事にはEAのプログラムを始める前に標準の作業、種類の変数を使用する理由、関数について、他のユーザーが通常不要とする説明があります。その一方で、すでに新たなトレード戦略を実装するとき、独自のソリューションのライブラリの経験豊富な開発者に対してはあまり説明しません。

プログラマのほとんどは、オブジェクト指向プログラミングの勉強に興味があります。よって、上記の目的のために、このEAの開発を役に立てましょう。手続き型からオブジェクト指向への移行を容易にするために、 オブジェクト指向プログラミングの最も複雑な部分OOPを使用しないでください。代わりに、 シンプルな構造体を使用します。構造の種類を操作するための関数の論理的に接続されたデータに参加します。継承を含むクラスのほぼすべての関数があります。しかし、クラス コードの書式設定のルールを知らずにを使用することができます。最小限の手続き型プログラミングで通常行うように調整を行うことができます。


「タートルスープ」トレードシステムと ' タートル スープ プラス一 '


タートルスープは、' tests ' シリーズの最初のトレードの戦略です。選択内容に基づいて明確にするには、 'テストレンジや価格を使用してサポート/レジスタンスレベル' として呼ばれている必要があります。タートルスープは、価格がレンジの罫線からバウンスすることがなく 20 日レンジを壊すことができない前提に基づいています。一時的なバウンスまたは偽のブレイク アウトから利益を得ます。トレード戦略は「バウンス戦略」を呼び出すことができるます。

ところで、タートルスープの名前と有名なタートルズ戦略の類似性は偶然ではありません。両方の戦略が 20 日レンジのリミットでプライスアクションを監視します。著者は、「タートルズ」を含む、ブレイク アウト戦略を使用してみましたが、そのようなトレードは偽のシグナルと深いロールバックの数が多いため非効率でした。しかし、ブレイク アウトとは反対方向の価格の動きから利益を得るためのルールのセットを作成するパターンを明らかにしました。

「タートルスープ」の TS でのロングのトレードエントリールールの完全なセットは次ように定式化できます。

  1. 20日の安値から、以前から少なくとも 3 日間を経過したことを確認してください
  2. 価格が 20 日間の安値を下回るまで待機。
  3. ブレイクした安値の5-10 ポイント上で買いの予約注文を入れます。く
  4. 保留中のオーダーがトリガーすると、この日の安値の1ポイント下にStopLoss を設定します。
  5. ポジションが利益になると、トレーリング ストップを使用します。
  6. ポジションは、最初または 2 番目の日にストップを終了する場合は、初期レベルのエントリを繰り返すことができます。 

売りトレードルールは同じです。すなわち 20 日高に基づくレンジの上部のボーダーに適用する必要があります。

コード ベースで使用できるインジケーターの1つで、適切な設定チャネルの境界線を表示できます。このインジケーターは、裁量トレードの可視化に使用できます。

 

では、どのくらい保留中のオーダーを保つ必要があるかついて直接答え、TSにないシンプルなロジックを使用します。テストするとき価格は新しい極端なポイントを形成し、次の日に上記の条件が不可能になります。その日シグナルはないので、前の日の保留中のオーダーをキャンセルしなければなりません。

' タートル スープ プラスワン' と呼ばれるこの修正バージョンには、2 つの違いがあります。

  1. 20 日レンジのブレイク アウト後すぐに保留中のオーダーを配置する代わりに、シグナルまで待つ必要があります。この日のバーがレンジから閉じる必要があります。分析された水平チャンネルの枠を 決済するのも ok です。
  2. 初期の StopLoss のレベルを決定する、適切な (高または低) 価格を使用します。

  

チャネル パラメータの定義


条件をチェックすると、時間制限を定義した後に見つけることができるレンジの高値と安値の価格を知っている必要があります。4つの変数は任意の時点で、チャネルを判断するので、単一の構造に結合できます。日 (バー) のレンジの安値から高値の数など、TSで使用される多くの変数を追加してみましょう。

struct CHANNEL {
  double    d_High;           //上部のレンジの境界線の価格
  double    d_Low;            //レンジの下端の価格
  datetime  t_From;           //チャネルの最初の (最も古い) バーの日付/時刻
  datetime  t_To;             //チャネルの最後のバーの日付/時刻
  int       i_Highest_Offset; //高値の右側のバーの数
  int       i_Lowest_Offset;  //安値の右側のバーの数
};


すべての変数は、 f_Set関数によって速やかに更新されます。この関数は、上のバーに仮想チャネル (i_Newest_Bar_Shift) とヒストリーの深さの描画を開始する必要があります。 (i_Bars_Limit)

void f_Set(int i_Bars_Limit, int i_Newest_Bar_Shift = 1) {
  double da_Price_Array[]; //チャネルのすべてのバーの高値/安値の補助配列
  
  //チャネルの上部の境界線を決定します。
  
  int i_Price_Bars = CopyHigh(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, da_Price_Array);
  int i_Bar = ArrayMaximum(da_Price_Array);
  d_High = da_Price_Array[i_Bar]; //レンジの上のチャネルを決定
  i_Highest_Offset = i_Price_Bars - i_Bar; //高値(バー) の年齢
  
  //レンジの下側の境界線を決定します。
  
  i_Price_Bars = CopyLow(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, da_Price_Array);
  i_Bar = ArrayMinimum(da_Price_Array);
  d_Low = da_Price_Array[i_Bar]; //レンジの下のチャネルの決定
  i_Lowest_Offset = i_Price_Bars - i_Bar; //安値(バー) の年
  
  datetime ta_Time_Array[];
  i_Price_Bars = CopyTime(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, ta_Time_Array);
  t_From = ta_Time_Array[0];
  t_To = ta_Time_Array[i_Price_Bars - 1];
}


この関数のコードは13 行のみです。;しかし、そうシンプルではなく、言語リファレンス (CopyHigh、CopyLow、CopyTime など) の時系列のデータにアクセスする MQL 関数の説明で読むことが必要です。関数によって返される値の数は、目的の時系列を最初にアクセスするときにリクエストされたデータが準備ができていないので、リクエストするから異なる場合があります。時系列データからのコピーのデータは、適切なメソッドを操作できます

したがって、プログラミングの最低限の基準に従って少なくとも、シンプルなエラー ハンドラーを追加してみましょう。エラーをわかりやすくするために、ログに記録するエラー データをPrintしてみましょう。オーダーは特定の決定を実行する理由についての詳細を含有することができますので、ログはデバッグに非常に有用です。多くの詳細を設定する列挙型の新しい変数をご紹介しましょう。

enum ENUM_LOG_LEVEL { //ログ出力レベルの一覧
  LOG_LEVEL_NONE,     //ログを無効
  LOG_LEVEL_ERR,      //エラー情報だけ
  LOG_LEVEL_INFO,     //エラー + ロボットのコメント
  LOG_LEVEL_DEBUG     //すべて
};

ユーザーが必要なレベルを選択して、ログに記録する情報をPrintする適切な演算子は、多くの関数に追加されます。したがって、リストとカスタム変数Log_Levelがシグナルのブロックではなく、メイン プログラムの先頭に含まれます。

f_Set関数は、すべての追加チェック (追加の行が強調表示されます) に戻ります。

void f_Set(int i_Bars_Limit, int i_Newest_Bar_Shift = 1) {
  double da_Price_Array[]; //チャネルのすべてのバーの高値/安値の補助配列
  
  //チャネルの上部の境界線を決定します。
  
  int i_Price_Bars = CopyHigh(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, da_Price_Array);
  
  if(i_Price_Bars == WRONG_VALUE) {
    //CopyHigh 関数のエラーの処理
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyHigh: error #%u", __FUNCSIG__, _LastError);
    return;
  }
  
  if(i_Price_Bars < i_Bars_Limit) {
    //CopyHigh 関数は、必要なデータ量を取得していません。
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyHigh: copied %u bars of %u", __FUNCSIG__, i_Price_Bars, i_Bars_Limit);
    return;
  }
  
  int i_Bar = ArrayMaximum(da_Price_Array);
  if(i_Bar == WRONG_VALUE) {
    //ArrayMaximum 関数のエラーの処理
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: ArrayMaximum: error #%u", __FUNCSIG__, _LastError);
    return;
  }
  
  d_High = da_Price_Array[i_Bar]; //レンジの上のチャネルを決定
  i_Highest_Offset = i_Price_Bars - i_Bar; //高値(バー) の年齢
  
  //レンジの下側の境界線を決定します。
  
  i_Price_Bars = CopyLow(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, da_Price_Array);
  
  if(i_Price_Bars == WRONG_VALUE) {
    //CopyLow 関数のエラーの処理
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyLow: error #%u", __FUNCSIG__, _LastError);
    return;
  }
  
  if(i_Price_Bars < i_Bars_Limit) {
    //CopyLow 関数は、必要なデータ量を取得していません。
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyLow: copied %u bars of %u", __FUNCSIG__, i_Price_Bars, i_Bars_Limit);
    return;
  }
  
  i_Bar = ArrayMinimum(da_Price_Array);
  if(i_Bar == WRONG_VALUE) {
    //ArrayMinimum 関数のエラーの処理
    if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: ArrayMinimum: error #%u", __FUNCSIG__, _LastError);
    return;
  }
  d_Low = da_Price_Array[i_Bar]; //レンジの下のチャネルの決定
  i_Lowest_Offset = i_Price_Bars - i_Bar; //安値(バー) の年
  
  datetime ta_Time_Array[];
  i_Price_Bars = CopyTime(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, ta_Time_Array);
  if(i_Price_Bars < 1) t_From = t_To = 0;
  else {
    t_From = ta_Time_Array[0];
    t_To = ta_Time_Array[i_Price_Bars - 1];
  }
}

エラーが検出されると、次を行います: ターミナルは次のティックまでコピー関数に必要なデータをダウンロードできるように、実行を中断します。他の関数の手順が完全に完了するまで、チャネルの使用を防ぐために構造体に適切なフラグ b_Ready (true = データ準備ができている、偽 = プロセスはまだ完了していません)を追加してみましょう。。また、チャネルのパラメータ更新フラグ (b_Updated) を追加し-最適なパフォーマンス TS で使用される 4 つのパラメータに変更があるかどうか調べるのに便利です。この目的のため は1つより多くの変数を追加する必要があります-チャネルのサイン (s_Signature)。f_Set関数は、構造体に追加するか、このようになります。 

//チャンネル情報と収集し、1 つの構造体で、更新
struct CHANNEL {
  //変数
  double    d_High;           //上部のレンジの境界線の価格
  double    d_Low;            //レンジの下端の価格
  datetime  t_From;           //チャネルの最初の (最も古い) バーの日付/時刻
  datetime  t_To;             //チャネルの最後のバーの日付/時刻
  int       i_Highest_Offset; //高値の右側のバーの数
  int       i_Lowest_Offset;  //安値の右側のバーの数
  bool      b_Ready;          //更新プロシージャに、パラメータを超えたか?
  bool      b_Updated;        //変更されたチャネルのパラメータを持つか?
  string    s_Signature;      //最後の知られている一連のデータのサイン
  
  //関数:
  
  CHANNEL() {
    d_High = d_Low = 0;
    t_From = t_To = 0;
    b_Ready = b_Updated = false;
    s_Signature = "-";
    i_Highest_Offset = i_Lowest_Offset = WRONG_VALUE;
  }
  
  void f_Set(int i_Bars_Limit, int i_Newest_Bar_Shift = 1) {
    b_Ready = false; //ピットストップ: サービス フラグを設定
    
    double da_Price_Array[]; //チャネルのすべてのバーの高値/安値の補助配列
    
    //チャネルの上部の境界線を決定します。
    
    int i_Price_Bars = CopyHigh(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, da_Price_Array);
    if(i_Price_Bars == WRONG_VALUE) {
      //CopyHigh 関数のエラーの処理
      if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyHigh: error #%u", __FUNCSIG__, _LastError);
      return;
    }
    
    if(i_Price_Bars < i_Bars_Limit) {
      //CopyHigh 関数は、必要なデータ量を取得していません。
      if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyHigh: copied %u bars of %u", __FUNCSIG__, i_Price_Bars, i_Bars_Limit);
      return;
    }
    
    int i_Bar = ArrayMaximum(da_Price_Array);
    if(i_Bar == WRONG_VALUE) {
      //ArrayMaximum 関数のエラーの処理
      if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: ArrayMaximum: error #%u", __FUNCSIG__, _LastError);
      return;
    }
    
    d_High = da_Price_Array[i_Bar]; //レンジの上のチャネルを決定
    i_Highest_Offset = i_Price_Bars - i_Bar; //高値(バー) の年齢
    
    //レンジの下側の境界線を決定します。
    
    i_Price_Bars = CopyLow(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, da_Price_Array);
    
    if(i_Price_Bars == WRONG_VALUE) {
      //CopyLow 関数のエラーの処理
      if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyLow: error #%u", __FUNCSIG__, _LastError);
      return;
    }
    
    if(i_Price_Bars < i_Bars_Limit) {
      //CopyLow 関数は、必要なデータ量を取得していません。
      if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyLow: copied %u bars of %u", __FUNCSIG__, i_Price_Bars, i_Bars_Limit);
      return;
    }
    
    i_Bar = ArrayMinimum(da_Price_Array);
    if(i_Bar == WRONG_VALUE) {
      //ArrayMinimum 関数のエラーの処理
      if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: ArrayMinimum: error #%u", __FUNCSIG__, _LastError);
      return;
    }
    d_Low = da_Price_Array[i_Bar]; //レンジの下のチャネルの決定
    i_Lowest_Offset = i_Price_Bars - i_Bar; //安値(バー) の年
    
    datetime ta_Time_Array[];
    i_Price_Bars = CopyTime(_Symbol, PERIOD_CURRENT, i_Newest_Bar_Shift, i_Bars_Limit, ta_Time_Array);
    if(i_Price_Bars < 1) t_From = t_To = 0;
    else {
      t_From = ta_Time_Array[0];
      t_To = ta_Time_Array[i_Price_Bars - 1];
    }
    
    string s_New_Signature = StringFormat("%.5f%.5f%u%u", d_Low, d_High, t_From, t_To);
    if(s_Signature != s_New_Signature) {
      //チャネルのデータが変わった
      b_Updated = true;
      if(Log_Level > LOG_LEVEL_ERR) PrintFormat("%s: Channel updated: %s .. %s / %s .. %s, min: %u max: %u ", __FUNCTION__, DoubleToString(d_Low, _Digits), DoubleToString(d_High, _Digits), TimeToString(t_From, TIME_DATE|TIME_MINUTES), TimeToString(t_To, TIME_DATE|TIME_MINUTES), i_Lowest_Offset, i_Highest_Offset);
      s_Signature = s_New_Signature;
    }
    
    b_Ready = true; //データ更新が正常に完了しました
  }
};

(アクセスできるようにさまざまなユーザー関数から) グローバル レベルのこのタイプの 1 つのチャネル オブジェクトを宣言する必要があります。

CHANNEL go_Channel;

シグナル生成関数


このシステムによれば買いするシグナルは、2 つの必要条件によって決定されます。

1. 少なくとも3トレード日で、それより前の20 日の安値を超えた

2a. 20日間の安値を下回った (タートル スープ)

2b. 日足が20日の安値を超えずに終値を迎えた。 (タートル スープ プラス ワン)


 

上記で説明したその他すべての TS ルールはトレードオーダーパラメータに属さないシグナルブロックに含まれますので、ポジションの管理をします。

1 つのモジュールで、トレードシステム (タートルスープとタートル スープ プラスワン) の両方の変更のルールに従ってシグナルをプログラムしていきます。ルールの適切なバージョンを選択する可能性は、EA パラメータに追加されます。適切なカスタム変数Strategy_Typeと呼びましょう。true/false (ブール型変数) を使用すると容易になるので、オプションにのみ戦略のリストが含まれます。番号付きリストを使用して、すべての戦略をこのシリーズの記事内にあるコードに変換する必要性があります。

enum ENUM_STRATEGY {     //戦略のリスト
  TS_TURTLE_SOUP,        //タートルスープ
  TS_TURTLE_SOUP_PLUS_1  //タートルスープ プラス 1
};
input ENUM_STRATEGY  Strategy_Type = TS_TURTLE_SOUP;  //トレード戦略:

メイン プログラムのシグナル検出関数に渡される戦略の種類、すなわち、バーを待機するかどうかを知る必要があります。bool 型のb_Wait_For_Bar_Close変数。2 番目の変数は前の極値i_Extremum_Barsの後一時ストップが必要です。この関数は、シグナルのステータス(買い/売り条件を満たしているか)を返す必要があります: 適切な番号付きリストは、メイン EA ファイルにも追加されます。

enum ENUM_ENTRY_SIGNAL {  //エントリー シグナルのリスト
  ENTRY_BUY,              //買いシグナル
  ENTRY_SELL,             //売りシグナル
  ENTRY_NONE,             //シグナルがない
  ENTRY_UNKNOWN           //不明確な状態
};

シグナルモジュールとメイン プログラムの関数の両方を使用する別の構造体は、最新のティックに関する情報を含むgo_Tickグローバル オブジェクトです。メインファイルで宣言する必要がある MqlTickの標準的な構造です。後でメイン プログラム本体 (OnTick 関数) でその更新をプログラムしていきます。

MqlTick go_Tick; //最後の知られているティックについての情報

さて、最後に、モジュールの主な関数です。

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal (
  bool b_Wait_For_Bar_Close = false,
  int i_Extremum_Bars = 3
) {}

売りシグナル状態のチェックをから始めましょう。前の高値 (最初の状態) の後、足 に十分な日を経過したかどうかと、価格が上限制限 (2 番目の条件) を壊れているかどうかのチェックです。

if(go_Channel.i_Highest_Offset > i_Extremum_Bars) //第 1 条件
  if(go_Channel.d_High < d_Actual_Price) //第 2 条件
    return(ENTRY_SELL); //売り条件の両方が満たされる

買いシグナルの状態のチェックは似ています。

if(go_Channel.i_Lowest_Offset > i_Extremum_Bars) //第 1 条件
  if(go_Channel.d_Low > d_Actual_Price) { //第 2 条件
    return(ENTRY_BUY); //買いする条件の両方が満たされる

ここでこの TS に関連する現在の価格は、 d_Actual_Price変数を使用しています。タートル スープに加えて、1 つ前のBid価格つまりタートル スープの日 (バー) の終値です。

double d_Actual_Price = go_Tick.bid; //既定の価格 - タートルスープのバージョン
if(b_Wait_For_Bar_Close) { //タートル スープに加えて、1 つのバージョン
  double da_Price_Array[1]; //補助配列
  CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, da_Price_Array));
  d_Actual_Price = da_Price_Array[0];
}

最低限必要な操作が含まれている関数は、このようになります。

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal(bool b_Wait_For_Bar_Close = false, int i_Extremum_Bars = 3) {
  double d_Actual_Price = go_Tick.bid; //既定の価格 - タートルスープのバージョン
  if(b_Wait_For_Bar_Close) { //タートル スープに加えて、1 つのバージョン
    double da_Price_Array[1];
    CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, da_Price_Array));
    d_Actual_Price = da_Price_Array[0];
  }
  
  //上限:
  if(go_Channel.i_Highest_Offset > i_Extremum_Bars) //第 1 条件
    if(go_Channel.d_High < d_Actual_Price) { //第 2 条件
      //価格上限が壊れています。
      return(ENTRY_SELL);
    }
  
  //下限:
  if(go_Channel.i_Lowest_Offset > i_Extremum_Bars) //第 1 条件
    if(go_Channel.d_Low > d_Actual_Price) { //第 2 条件
      //下限価格が壊れています。
      return(ENTRY_BUY);
    }
  
  return(ENTRY_NONE);
}

チャネル オブジェクト可能性があります。 (フラグgo_Channel.b_Ready = false)。よって、 このフラグのチェックを追加する必要があります。この関数では、標準的な関数の 1 つを使用して、時系列データ (CopyClose) からデータをコピーするため、エラー処理を追加します。デバッグを容易にするデータのロギングについて忘れてはいけません。

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal(bool b_Wait_For_Bar_Close = false, int i_Extremum_Bars = 3) {
  if(!go_Channel.b_Ready) {
    //チャネルのデータ使用をしていません。
    if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: Channel parameters are not prepared", __FUNCTION__);
    return(ENTRY_UNKNOWN);
  }
  
  double d_Actual_Price = go_Tick.bid; //既定の価格 - タートルスープのバージョン
  if(b_Wait_For_Bar_Close) { //タートル スープに加えて、1 つのバージョン
    double da_Price_Array[1];
    if(WRONG_VALUE == CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, da_Price_Array)) {
      //CopyClose 関数のエラーを処理
      if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyClose: error #%u", __FUNCSIG__, _LastError);
      return(ENTRY_NONE);
    }
    d_Actual_Price = da_Price_Array[0];
  }
  
  //上限:
  if(go_Channel.i_Highest_Offset > i_Extremum_Bars) //第 1 条件
    if(go_Channel.d_High < d_Actual_Price) { //第 2 条件
      //価格上限が壊れています。
      if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: Price (%s) has broken the upper limit (%s)", __FUNCTION__, DoubleToString(d_Actual_Price, _Digits), DoubleToString(go_Channel.d_High, _Digits));
      return(ENTRY_SELL);
    }
  
  //下限:
  if(go_Channel.i_Lowest_Offset > i_Extremum_Bars) //第 1 条件
    if(go_Channel.d_Low > d_Actual_Price) { //第 2 条件
      //下限価格が壊れています。
      if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: Price (%s) has broken the lower limit (%s)", __FUNCTION__, DoubleToString(d_Actual_Price, _Digits), DoubleToString(go_Channel.d_Low, _Digits));
      return(ENTRY_BUY);
    }
  
  //価格レンジ内で達した場合、2番目の条件が満たされない
  
  return(ENTRY_NONE);
}

十万回この関数が呼び出されます。ただし、(最後の極値から3日以上) の最初の条件が満たされていない場合は、更なる行動が無意味になります。適切なプログラミング スタイルのルール、 リソースの消費を最小限に抑えるため、次の足まで、スリープ状態するようにする必要があります。

ENUM_ENTRY_SIGNAL fe_Get_Entry_Signal(bool b_Wait_For_Bar_Close = false, int i_Extremum_Bars = 3) {
  static datetime st_Pause_End = 0; //次のチェックの時間
  if(st_Pause_End > go_Tick.time) return(ENTRY_NONE);
  st_Pause_End = 0;
  
  if(go_Channel.b_In_Process) {
    //チャネルのデータ使用をしていません。
    if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: Channel parameters are not prepared", __FUNCTION__);
    return(ENTRY_UNKNOWN);
  }
  if(go_Channel.i_Lowest_Offset < i_Extremum_Bars && go_Channel.i_Highest_Offset < i_Extremum_Bars) {
    //第 1 の条件が満たされない
    if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: the 1st condition is not met", __FUNCTION__);
    
    //チャネルが更新されるまでは一時ストップ
    st_Pause_End = go_Tick.time + PeriodSeconds() - go_Tick.time % PeriodSeconds();
    
    return(ENTRY_NONE);
  }
  
  double d_Actual_Price = go_Tick.bid; //既定の価格 - タートルスープのバージョン
  if(b_Wait_For_Bar_Close) { //タートル スープに加えて、1 つのバージョン
    double da_Price_Array[1];
    if(WRONG_VALUE == CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, da_Price_Array)) {
      //CopyClose 関数のエラーを処理
      if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyClose: error #%u", __FUNCSIG__, _LastError);
      return(ENTRY_NONE);
    }
    d_Actual_Price = da_Price_Array[0];
  }
  
  //上限:
  if(go_Channel.i_Highest_Offset > i_Extremum_Bars) //第 1 条件
    if(go_Channel.d_High < d_Actual_Price) { //第 2 条件
      //価格上限が壊れています。
      if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: Price (%s) has broken the upper limit (%s)", __FUNCTION__, DoubleToString(d_Actual_Price, _Digits), DoubleToString(go_Channel.d_High, _Digits));
      return(ENTRY_SELL);
    }
  
  //下限:
  if(go_Channel.i_Lowest_Offset > i_Extremum_Bars) //第 1 条件
    if(go_Channel.d_Low > d_Actual_Price) { //第 2 条件
      //下限価格が壊れています。
      if(Log_Level == LOG_LEVEL_DEBUG) PrintFormat("%s: Price (%s) has broken the lower limit (%s)", __FUNCTION__, DoubleToString(d_Actual_Price, _Digits), DoubleToString(go_Channel.d_Low, _Digits));
      return(ENTRY_BUY);
    }
  
  //価格レンジ内で達した場合、2番目の条件が満たされない
  
  if(b_Wait_For_Bar_Close) //タートル スープに加えた1 つのバージョン
    //現在の足を閉じるまで一時ストップ。
    st_Pause_End = go_Tick.time + PeriodSeconds() - go_Tick.time % PeriodSeconds();
  
  return(ENTRY_NONE);
}

この関数の最後のコードです。シグナルモジュールSignal_Turtle_Soup.mqhのファイルを呼び出し、チャネルおよびシグナルに関連するコードを追加してみましょう。ファイルの先頭には、戦略のカスタム設定のエントリ フィールドを追加します。

enum ENUM_STRATEGY {     //戦略バージョン
  TS_TURTLE_SOUP,        //タートルスープ
  TS_TURTLE_SOUP_PLIS_1  //タートルスープ プラス 1
};

//カスタム設定
input ENUM_STRATEGY  Turtle_Soup_Type = TS_TURTLE_SOUP;  //タートルスープ: 戦略バージョン
input uint           Turtle_Soup_Period_Length = 20;     //タートルスープ: 極値探索 (バー) の深さ
input uint           Turtle_Soup_Extremum_Offset = 3;    //タートルスープ: (バー) で最後の極値
input double         Turtle_Soup_Entry_Offset = 10;      //タートルスープ: エントリ: ポイント単位で極端なレベルからのオフセット
input double         Turtle_Soup_Exit_Offset = 1;        //タートルスープ: 終了: (ポイント) の反対の極値からオフセット

ターミナルデータ フォルダーにこのファイルを保存します。シグナルライブラリは、MQL5\Include\Expert\Signal に格納する必要があります。

 

TS テストの基本的なEA


EAコードの先頭、フィールドの設定で使用される列挙型のリストを追加する前に、ユーザー設定フィールドを追加します。設定を 2 つのグループに分けてみましょう。 —「戦略設定」と「ポジションと管理」。最初のグループ設定はコンパイル時にシグナルライブラリ ファイルから含まれているになります。これまで、1つのファイルを作成しました。必要なカスタム設定を含むシグナルモジュールを置換 (または追加) することが可能になります。

取引オペレーションを実行するため mql5 を標準ライブラリ ファイルに含めます。

enum ENUM_LOG_LEVEL {  //ログ出力レベルの一覧
  LOG_LEVEL_NONE,      //ログを無効に
  LOG_LEVEL_ERR,       //エラー情報だけ
  LOG_LEVEL_INFO,      //エラー + ロボットのコメント
  LOG_LEVEL_DEBUG      //すべて
};
enum ENUM_ENTRY_SIGNAL {  //エントリー シグナルのリスト
  ENTRY_BUY,              //買いシグナル
  ENTRY_SELL,             //売りシグナル
  ENTRY_NONE,             //シグナルがない
  ENTRY_UNKNOWN           //不明確な状態
};

#include <Trade\Trade.mqh> //トレード操作を実行するクラス



input string  _ = "** Strategy settings:";  //.

#include <Expert\Signal\Signal_Turtle_Soup.mqh> //シグナルモジュール

                                                        
input string  __ = "** Position opening and management:"; //.
input double  Trade_Volume = 0.1;                  //ボリュームを交換
input uint    Trail_Trigger = 100;                 //: トレーリング (ポイント) を有効にする
input uint    Trail_Step = 5;                      //トレーリング: (ポイント) の SL 移動ステップ
input uint    Trail_Distance = 50;                 //トレーリング: 最大距離を価格から SL にポイントで)
input ENUM_LOG_LEVEL  Log_Level = LOG_LEVEL_INFO;  //ログ モード:

任意の特別なお金の管理やリスク管理手法は言及しません。

トレーリングの設定は、ポイントでエントリーする必要があります。ここで 1 つポイントが最小シンボル価格変更に対応している5桁のクオートの導入、使用する単位にリードしています。つまり、5 桁のクオートの一点は 0.00001、4 桁のクオートは 0.0001 に等しい。ピップと混同しないように-ピップの引用符の実際の精度を無視して、常に 4 桁に変換します。すなわちシンボル (ポイント) の最低の価格変動は 0.00001、1 pip は 10 ポイントに等しくなります。

トレーリングストップ関数が刻むごとに、設定を使用して、多くの CPU リソースを消費しません。一度EAの初期化中にユーザーがエントリーした値を再計算し、将来使用するためのグローバル変数に保存する方が正しいでしょう。多くの正規化に使用する変数と同じことを行うことができます。読み込むたびにする必要はありませんので、EAの操作中には変更しないでください。グローバル変数、初期化関数の宣言です。

int
  gi_Try_To_Trade = 4, //トレードオーダーを送信する回数
  gi_Connect_Wait = 2000 // (単位はミリ秒) 一時ストップ
;
double
  gd_Stop_Level, //シンボルの価格に変換サーバーのStopLevel
  gd_Lot_Step, gd_Lot_Min, gd_Lot_Max, //多くのサーバーの設定から値の制限
  gd_Entry_Offset, //エントリ: オフセットシンボル価格の極値から
  gd_Exit_Offset, //決済: オフセットシンボル価格の極値から
  gd_Trail_Trigger, gd_Trail_Step, gd_Trail_Distance //トレーリングパラメータシンボル価格に変換
;
MqlTick go_Tick; //最後の知られているティックについての情報



intOnInit() {
  //通貨価格にポイントから設定を変換します。
  double d_One_Point_Rate = pow(10, _Digits);
  gd_Entry_Offset = Turtle_Soup_Entry_Offset / d_One_Point_Rate;
  gd_Exit_Offset = Turtle_Soup_Exit_Offset / d_One_Point_Rate;
  gd_Trail_Trigger = Trail_Trigger / d_One_Point_Rate;
  gd_Trail_Step = Trail_Step / d_One_Point_Rate;
  gd_Trail_Distance = Trail_Distance / d_One_Point_Rate;
  gd_Stop_Level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) / d_One_Point_Rate;
  //多くの制限の初期化:
  gd_Lot_Min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
  gd_Lot_Max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
  gd_Lot_Step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
  
  return(INIT_SUCCEEDED);
}

Mql5 を標準ライブラリにトレーリングのモジュールが含まれていることに注意してください (TrailingFixedPips.mqh)。コードのクラスを実行するトレーディングオペレーションを含めることができます。しかし、完全に従わないこのEAの関数を持つので、トレーリングのコードを書き込み、別のカスタム関数の形式でEAに追加します。

bool fb_Trailing_Stop(    //トレーリングの現在のシンボルポジションの SL
  double d_Trail_Trigger,  //トレーリングを有効にするまでの距離 (シンボルの価格で)
  double d_Trail_Step,    //(シンボルの価格) で SL トレーリングステップ
  double d_Trail_Distance  //(シンボルの価格) で SL 価格からの距離</s3>
) {
  if(!PositionSelect(_Symbol)) return(false); //トレールには何もありません
  
  //新しい SL レベル - 現在の価格の値の計算の基本的な値:
  double d_New_SL = PositionGetDouble(POSITION_PRICE_CURRENT);
  
  if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //ロング ポジション
    if(d_New_SL - PositionGetDouble(POSITION_PRICE_OPEN) < d_Trail_Trigger)
      return(false); //価格がトレーリングを有効にするのに十分移動しなかった
      
    if(d_New_SL - PositionGetDouble(POSITION_SL) < d_Trail_Distance + d_Trail_Step)
      return(false); //トレーリングのステップ セット SL より価格変更
    
    d_New_SL -= d_Trail_Distance; //新しい SL レベル
  } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { //ショート ポジション
    if(PositionGetDouble(POSITION_PRICE_OPEN) - d_New_SL < d_Trail_Trigger)
      return(false); //価格がトレーリングを有効にするのに十分移動しなかった
    
    if(PositionGetDouble(POSITION_SL) > 0.0) if(PositionGetDouble(POSITION_SL) - d_New_SL < d_Trail_Distance + d_Trail_Step)
      return(false); //価格がトレーリングを有効にするのに十分移動しなかった
    
    d_New_SL += d_Trail_Distance; //新しい SL レベル
  } else return(false);
  
  //サーバーの設定は現在の価格からこの距離で新しい SL を置くことを許可しているか?
  if(!fb_Is_Acceptable_Distance(d_New_SL, PositionGetDouble(POSITION_PRICE_CURRENT))) return(false);
  
  CTrade Trade;
  Trade.LogLevel(LOG_LEVEL_ERRORS);
  //SLを移動。
  Trade.PositionModify(_Symbol, d_New_SL, PositionGetDouble(POSITION_TP));
  
  return(true);
}



bool fb_Is_Acceptable_Distance (2重d_Level_To_Check、ダブルd_Current_Price) {
  return(
    fab(d_Current_Price - d_Level_To_Check)
    >
    fmax(gd_Stop_Level、go_Tick.ask - go_Tick.bid)
  );
}

新しい距離で SL を置くことを許可するかどうかは保留中の検証にも使用できます。別の関数fb_Is_Acceptable_Distanceに含まれてチェック レベルを配置してオープン ポジションのストップロス レベルでオーダーします。

OnTick - 新しいティック到着イベントを処理するハンドラー関数によって呼び出されるコードで、主な作業領域です。戦略のルールによると、オープン ポジションがある場合、EAが新しいシグナルを検索する必要があります。したがって、 適切なチェックで始まります。ロボットは 2 つのオプションでポジションが既に存在する場合: いずれかの計算、新しいポジションの初期の StopLoss レベルを設定またはトレーリングをアクティブにする関数、StopLoss が移動する必要があるか、適切な操作を実行するかどうかを決定します。トレーリングの関数を呼び出すことは簡単です。StopLoss レベル計算は極値gd_Exit_Offsetポイントでユーザーがエントリーした、通貨価格に変換からのオフセットを使用します。極端な価格は、標準の mql5 を関数の CopyHigh または CopyLow を使用して見つけることができます。Fb_Is_Acceptable_Distance関数とgo_Tick構造体から現在の価格の値を使用して、計算レベルを検証する必要があります。BuyStop および SellStopの検証が分離されます。

if(PositionSelect(_Symbol)) { //オープン ポジションがある
  if(PositionGetDouble(POSITION_SL) == 0.) { //新しいポジション
    double
      d_SL = WRONG_VALUE, //SL レベル
      da_Price_Array[] //補助配列
    ;
    
    //StopLoss レベルを計算します。
    if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { //ロング ポジション
      if(WRONG_VALUE == CopyLow(_Symbol, PERIOD_CURRENT, 0, 1 + (Turtle_Soup_Type == TS_TURTLE_SOUP_PLIS_1), da_Price_Array)) {
        //CopyLow 関数のエラーの処理
        if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyLow: error #%u", __FUNCTION__, _LastError);
        return;
      }
      d_SL = da_Price_Array[ArrayMinimum(da_Price_Array)] - gd_Exit_Offset;
      
      //現在の距離は十分な価格か。
      if(!fb_Is_Acceptable_Distance(d_SL, go_Tick.bid)) {
        if(Log_Level > LOG_LEVEL_NONE) PrintFormat("Calculated SL level %s is replaced by the minimum allowable %s", DoubleToString(d_SL, _Digits), DoubleToString(go_Tick.bid + fmax(gd_Stop_Level, go_Tick.ask - go_Tick.bid), _Digits));
        d_SL = go_Tick.bid - fmax(gd_Stop_Level, go_Tick.ask - go_Tick.bid);
      }
      
    } else { //ショート ポジション
      if(WRONG_VALUE == CopyHigh(_Symbol, PERIOD_CURRENT, 0, 1 + (Turtle_Soup_Type == TS_TURTLE_SOUP_PLIS_1), da_Price_Array)) {
        //CopyHigh 関数のエラーの処理
        if(Log_Level > LOG_LEVEL_NONE) PrintFormat("%s: CopyHigh: error #%u", __FUNCTION__, _LastError);
        return;
      }
      d_SL = da_Price_Array[ArrayMaximum(da_Price_Array)] + gd_Exit_Offset;
      
      //現在の距離は十分な価格か。
      if(!fb_Is_Acceptable_Distance(d_SL, go_Tick.ask)) {
        if(Log_Level > LOG_LEVEL_NONE) PrintFormat("Calculated SL level %s is replaced by the minimum allowable %s", DoubleToString(d_SL, _Digits), DoubleToString(go_Tick.ask - fmax(gd_Stop_Level, go_Tick.ask - go_Tick.bid), _Digits));
        d_SL = go_Tick.ask + fmax(gd_Stop_Level, go_Tick.ask - go_Tick.bid);
      }
    }
    
    CTrade Trade;
    Trade.LogLevel(LOG_LEVEL_ERRORS);
    //SLのセット
    Trade.PositionModify(_Symbol, d_SL, PositionGetDouble(POSITION_TP));
    return;
  }
  
  //トレーリング
  fb_Trailing_Stop(gd_Trail_Trigger, gd_Trail_Step, gd_Trail_Distance);
  return;
}

新しいティックの計算されたパラメータに加えて、またシグナルの検出に使用されるチャネルのパラメータを更新する必要があります。go_Channel構造体の適切なf_Set関数を呼び出します。トレードのロボットは、(バー) の初めに、無関係な昨日の保留中のオーダーの削除をします。2つのアクションをプログラムしてみましょう。

int
  i_Order_Ticket = WRONG_VALUE, //保留中のオーダーのチケット
  i_Try = gi_Try_To_Trade, //操作を実行する試行回数
  i_Pending_Type = -10 //既存の保留中のオーダーの種類
;
static int si_Last_Tick_Bar_Num = 0; //前のティックのバーの番号 (0 = MQL の計算の初め)

//新しい一日 (バー) は、先頭にリンク イベントを処理します。
if(si_Last_Tick_Bar_Num < int(floor(go_Tick.time / PeriodSeconds()))) {
  //新しい朝が来た:) 
  si_Last_Tick_Bar_Num = int(floor(go_Tick.time / PeriodSeconds()));
  
  //廃止された保留中のオーダーはありますか。
  i_Pending_Type = fi_Get_Pending_Type(i_Order_Ticket);
  if(i_Pending_Type == ORDER_TYPE_SELL_STOP || i_Pending_Type == ORDER_TYPE_BUY_STOP) {
    //古いオーダーを削除します。
    if(Log_Level > LOG_LEVEL_ERR) Print("Deleting yesterday's pending order");
    
    CTrade o_Trade;
    o_Trade.LogLevel(LOG_LEVEL_ERRORS);
    while(i_Try-- > 0) { //削除
      if(o_Trade.OrderDelete(i_Order_Ticket)) { //成功
        i_Try = -10; //成功した操作のフラグ
        break;
      }
      //試行が失敗
      Sleep(gi_Connect_Wait); //次の試みの前に一時停止
    }
    
    if(i_Try == WRONG_VALUE) { //保留中のオーダーの削除に失敗しました
      if(Log_Level > LOG_LEVEL_NONE) Print("Pending order deleting error");
      return; //次のティックまで待つ
    }
  }
  
  //チャネル パラメータの更新。
  go_Channel.f_Set(Turtle_Soup_Period_Length, 1 + (Turtle_Soup_Type == TS_TURTLE_SOUP_PLIS_1));
}

ここに使用するfi_Get_Pending_Type関数は、保留中のオーダーの種類を返します。i_Order_Ticket変数に受信した参照を使用して、チケット番号を追加します。オーダータイプは、オーダーを削除する必要がある場合にチケットが使用されている間、このティックに実際のシグナル方向を比較するため後で使用されます。保留中のオーダーがない場合、両方の値は WRONG_VALUE に等しくなります。この関数のリストは以下の通りです。

int fi_Get_Pending_Type( //現在のシンボルの保留中のオーダーの存在を検出
  int& i_Order_Ticket //選択したチケットへの保留オーダー
) {
  int
    i_Order = OrdersTotal(), //オーダーの総数
    i_Order_Type = WRONG_VALUE //オーダーのタイプの変数
  ;
  i_Order_Ticket = WRONG_VALUE; //既定のチケットの値返されます
  
  if(i_Order < 1) return(i_Order_Ticket); //オーダーなし
  
  while(i_Order-- > 0) { //既存のオーダーをチェック
    i_Order_Ticket = int(OrderGetTicket(i_Order)); //チケットを読む
    if(i_Order_Ticket > 0)
      if(StringCompare(OrderGetString(ORDER_SYMBOL), _Symbol, false) == 0) {
        i_Order_Type = int(OrderGetInteger(ORDER_TYPE));
        //保留中のオーダーのみ必要です。
        if(i_Order_Type == ORDER_TYPE_BUY_LIMIT || i_Order_Type == ORDER_TYPE_BUY_STOP || i_Order_Type == ORDER_TYPE_SELL_LIMIT || i_Order_Type == ORDER_TYPE_SELL_STOP)
          break; //保留中のオーダーを発見されている
      }
    i_Order_Ticket = WRONG_VALUE; //まだ見つかりません
  }
  
  return(i_Order_Type);
}

今すぐすべてのシグナルの状態を確認する準備ができました。TS条件が満たされない場合 (シグナルが ENTRY_NONE または ENTRY_UNKNOWN の状態)、このティックのメイン プログラムの操作を完了することができます。

//シグナルの状態を取得します。
ENUM_ENTRY_SIGNAL e_Signal = fe_Get_Entry_Signal(Turtle_Soup_Type == TS_TURTLE_SOUP_PLIS_1, Turtle_Soup_Extremum_Offset);
if(e_Signal > 1) return; //シグナルなし

シグナルがある場合は、既に配置されている場合に、保留中のオーダー既存の方向と比較します。

//これをまだ行っていない場合は、保留中のオーダー、そのチケットの種類を見つけます。
if(i_Pending_Type == -10)
  i_Pending_Type = fi_Get_Pending_Type(i_Order_Ticket);

//新しい保留中のオーダーが必要かどうか。
if(
  (e_Signal == ENTRY_SELL && i_Pending_Type == ORDER_TYPE_SELL_STOP)
  ||
  (e_Signal == ENTRY_BUY && i_Pending_Type == ORDER_TYPE_BUY_STOP)
) return; //シグナルの方向で保留中のオーダーがある

//保留中のオーダーを削除するか?
if(
  (e_Signal == ENTRY_SELL && i_Pending_Type == ORDER_TYPE_BUY_STOP)
  ||
  (e_Signal == ENTRY_BUY && i_Pending_Type == ORDER_TYPE_SELL_STOP)
) { //保留中のオーダーの方向とシグナルの方向が一致しない
  if(Log_Level > LOG_LEVEL_ERR) Print("The direction of the pending order does not correspond to the direction of the signal");
    
  i_Try = gi_Try_To_Trade;
  while(i_Try-- > 0) { //削除
    if(o_Trade.OrderDelete(i_Order_Ticket)) { //成功
      i_Try = -10; //成功した操作のフラグ
      break;
    }
    //試行が失敗
    Sleep(gi_Connect_Wait); //次の試みの前に一時停止
  }
  
  if(i_Try == WRONG_VALUE) { //保留中のオーダーの削除に失敗しました
    if(Log_Level > LOG_LEVEL_NONE) Print("Pending order deleting error");
    return; //次のティックまで待つ
  }
}

新しい保留中のオーダーを配置するパラメータを計算してみましょう。戦略のルールに従って、チャネルの制限から内側のオフセットオーダーをする必要があります。価格逆転の 2 日前 (選択した戦略のバージョン) に応じて、ボーダーの反対側で StopLoss を配置する必要があります。保留中のオーダーのトリガー後、StopLoss ポジションを計算する必要があります。


関連するチャネル制限をgo_Channel構造体から赤にする必要があります。 gd_Entry_Offset変数で利用できるエントリのオフセットは、ユーザーによって指定された、シンボル価格に変換されます。Fb_Is_Acceptable_Distance関数およびgo_Tick構造体から現在の価格の値を使用して計算されるレベルを検証しなければなりません。BuyStop および SellStopの検証が分離されます。

double d_Entry_Level = WRONG_VALUE; //保留中のオーダーのレベル
if(e_Signal == ENTRY_BUY) { //保留中の買いオーダー
  //オーダーする可能性をチェック。
  d_Entry_Level = go_Channel.d_Low + gd_Entry_Offset; //発注レベル
  if(!fb_Is_Acceptable_Distance(d_Entry_Level, go_Tick.ask)) {
    //現在の価格からの距離が十分ではない。 
    if(Log_Level > LOG_LEVEL_ERR)
      PrintFormat("BuyStop 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)
      );
    
    return; //現在の価格変更まで待つ
  }
} else {
  //オーダーする可能性をチェック。
  d_Entry_Level = go_Channel.d_High - gd_Entry_Offset; //発注レベル
  if(!fb_Is_Acceptable_Distance(d_Entry_Level, go_Tick.bid)) {
    //現在の価格からの距離が十分ではない。 
    if(Log_Level > LOG_LEVEL_ERR)
      PrintFormat("SellStop 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)
      );
    
    return; //現在の価格変更まで待つ
  }
}

計算される発注レベルが正常に確認された場合は、標準ライブラリのクラスを使用してサーバーに適切なオーダーを送信できます。

//多くのサーバーの要件に従って。
double d_Volume = fd_Normalize_Lot(Trade_Volume);

//保留中のオーダーを配置します。
i_Try = gi_Try_To_Trade;

if(e_Signal == ENTRY_BUY) {
  while(i_Try-- > 0) { //BuyStop を配置
    if(o_Trade.BuyStop(
      d_Volume,
      d_Entry_Level,
      _Symbol
    )) { //成功
      Alert("A pending Buy order has been placed!");
      i_Try = -10; //成功した操作のフラグ
      break;
    }
    //失敗
    Sleep(gi_Connect_Wait); //次の試みの前に一時停止
  }
} else {
  while(i_Try-- > 0) { //SellStop を配置
    if(o_Trade.SellStop(
      d_Volume,
      d_Entry_Level,
      _Symbol
    )) { //成功
      Alert("A pending Sell order has been placed!");
      i_Try = -10; //成功した操作のフラグ
      break;
    }
    //失敗
    Sleep(gi_Connect_Wait); //次の試みの前に一時停止
  }
}

if(i_Try == WRONG_VALUE) //保留中のオーダーに失敗
  if(Log_Level > LOG_LEVEL_NONE) Print("Pending order placing error");

EAプログラミングの最後のステップです。コンパイルする必要があり、戦略テスターでのパフォーマンスを分析します。

 

戦略バックテスト


この本での、コナーズとラシュキ テストの主な目的は、近年の使用戦略パフォーマンスをチェックする戦略を示しています。ソースパラメータおよび著者によって指定された毎日の時間枠は、テストに使用しました。20年前、 5 桁のクオートは一般的でなかったし、このテストはMetaQuotes デモ サーバー上で実行されました。トレーリングのパラメータが元の戦略の説明に含まれていません。

タートルスープの直近5 年間における USDJPY のテストのグラフ:

タートル スープ、USDJPY、D1、5 年


タートル スープに加えて、同じインストゥルメントの同じ履歴の間隔で同じパラメータを使用したテストグラフ:

タートル スーププラスワン、USDJPY、D1、5 年


このテストのグラフは過去 5 年間ゴールドの結果です: タートル スープ戦略。

タートル スープ、XAUUSD、D1、5 年


タートルスープ プラス 1:

タートル スーププラスワン、1 つは、XAUUSD、D1、5 年

 


直近 4 年間で原油の結果: タートル スープ戦略。

タートルスープ、オイル、D1、4 年


タートルスープ プラス 1:

タートル スーププラスワン、石油、D1、4 年


添付ファイルには、完全な結果があります。

ご自身で結論を出してみてください。コナーズとラシュキの本で説明されている戦略のいずれかのルールに対して警告します。価格がチャネルのボーダーに近づくメソッドとリミットをテストした後の動作を分析する必要があると考えています。残念なことに、詳細は提供できません。最適化に関してより良いシンボルとパラメータを選択し、他の時間枠のパラメータを調整することができます。

結論

本の通りのトレード戦略ルールをプログラム: 高確率短期トレーディング戦略-タートルスープとタートル スープ プラスワン。ラシュキとコナーズによって記述されたすべてのルールは含まれておらず、記載されているトレードの細部のいくつかは含まれません。少なくともトレードセッションの制限を考慮する必要があります。さらに、 1 つの有益なエントリを許可するトレードを制限しようとするロジックです。EAを改善したい場合は、これを行うことができます。

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

添付されたファイル |
MQL5.zip (83.14 KB)
Reports.zip (598.05 KB)
80-20 トレード戦略 80-20 トレード戦略
この記事では、80-20 トレード戦略を分析するためツール (インジケーターおよびEA) の開発について説明します。トレードルールは"ストリートスマート"より引用します。リンダラッシュクとローレンス · コナーズによる"短期的なトレード戦略”です。mql5を使用して、戦略ルールを定式化し、最近の相場のヒストリーベースで、インディケータとEAをテストします。
MQL5をプログラミングの基礎: ターミナルのグローバル変数 MQL5をプログラミングの基礎: ターミナルのグローバル変数
ターミナルのグローバル変数は、高度で信頼性の高いEAを開発するために欠かせないツールです。グローバル変数なしで MQL5で EA の開発をすることは想像を絶します。
トレーダーのライフハック: テストの比較レポート トレーダーのライフハック: テストの比較レポート
この記事では、EAを4 つの異なるトレードでテストします。4つのテストにおけるレポートの最終的な比較は、オンラインストアでの商品のような表にします。追加として、各シンボルの分布図が自動的に作成されます。
グラフィカルインタフェースX: 標準チャートコントロール(ビルド4) グラフィカルインタフェースX: 標準チャートコントロール(ビルド4)
今回は、標準のチャートコントロールについて考えていきます。これによって水平スクロールを同期させる機能を持つサブチャートの配列の作成が可能になります。また、引き続き、ライブラリのコードを最適化してCPU負荷を軽減します。