エントリを指標によって分類する技術を用いた新たな取引戦略の作成

Dmitriy Gizlyk | 22 2月, 2018


はじめに

トレーダーの100%が金融市場で安定した利益を得ることを望んでいる一方、それを達成できるのは5%だけであることはよく知られています。 

取引に勝つには、収益性の高い取引戦略が必要です。取引に関連するウェブサイトや文献には、多様な数百もの取引戦略が記述されています。シグナルの詳細な解釈はすべての指標に伴いますが、この統計は変わりません。5%は100%にも、10%にさえにもならないのですトレーディングイデオロギー派は、これは、これまでの収益性の高い戦略は市場の不安定さによって有効性を失うからだといいます。

前回の記事では、すでにエントリを指標値によって分類することについて語り、既存の戦略を改善する例を示しました。ここでは、指定された技術を使用して「空白シートで」カスタム戦略を作成することを提案します。これにより、知っている指標を「新しい目」で見たり、カスタム指標テンプレートを収集したり、そのシグナルを再検討したりすることができます。提案された技術の応用は、指標シグナルの解釈に対する独創的なアプローチをもって各ユーザが独自の戦略を作成することを可能になることを意味します。

1. テストと解析のためのモデル作成

取引端末で最初に目にするのは、継続的な価格の動きです。潜在的に、利益を得る可能性はいつ注文を出してもあります。しかし、次の瞬間にどの方向にどのくらい価格が動くかはどのように決定することができるでしょうか。トレーダーは、テクニカル分析やファンダメンタル分析でこの質問に対する答えを見つけようとします。様々なファンダメンタル分析を行うための指標が恒久的に発明され改良されています。ここでの新規性は、これらの指標シグナルの一般的なものとは異なる解釈にあります。

したがって、指標値によってエントリを分類する技術とは、指標値とポジションの比較を意味します。繰り返すと、潜在的に、利益を得る可能性はいつでもあります。これらの入力データに基づいて、各ローソク足の始めに、設定されたパラメータを有する2つの双方向ポジションを開きます。次に、各取引の利益率が指標値にどのように依存するかを分析します。

この問題を解決するには、軽度の準備作業を行います。

1.1. 仮想注文クラスの作成

私はネッティング勘定を使用しています。したがって、双方向注文を開くためには仮想注文を作成します。仮想注文は、端末(口座設定に従う)ではなく、エキスパートアドバイザーによって追跡されます。この目的のためにCDealクラスを作成します。クラスインスタンスを初期化するときには、銘柄名、ポジションの種類、約定時間と約定価格、指値と逆指値を渡します。ポジションボリュームは意図的に省略されています。なぜなら、ここではボリュームには興味がないからです。ここで重要なのは価格の動きです。したがって、損益は金額ではなくポイント単位で計算されます。

クラスへのサービスのために、ポジション状態確認関数が追加されました。

  • IsClosed — ポジションが決済されているかどうかにかかわらず論理値を返します。
  • Type - ポジションの種類を返します。
  • GetProfit — 決済されたポジションの利益を返します(損失ポジションの場合、値は負になります)。
  • GetTime — ポジションが開かれた時刻を返します。

class CDeal          :  public CObject
  {
private:
   string               s_Symbol;
   datetime             dt_OpenTime;         // ポジションが開かれた時刻
   double               d_OpenPrice;         // ポジションの約定時刻
   double               d_SL_Price;          // ポジションの逆指値
   double               d_TP_Price;          // ポジションの指値
   ENUM_POSITION_TYPE   e_Direct;            // ポジションの方向
   double               d_ClosePrice;        // ポジションの終値
   int                  i_Profit;            // ピップ単位でのポジション利益
//---
   double               d_Point;
   
public:
                     CDeal(string symbol, ENUM_POSITION_TYPE type,datetime time,double open_price,double sl_price, double tp_price);
                    ~CDeal();
   //--- 状態を確認する
   bool              IsClosed(void);
   ENUM_POSITION_TYPE Type(void)    {  return e_Direct;    }
   double            GetProfit(void);
   datetime          GetTime(void)  {  return dt_OpenTime;  }
   //---
   void              Tick(void);
  };

入ってくるティックはTick関数で処理されます。これは、ポジション決済の必要性を確認し、必要に応じて指値または逆指値テイで決済し、蓄積された利益を保存します。

void CDeal::Tick(void)
  {
   if(d_ClosePrice>0)
      return;
   double price=0;
   switch(e_Direct)
     {
      case POSITION_TYPE_BUY:
        price=SymbolInfoDouble(s_Symbol,SYMBOL_BID);
        if(d_SL_Price>0 && d_SL_Price>=price)
          {
           d_ClosePrice=price;
           i_Profit=(int)((d_ClosePrice-d_OpenPrice)/d_Point);
          }
        else
          {
           if(d_TP_Price>0 && d_TP_Price<=price)
             {
              d_ClosePrice=price;
              i_Profit=(int)((d_ClosePrice-d_OpenPrice)/d_Point);
             }
          }
        break;
      case POSITION_TYPE_SELL:
        price=SymbolInfoDouble(s_Symbol,SYMBOL_ASK);
        if(d_SL_Price>0 && d_SL_Price<=price)
          {
           d_ClosePrice=price;
           i_Profit=(int)((d_OpenPrice-d_ClosePrice)/d_Point);
          }
        else
          {
           if(d_TP_Price>0 && d_TP_Price>=price)
             {
              d_ClosePrice=price;
              i_Profit=(int)((d_OpenPrice-d_ClosePrice)/d_Point);
             }
          }
        break;
     }
  }

1.2. 指標操作クラスの作成

指標データを保存して分析するには、前の記事に詳述されているクラスを使用しました。また、ここではすべての指標クラスを一般化してCDealsToIndicatorsクラスを作成しました。これは、指標クラス配列を格納し、それらの機能を配置します。

class CDealsToIndicators
  {
private:
   CADX              *ADX[];
   CAlligator        *Alligator[];
   COneBufferArray   *OneBuffer[];
   CMACD             *MACD[];
   CStaticOneBuffer  *OneBufferStatic[];
   CStaticMACD       *MACD_Static[];
   CStaticADX        *ADX_Static[];
   CStaticAlligator  *Alligator_Static[];
   
   template<typename T>
   void              CleareArray(T *&array[]);

public:
                     CDealsToIndicators();
                    ~CDealsToIndicators();
   //---
   bool              AddADX(string symbol, ENUM_TIMEFRAMES timeframe, int period, string name);
   bool              AddADX(string symbol, ENUM_TIMEFRAMES timeframe, int period, string name, int &handle);
   bool              AddAlligator(string symbol,ENUM_TIMEFRAMES timeframe,uint jaw_period, uint jaw_shift, uint teeth_period, uint teeth_shift, uint lips_period, uint lips_shift, ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price, string name);
   bool              AddAlligator(string symbol,ENUM_TIMEFRAMES timeframe,uint jaw_period, uint jaw_shift, uint teeth_period, uint teeth_shift, uint lips_period, uint lips_shift, ENUM_MA_METHOD method, ENUM_APPLIED_PRICE price, string name, int &handle);
   bool              AddMACD(string symbol, ENUM_TIMEFRAMES timeframe, uint fast_ema, uint slow_ema, uint signal, ENUM_APPLIED_PRICE applied_price, string name);
   bool              AddMACD(string symbol, ENUM_TIMEFRAMES timeframe, uint fast_ema, uint slow_ema, uint signal, ENUM_APPLIED_PRICE applied_price, string name, int &handle);
   bool              AddOneBuffer(int handle, string name);
   //---
   bool              SaveNewValues(long ticket);
   //---
   bool              Static(CArrayObj *deals);
  };

1.3. テスト用エキスパートアドバイザーの作成

準備が整ったので、ストラテジーテスターで作動するEAの作成に進みます。まず、適用される指標のリストとそのパラメータを定義します。技術を実証するために、以下の指標を使います。

  • ADX
  • アリゲータ
  • CCI
  • チャイキン
  • 勢力指数
  • MACD

それぞれに3つのパラメータセットが作成され、3つの時間枠でデータが追跡されます。

逆指値と指値による取引はATR指標値に拘束され、利益対リスク比で設定されます。

//--- 入力パラメータ
input double            Reward_Risk    =  1.0;
input int               ATR_Period     =  288;
input ENUM_TIMEFRAMES   TimeFrame1     =  PERIOD_M5;
input ENUM_TIMEFRAMES   TimeFrame2     =  PERIOD_H1;
input ENUM_TIMEFRAMES   TimeFrame3     =  PERIOD_D1;
input string            s1                =  "ADX"                ;  //---
input uint              ADX_Period1       =  14                   ;
input uint              ADX_Period2       =  28                   ;
input uint              ADX_Period3       =  56                   ;
input string            s2                =  "Alligator"          ;  //---
input uint              JAW_Period1       =  13                   ;
input uint              JAW_Shift1        =  8                    ;
input uint              TEETH_Period1     =  8                    ;
input uint              TEETH_Shift1      =  5                    ;
input uint              LIPS_Period1      =  5                    ;
input uint              LIPS_Shift1       =  3                    ;
input uint              JAW_Period2       =  26                   ;
input uint              JAW_Shift2        =  16                   ;
input uint              TEETH_Period2     =  16                   ;
input uint              TEETH_Shift2      =  10                   ;
input uint              LIPS_Period2      =  10                   ;
input uint              LIPS_Shift2       =  6                    ;
input uint              JAW_Period3       =  42                   ;
input uint              JAW_Shift3        =  32                   ;
input uint              TEETH_Period3     =  32                   ;
input uint              TEETH_Shift3      =  20                   ;
input uint              LIPS_Period3      =  20                   ;
input uint              LIPS_Shift3       =  12                   ;
input ENUM_MA_METHOD    Alligator_Method  =  MODE_SMMA            ;
input ENUM_APPLIED_PRICE Alligator_Price  =  PRICE_MEDIAN         ;
input string            s5                =  "CCI"                ;  //---
input uint              CCI_Period1       =  14                   ;
input uint              CCI_Period2       =  28                   ;
input uint              CCI_Period3       =  56                   ;
input ENUM_APPLIED_PRICE CCI_Price        =  PRICE_TYPICAL        ;
input string            s6                =  "Chaikin"            ;  //---
input uint              Ch_Fast_Period1   =  3                    ;
input uint              Ch_Slow_Period1   =  14                   ;
input uint              Ch_Fast_Period2   =  6                    ;
input uint              Ch_Slow_Period2   =  28                   ;
input uint              Ch_Fast_Period3   =  12                   ;
input uint              Ch_Slow_Period3   =  56                   ;
input ENUM_MA_METHOD    Ch_Method         =  MODE_EMA             ;
input ENUM_APPLIED_VOLUME Ch_Volume       =  VOLUME_TICK          ;
input string            s7                =  "Force Index"        ;  //---
input uint              Force_Period1     =  14                   ;
input uint              Force_Period2     =  28                   ;
input uint              Force_Period3     =  56                   ;
input ENUM_MA_METHOD    Force_Method      =  MODE_SMA             ;
input ENUM_APPLIED_VOLUME Force_Volume    =  VOLUME_TICK          ;
input string            s8                =  "MACD"               ;  //---
input uint              MACD_Fast1        =  12                   ;
input uint              MACD_Slow1        =  26                   ;
input uint              MACD_Signal1      =  9                    ;
input uint              MACD_Fast2        =  24                   ;
input uint              MACD_Slow2        =  52                   ;
input uint              MACD_Signal2      =  18                   ;
input uint              MACD_Fast3        =  48                   ;
input uint              MACD_Slow3        =  104                  ;
input uint              MACD_Signal3      =  36                   ;
input ENUM_APPLIED_PRICE MACD_Price       =  PRICE_CLOSE          ;

グローバル変数ブロックで下記を宣言します。

  • Dealsクラスを格納するための配列取引
  • 指標で動作するクラスインスタンスIndicatorsStatic
  • ATR指標ハンドルを格納するための変数
  • 最後に処理されたバーの時刻(last_bar)と最後に決済された注文(last_closed_deal)を格納する2つのサービス変数。後者では、それぞれのティックですでに決済されたポジションを渡さないようにする必要がある。

OnInit関数では、グローバル変数と必要な指標クラスの初期化を実行します。

int OnInit()
  {
//---
   last_bar=0;
   last_closed_deal=0;
//---
   Deals =  new CArrayObj();
   if(CheckPointer(Deals)==POINTER_INVALID)
      return INIT_FAILED;
//---
   IndicatorsStatic  =  new CDealsToIndicators();
   if(CheckPointer(IndicatorsStatic)==POINTER_INVALID)
      return INIT_FAILED;
//---
   atr=iATR(_Symbol,TimeFrame1,ATR_Period);
   if(atr==INVALID_HANDLE)
      return INIT_FAILED;
//---
   AddIndicators(TimeFrame1);
   AddIndicators(TimeFrame2);
   AddIndicators(TimeFrame3);
//---
   return(INIT_SUCCEEDED);
  }

3つの異なる時間枠で同じ指標セットを使用します。これが指標クラスの初期化を別の関数AddIndicatorsに置くことが賢明な理由です。そのパラメータには、必要な時間枠が指定されます。

bool AddIndicators(ENUM_TIMEFRAMES timeframe)
  {
   if(CheckPointer(IndicatorsStatic)==POINTER_INVALID)
     {
      IndicatorsStatic  =  new CDealsToIndicators();
      if(CheckPointer(IndicatorsStatic)==POINTER_INVALID)
         return false;
     }
   string tf_name=StringSubstr(EnumToString(timeframe),7);
   string name="ADX("+IntegerToString(ADX_Period1)+") "+tf_name;
   if(!IndicatorsStatic.AddADX(_Symbol, timeframe, ADX_Period1, name))
      return false;
   name="ADX("+IntegerToString(ADX_Period2)+") "+tf_name;
   if(!IndicatorsStatic.AddADX(_Symbol, timeframe, ADX_Period2, name))
      return false;
   name="ADX("+IntegerToString(ADX_Period3)+") "+tf_name;
   if(!IndicatorsStatic.AddADX(_Symbol, timeframe, ADX_Period3, name))
      return false;
   name="Alligator("+IntegerToString(JAW_Period1)+","+IntegerToString(TEETH_Period1)+","+IntegerToString(LIPS_Period1)+") "+tf_name;
   if(!IndicatorsStatic.AddAlligator(_Symbol, timeframe, JAW_Period1, JAW_Shift1, TEETH_Period1, TEETH_Shift1, LIPS_Period1, LIPS_Shift1, Alligator_Method, Alligator_Price, name))
      return false;
   name="Alligator("+IntegerToString(JAW_Period2)+","+IntegerToString(TEETH_Period2)+","+IntegerToString(LIPS_Period2)+") "+tf_name;
   if(!IndicatorsStatic.AddAlligator(_Symbol, timeframe, JAW_Period2, JAW_Shift2, TEETH_Period2, TEETH_Shift2, LIPS_Period2, LIPS_Shift2, Alligator_Method, Alligator_Price, name))
      return false;
   name="Alligator("+IntegerToString(JAW_Period3)+","+IntegerToString(TEETH_Period3)+","+IntegerToString(LIPS_Period3)+") "+tf_name;
   if(!IndicatorsStatic.AddAlligator(_Symbol, timeframe, JAW_Period3, JAW_Shift3, TEETH_Period3, TEETH_Shift3, LIPS_Period3, LIPS_Shift3, Alligator_Method, Alligator_Price, name))
      return false;
   name="MACD("+IntegerToString(MACD_Fast1)+","+IntegerToString(MACD_Slow1)+","+IntegerToString(MACD_Signal1)+") "+tf_name;
   if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast1, MACD_Slow1, MACD_Signal1, MACD_Price, name))
      return false;
   name="MACD("+IntegerToString(MACD_Fast2)+","+IntegerToString(MACD_Slow2)+","+IntegerToString(MACD_Signal2)+") "+tf_name;
   if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast2, MACD_Slow2, MACD_Signal2, MACD_Price, name))
      return false;
   name="MACD("+IntegerToString(MACD_Fast3)+","+IntegerToString(MACD_Slow3)+","+IntegerToString(MACD_Signal3)+") "+tf_name;
   if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast3, MACD_Slow3, MACD_Signal3, MACD_Price, name))
      return false;
   name="CCI("+IntegerToString(CCI_Period1)+") "+tf_name;
   int handle = iCCI(_Symbol, timeframe, CCI_Period1, CCI_Price);
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   name="CCI("+IntegerToString(CCI_Period2)+") "+tf_name;
   handle = iCCI(_Symbol, timeframe, CCI_Period2, CCI_Price);
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   handle = iCCI(_Symbol, timeframe, CCI_Period3, CCI_Price);
   name="CCI("+IntegerToString(CCI_Period3)+") "+tf_name;
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   handle = iForce(_Symbol, timeframe, Force_Period1, Force_Method, Force_Volume);
   name="Force("+IntegerToString(Force_Period1)+") "+tf_name;
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   handle = iForce(_Symbol, timeframe, Force_Period2, Force_Method, Force_Volume);
   name="Force("+IntegerToString(Force_Period2)+") "+tf_name;
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   handle = iForce(_Symbol, timeframe, Force_Period3, Force_Method, Force_Volume);
   name="Force("+IntegerToString(Force_Period3)+") "+tf_name;
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   name="CHO("+IntegerToString(Ch_Slow_Period1)+","+IntegerToString(Ch_Fast_Period1)+") "+tf_name;
   handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period1, Ch_Slow_Period1, Ch_Method, Ch_Volume);
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period2, Ch_Slow_Period2, Ch_Method, Ch_Volume);
   name="CHO("+IntegerToString(Ch_Slow_Period2)+","+IntegerToString(Ch_Fast_Period2)+") "+tf_name;
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period3, Ch_Slow_Period3, Ch_Method, Ch_Volume);
   name="CHO("+IntegerToString(Ch_Slow_Period3)+","+IntegerToString(Ch_Fast_Period3)+") "+tf_name;
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   return true;
  }

OnTickで行われる操作は、ポジションを確認するブロックと新しいポジションを開くブロックの2つに分けられます。

最初の操作ブロックは各ティックで実行されます。その中で、以前に開かれたすべての取引が結果的に配列から検索され、それぞれに対してTick関数が呼び出されます。ポジションの指値と逆指値のトリガーをチェックして、必要に応じて現在の価格で注文を決済し、利益を保存します。以前に決済された取引を再び確認しないために、変数last_closed_dealに最初の決済トランザクションより前の取引の番号が保存されます。 

void OnTick()
  {
//---
   int total=Deals.Total();
   CDeal *deal;
   bool found=false;
   for(int i=last_closed_deal;i<total;i++)
     {
      deal  =  Deals.At(i);
      if(CheckPointer(deal)==POINTER_INVALID)
         continue;
      if(!found)
        {
         if(deal.IsClosed())
           {
            last_closed_deal=i;
            continue;
           }
         else
            found=true;
        }
      deal.Tick();
     }

2番目の操作ブロックは、新しいバーが発生したかどうかの確認から始まります。各バーの初めに、最後に閉じたローソク足でのATR指標値をダウンロードし、設定されたパラメータに従って指値と逆指値を計算し、仮想ポジションを開きます。各ポジションで、この指標操作クラスのSaveNewValues関数を呼び出した後に指標データを保存します。

//---
   datetime cur_bar=(datetime)SeriesInfoInteger(_Symbol,PERIOD_CURRENT,SERIES_LASTBAR_DATE);
   datetime cur_time=TimeCurrent();
   if(cur_bar==last_bar || (cur_time-cur_bar)>10)
      return;
   double atrs[];
   if(CopyBuffer(atr,0,1,1,atrs)<=0)
      return;

   last_bar=cur_bar;
   double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
   double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
   double stops=MathMax(2*atrs[0],SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point);
   double sl=NormalizeDouble(stops,_Digits);
   double tp=NormalizeDouble(Reward_Risk*(stops+ask-bid),_Digits);
   deal  =  new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp);
   if(CheckPointer(deal)!=POINTER_INVALID)
      if(Deals.Add(deal))
         IndicatorsStatic.SaveNewValues(Deals.Total()-1);
   deal  =  new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp);
   if(CheckPointer(deal)!=POINTER_INVALID)
      if(Deals.Add(deal))
         IndicatorsStatic.SaveNewValues(Deals.Total()-1);
   return;
  }

OnTesterでは、テストの実行結果を収集し、分析用のチャートを作成します。この目的のために、指標を扱うためのクラスの静的関数を呼び出します。

必ずOnDeinit関数でメモリをクリーンしてください。

EAの完全なコードと使用されるクラスは添付ファイルで提供されています。

2. テスト結果の分析

これで、テスト用EAが作成されました。今度は、分析期間について考えてみましょう。期間を選択するときは、分析の客観性を提供するのに十分な時間が必要であることを考慮しなければなりません。期間に対するもう一つの要件は、単方向の動きだけでなく、双方向のトレンドの動きと横方向の(平らな)動きを含んでいるということです。このようなアプローチは、あらゆる動きに対しても利益を生み出すことができる取引戦略の作成を可能にします。私の例では、2011年1月1日から2011年10月1日までの通貨ペアEURUSDを分析しています。

テスト期間テストパラメータ

これまでのところ、プロセスには反復的特徴があるので、EAをテストするために必要なすべてのパラメータを設定した後で、今後の作業のためにパラメータセットファイルを保存するようお勧めします。

各テスト段階は1/1と15/1に等しい利益/リスク比で2回実行されます。1回目の実行では方向付けられた動きの確率、2番目のの実行では移動力を評価します。

EAは分析のために多数のチャートを表示しているため、本稿では全部は提供されていません。レポートはすべて添付ファイルで提供されています。ここでは、新しい戦略における指標の使用に関する決定が行われたチャートのみを示します。

2.1. 第1段階

期待通り、テストの第1段階では、確実な収益性のある領域は示されませんでした。しかし、同時に、勢力指数指標には注意を払うべきです。M5時間枠では、取引利益の指標値への依存度のチャートは、ゼロエリアでは鈍化しています。この観察点の重要性は、この現象が、テストに使用されるすべてのパラメータで分析指標チャートに現れるという事実によって証明されます。テンプレートでは、この現象が最も顕著な(最大ドローダウン)パラメータを選択します。

時間枠M5における期間56の勢力指標の分析図

分析されたチャートを拡大しましょう。ご覧のとおり、この効果は、-0.01から0.01の範囲にあります。観察された現象は、売り取引と買い取引の両方に同様に当てはまります。

この観察は、範囲内の値にボラティリティがないことによって説明することができるかもしれません。ここでの戦略のためには、この範囲内では注文を開かないようにしてください。

ゼロマークに近い勢力指数値に対する利益依存

EAをこのフィルタに追加します。これを行うには、指標ハンドルを格納するためのグローバル変数を追加します。

int                  force;

フィルタとして必要な指標が既にEAに適用されている限り、再びチャートには添付しません。そのハンドルをAddIndicatorsでグローバル変数にコピーするだけです。しかし、この関数は指標の初期化のために3回呼び出されることを覚えておいてください。したがって、指標ハンドルをコピーする前に、時間枠の一致を確認する必要があります。

   handle = iForce(_Symbol, timeframe, Force_Period3, Force_Method, Force_Volume);
   if(timeframe==TimeFrame1)
      force=handle;

そして、直後にフィルタをOnTickに追加します。チャートを作成するとき、分析チャート構築関数では、データは四捨五入されていることを覚えておいてください。したがって、取引を絞り込む際には、指標値もまた予備の四捨五入する必要があります。

   double atrs[];
   double force_data[];
   if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0)
      return;      // 指標データ読み込みにおけるエラー

   last_bar=cur_bar;
   double d_Step=_Point*1000;
   if(MathAbs(NormalizeDouble(force_data[0]/d_Step,0)*d_Step)<=0.01)
      return;    // 勢力指数による絞り込み

本稿には完全なEAコードが添付されています。

EAにフィルタを追加した後は、テストの第2段階を実装します。テストする前に、以前に保存したパラメータをダウンロードしてください。

2.2. 第2段階

EAテストを繰り返した後、MACD指標に注意を払いました。収益性の高い領域がチャートに現れました。

MACDヒストグラム値に依存する利益のチャート

収益/リスクの比率が15/1のチャートでは、これらの領域がよりよく表現されています。 これらの範囲内に潜在的なシグナルがあるかもしれません。

MACDヒストグラム値(利益/リスク= 15/1)に対する利益のチャート

このフィルタをEAコードにも追加します。フィルタ追加の論理は、第1段階の説明で提供される論理に類似しています。

グローバル変数:

int                  macd;

AddIndicators関数:

   name="MACD("+IntegerToString(MACD_Fast2)+","+IntegerToString(MACD_Slow2)+","+IntegerToString(MACD_Signal2)+") "+tf_name;
   if(timeframe==TimeFrame1)
     {
      if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast2, MACD_Slow2, MACD_Signal2, MACD_Price, name, macd))
         return false;
     }
   else
     {
      if(!IndicatorsStatic.AddMACD(_Symbol, timeframe, MACD_Fast2, MACD_Slow2, MACD_Signal2, MACD_Price, name))
         return false;
     }

OnTick:

   double macd_data[];
   if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0 || CopyBuffer(macd,0,1,1,macd_data)<=0)
      return;

そして

   double macd_Step=_Point*50;
   macd_data[0]=NormalizeDouble(macd_data[0]/macd_Step,0)*macd_Step;
   if(macd_data[0]>=0.0015 && macd_data[0]<=0.0035)
     {
      deal  =  new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp);
      if(CheckPointer(deal)!=POINTER_INVALID)
         if(Deals.Add(deal))
            IndicatorsStatic.SaveNewValues(Deals.Total()-1);
     }
   if(macd_data[0]<=(-0.0015) && macd_data[0]>=(-0.0035))
     {
      deal  =  new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp);
      if(CheckPointer(deal)!=POINTER_INVALID)
         if(Deals.Add(deal))
            IndicatorsStatic.SaveNewValues(Deals.Total()-1);
     }

フィルタを追加したらテストの第3段階に移ります。

2.3. 第3段階

第3段階では、チャイキンオシレータに注意を払いました。時間枠D1のオシレータ解析チャートでは、指標値の成長時にはショートポジションで利益が伸びる一方で、指標値の下落時にはロングポジションで利益成長が見られます。

チャイキンオシレータ値に対する利益依存性

利益率/リスク率が15/1のチャートの分析でもこのこの所見が確認されています。

チャイキンオシレータ値に対する利益依存性

この所見をEAコードに追加します。

グローバル変数:

int                  cho;

AddIndicators関数:

   handle = iChaikin(_Symbol, timeframe, Ch_Fast_Period2, Ch_Slow_Period2, Ch_Method, Ch_Volume);
   name="CHO("+IntegerToString(Ch_Slow_Period2)+","+IntegerToString(Ch_Fast_Period2)+") "+tf_name;
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   if(timeframe==TimeFrame3)
      cho=handle;

OnTick:

   double cho_data[];
   if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0 || CopyBuffer(macd,0,1,1,macd_data)<=0
      || CopyBuffer(cho,0,1,2,cho_data)<2)
      return;

そして

   if(macd_data[0]>=0.0015 && macd_data[0]<=0.0035 && (cho_data[1]-cho_data[0])<0)
     {
      deal  =  new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp);
      if(CheckPointer(deal)!=POINTER_INVALID)
         if(Deals.Add(deal))
            IndicatorsStatic.SaveNewValues(Deals.Total()-1);
     }
   if(macd_data[0]<=(-0.0015) && macd_data[0]>=(-0.0035) && (cho_data[1]-cho_data[0])>0)
     {
      deal  =  new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp);
      if(CheckPointer(deal)!=POINTER_INVALID)
         if(Deals.Add(deal))
            IndicatorsStatic.SaveNewValues(Deals.Total()-1);
     }

次のステージに移ります。

2.4. 第4段階

さらにEAを再びテストした後で、私は再度時間枠D1に注目しました。今回考察したのはCCI指標です。その分析チャートは、指標値の下落時にはショートポジションで利益成長が見られ、指標値の成長時にはロングポジションで利益が伸びることを実証しました。この傾向は、調査した3期間すべてで観察されましたが、最大利益はこのオシレータの標準である期間14を使用した場合に到達されました。

利益のCCI指標値への依存

利益/リスク率が15/1のテストで得られた分析チャートは、私たちの所見を裏付けています。

利益のCCI指標値への依存

この所見をEAコードのテストにも追加します。

グローバル変数:

int                  cci;

AddIndicators:

   name="CCI("+IntegerToString(CCI_Period1)+") "+tf_name;
   int handle = iCCI(_Symbol, timeframe, CCI_Period1, CCI_Price);
   if(handle<0 || !IndicatorsStatic.AddOneBuffer(handle, name) )
      return false;
   if(timeframe==TimeFrame3)
      cci=handle;

OnTick:

   double cci_data[];
   if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0 || CopyBuffer(macd,0,1,1,macd_data)<=0
      || CopyBuffer(cho,0,1,2,cho_data)<2 || CopyBuffer(cci,0,1,2,cci_data)<2)
      return;

そして

   if(macd_data[0]>=0.0015 && macd_data[0]<=0.0035 && (cho_data[1]-cho_data[0])<0 && (cci_data[1]-cci_data[0])>0)
     {
      deal  =  new CDeal(_Symbol,POSITION_TYPE_BUY,TimeCurrent(),ask,bid-sl,ask+tp);
      if(CheckPointer(deal)!=POINTER_INVALID)
         if(Deals.Add(deal))
            IndicatorsStatic.SaveNewValues(Deals.Total()-1);
     }
   if(macd_data[0]<=(-0.0015) && macd_data[0]>=(-0.0035) && (cho_data[1]-cho_data[0])>0 && (cci_data[1]-cci_data[0])<0)
     {
      deal  =  new CDeal(_Symbol,POSITION_TYPE_SELL,TimeCurrent(),bid,ask+sl,bid-tp);
      if(CheckPointer(deal)!=POINTER_INVALID)
         if(Deals.Add(deal))
            IndicatorsStatic.SaveNewValues(Deals.Total()-1);
     }

全段階でのEAの完全なコードは添付ファイルで提供されています。

3. 選択されたシグナルに対するEAの作成とテスト

完璧さに限界はありません。戦略利益要因を増やすためにフィルタの分析と追加を続けることができるでしょう。しかし、私はこの4段階が技術の実証に十分であると信じています。次のステップでは、テスターで戦略を確認するための簡単なEAを作成します。これにより、収益要因と戦略の縮小、ならびに進行中のバランス変動を評価することが可能になります。

この戦略では、取引に関する意思決定には4つの指標を用い、逆指値と指値の設定にはATR指標を用いました。したがって、EA入力パラメータでは、指標に必要なすべての入力情報を設定する必要があります。この段階では資金管理はしません。すべての注文は一定量のボリュームを使用します。

//--- 入力パラメータ
input double            Lot               =  0.1                  ;
input double            Reward_Risk       =  15.0                 ;
input ENUM_TIMEFRAMES   ATR_TimeFrame     =  PERIOD_M5            ;
input int               ATR_Period        =  288                  ;
input string            s1                =  "CCI"                ;  //---
input ENUM_TIMEFRAMES   CCI_TimeFrame     =  PERIOD_D1            ;
input uint              CCI_Period        =  14                   ;
input ENUM_APPLIED_PRICE CCI_Price        =  PRICE_TYPICAL        ;
input string            s2                =  "Chaikin"            ;  //---
input ENUM_TIMEFRAMES   Ch_TimeFrame      =  PERIOD_D1            ;
input uint              Ch_Fast_Period    =  6                    ;
input uint              Ch_Slow_Period    =  28                   ;
input ENUM_MA_METHOD    Ch_Method         =  MODE_EMA             ;
input ENUM_APPLIED_VOLUME Ch_Volume       =  VOLUME_TICK          ;
input string            s3                =  "Force Index"        ;  //---
input ENUM_TIMEFRAMES   Force_TimeFrame   =  PERIOD_M5            ;
input uint              Force_Period      =  56                   ;
input ENUM_MA_METHOD    Force_Method      =  MODE_SMA             ;
input ENUM_APPLIED_VOLUME Force_Volume    =  VOLUME_TICK          ;
input string            s4                =  "MACD"               ;  //---
input ENUM_TIMEFRAMES   MACD_TimeFrame    =  PERIOD_M5            ;
input uint              MACD_Fast         =  12                   ;
input uint              MACD_Slow         =  26                   ;
input uint              MACD_Signal       =  9                    ;
input ENUM_APPLIED_PRICE MACD_Price       =  PRICE_CLOSE          ;

グローバル変数で下記を宣言します。

  • 取引操作を実行するためのクラスインスタンス
  • 使用される指標ハンドルを格納するための変数
  • 最後に処理されたバーおよび最後の取引の日付を記録するための補助変数
  • 最大および最小時間枠を格納するための変数

OnInit関数では、指標を初期化し、変数の初期値を設定します。

int OnInit()
  {
//---
   last_bar=0;
   last_deal=0;
//---
   atr=iATR(_Symbol,ATR_TimeFrame,ATR_Period);
   if(atr==INVALID_HANDLE)
      return INIT_FAILED;
//---
   force=iForce(_Symbol,Force_TimeFrame,Force_Period,Force_Method,Force_Volume);
   if(force==INVALID_HANDLE)
      return INIT_FAILED;
//---
   macd=iMACD(_Symbol,MACD_TimeFrame,MACD_Fast,MACD_Slow,MACD_Signal,MACD_Price);
   if(macd==INVALID_HANDLE)
      return INIT_FAILED;
//---
   cho=iChaikin(_Symbol,Ch_TimeFrame,Ch_Fast_Period,Ch_Slow_Period,Ch_Method,Ch_Volume);
   if(cho==INVALID_HANDLE)
      return INIT_FAILED;
//---
   cci=iCCI(_Symbol,CCI_TimeFrame,CCI_Period,CCI_Price);
   if(cci==INVALID_HANDLE)
      return INIT_FAILED;
//---
   MaxPeriod=fmax(Force_TimeFrame,MACD_TimeFrame);
   MaxPeriod=fmax(MaxPeriod,Ch_TimeFrame);
   MaxPeriod=fmax(MaxPeriod,CCI_TimeFrame);
   MinPeriod=fmin(Force_TimeFrame,MACD_TimeFrame);
   MinPeriod=fmin(MinPeriod,Ch_TimeFrame);
   MinPeriod=fmin(MinPeriod,CCI_TimeFrame);
//---
   return(INIT_SUCCEEDED);
  }

使用した指標をOnDeinit関数で閉じます。

void OnDeinit(const int reason)
  {
//---
   if(atr!=INVALID_HANDLE)
      IndicatorRelease(atr);
//---
   if(force==INVALID_HANDLE)
      IndicatorRelease(force);
//---
   if(macd==INVALID_HANDLE)
      IndicatorRelease(macd);
//---
   if(cho==INVALID_HANDLE)
      IndicatorRelease(cho);
//---
   if(cci==INVALID_HANDLE)
      IndicatorRelease(cci);
  }

主要アクションはOnTickでとられます。関数の初めに、新しいバーの出現を確認します。新しいポジションは、最小時間枠(バーが開いてから10秒に制限)で新しいバーが開かれたときにのみ開き、現在のバー内の最大時間枠でのポジションが開かれていない限り開きません。このようにして、私は1つのシグナルに対して1つの注文を出すように制限しました。

void OnTick()
  {
//---
   datetime cur_bar=(datetime)SeriesInfoInteger(_Symbol,MinPeriod,SERIES_LASTBAR_DATE);
   datetime cur_max=(datetime)SeriesInfoInteger(_Symbol,MaxPeriod,SERIES_LASTBAR_DATE);
   datetime cur_time=TimeCurrent();
   if(cur_bar<=last_bar || (cur_time-cur_bar)>10 || cur_max<=last_deal)
      return;

さらに、使用された指標のデータを入手します。1つの指標にでもデータ受信エラーが発生した場合、関数を終了します。

   last_bar=cur_bar;
   double atrs[];
   double force_data[];
   double macd_data[];
   double cho_data[];
   double cci_data[];
   if(CopyBuffer(atr,0,1,1,atrs)<=0 || CopyBuffer(force,0,1,1,force_data)<=0 || CopyBuffer(macd,0,1,1,macd_data)<=0
      || CopyBuffer(cho,0,1,2,cho_data)<2 || CopyBuffer(cci,0,1,2,cci_data)<2)
     {
      return;
     }

次に、戦略に沿うために、勢力指数値を確認します。フィルタに合致しない場合は、次のバーが開くまで関数を終了します。

   double force_Step=_Point*1000;
   if(MathAbs(NormalizeDouble(force_data[0]/force_Step,0)*force_Step)<=0.01)
      return;

次の段階で、ロングポジションを開くシグナルを確認します。正のシグナルがある場合は、既にポジションがあるかどうかを確認します。もしあって、それがショートなら、決済します。損失を持つロングポジションが既に開いている場合は、シグナルを無視して関数を終了します。

その後、新しいポジションのパラメータを計算し、注文を送信します。

ショートポジションでも同じ操作を実行します。

   double macd_Step=_Point*50;
   macd_data[0]=NormalizeDouble(macd_data[0]/macd_Step,0)*macd_Step;
   if(macd_data[0]>=0.0015 && macd_data[0]<=0.0035 && (cho_data[1]-cho_data[0])<0 && (cci_data[1]-cci_data[0])>0)
     {
      if(PositionSelect(_Symbol))
        {
         switch((int)PositionGetInteger(POSITION_TYPE))
           {
            case POSITION_TYPE_BUY:
              if(PositionGetDouble(POSITION_PROFIT)<=0)
                 return;
              break;
            case POSITION_TYPE_SELL:
              Trade.PositionClose(_Symbol);
              break;
           }
        }
      last_deal=cur_max;
      double stops=MathMax(2*atrs[0],SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point);
      double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
      double sl=NormalizeDouble(stops,_Digits);
      double tp=NormalizeDouble(Reward_Risk*(stops+ask-bid),_Digits);
      double SL=NormalizeDouble(bid-sl,_Digits);
      double TP=NormalizeDouble(ask+tp,_Digits);
      if(!Trade.Buy(Lot,_Symbol,ask,SL,TP,"New Strategy"))
         Print("Error of open BUY ORDER "+Trade.ResultComment());
     }
   if(macd_data[0]<=(-0.0015) && macd_data[0]>=(-0.0035) && (cho_data[1]-cho_data[0])>0 && (cci_data[1]-cci_data[0])<0)
     {
      if(PositionSelect(_Symbol))
        {
         switch((int)PositionGetInteger(POSITION_TYPE))
           {
            case POSITION_TYPE_SELL:
              if(PositionGetDouble(POSITION_PROFIT)<=0)
                 return;
              break;
            case POSITION_TYPE_BUY:
              Trade.PositionClose(_Symbol);
              break;
           }
        }
      last_deal=cur_max;
      double stops=MathMax(2*atrs[0],SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point);
      double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID);
      double sl=NormalizeDouble(stops,_Digits);
      double tp=NormalizeDouble(Reward_Risk*(stops+ask-bid),_Digits);
      double SL=NormalizeDouble(ask+sl,_Digits);
      double TP=NormalizeDouble(bid-tp,_Digits);
      if(!Trade.Sell(Lot,_Symbol,bid,SL,TP,"New Strategy"))
         Print("Error of open SELL ORDER "+Trade.ResultComment());
     }
   return;
  }

EAの完全なコードは添付ファイルで提供されています。

EAを実行した後、戦略をテストすることができます。「戦略を期間に合わせる」ことから抽象化するために、テスト期間を延長して、2015年1月1日から2017年1月12日まで戦略をテストします。テストの初期資本は10,000ドル、取引サイズは1ロットです。

戦略のテスト 

テスト結果によると、EAは74.8%の利益を示しました。最高残高ドローダウンは12.4%、株式23.8%です。合計44の取引(22ショートポジションと22ロングポジション)が行われました。収益性の高いポジションのシェアは18.2%を占め、ショートおよびロングポジションで同じです。このような収益性の低いポジションの割合は、リスクに対する期待利益率が高い (15:1) ことを前提としており、さらなる戦略改善の余地があります。

戦略テストの結果

終わりに

本稿では、指標値によってエントリを分別する方法を使用して「ブランクシートを使用した」取引戦略作成技術を示します。結果として得られる戦略が長期間で利益を生み出すことができることは3年間のテストによって証明されています。標準的なMetaTraderパッケージから戦略指標を作成すると、取引を行うためのシグナルは、取られた指標の文献に記載されたものとはまったく異なるという事実にもかかわらず、提案された技術は、取引戦略において指標を使用する独創的なアプローチを可能にし、使われた指標によって制限されません。シグナルの品質を評価するために、ユーザー指標とバリアントの使用が可能です。

参照文献

  1. 指標値によるエントリの分解
  2. HTML形式でのチャートおよび図

以下は本稿で使用されているプログラムです。

#
 名称
種類 
説明 
  New_Strategy_Gizlyk.zip    
1 NewStrategy1.mq5  EA  戦略作成の第1段階を実行するためのEA
 2 NewStrategy2.mq5   EA 戦略作成の第2段階を実行するためのEA
 3 NewStrategy3.mq5   EA  戦略作成の第3段階を実行するためのEA 
 4 NewStrategy4.mq5   EA
 戦略作成の第4段階を実行するためのEA 
 5 NewStrategy_Final.mq5  EA
 戦略テストEA
6 DealsToIndicators.mqh  クラスライブラリ  指標クラス操作のためのクラス
7 Deal.mqh   クラスライブラリ  取引についての情報を保存するクラス
8 Value.mqh   クラスライブラリ  指標バッファ状態データを保存するクラス
9 OneBufferArray.mqh  クラスライブラリ  1バッファ指標データ履歴を保存するクラス
10 StaticOneBuffer.mqh  クラスライブラリ  1バッファ指標統計の収集と分析のためのクラス
11 ADXValue.mqh  クラスライブラリ  ADX指標状態データを保存するクラス
12 ADX.mqh  クラスライブラリ  ADX指標データ履歴を保存するクラス
13 StaticADX.mqh  クラスライブラリ  ADX指標統計の収集および分析のためのクラス
14 AlligatorValue.mqh  クラスライブラリ  アリゲータ指標状態データを保存するクラス
15 Alligator.mqh  クラスライブラリ  アリゲータ指標データ履歴を保存するクラス
16 StaticAlligator.mqh  クラスライブラリ  アリゲータ指標統計の収集および分析のためのクラス
17 MACDValue.mqh  クラスライブラリ  MACD指標状態データを保存するクラス
18 MACD.mqh  クラスライブラリ  MACD指標データ履歴を保存するクラス
19 StaticMACD.mqh  クラスライブラリ  MACD指標統計の収集および分析のためのクラス
   Common.zip    
20  NewStrategy1_Report_1to1_2016-17.html  インターネットファイル  戦略作成の第1段階の分析チャート(利益/リスク = 1/1)
21  NewStrategy1_Report_15to1_2016-17.html  インターネットファイル  戦略作成の第1段階の分析チャート(利益/リスク = 15/1)
22  NewStrategy2_Report_1to1_2016-17.html   インターネットファイル  戦略作成の第2段階の分析チャート(利益/リスク = 1/1)
23  NewStrategy2_Report_15to1_2016-17.html  インターネットファイル  戦略作成の第2段階の分析チャート(利益/リスク = 15/1)
24  NewStrategy3_Report_1to1_2016-17.html   インターネットファイル  戦略作成の第3段階の分析チャート(利益/リスク = 1/1)
25  NewStrategy3_Report_15to1_2016-17.html   インターネットファイル  戦略作成の第3段階の分析チャート(利益/リスク = 15/1)
26  NewStrategy4_Report_1to1_2016-17.html   インターネットファイル  戦略作成の第4段階の分析チャート(利益/リスク = 1/1)
27  NewStrategy4_Report_15to1_2016-17.html   インターネットファイル  戦略作成の第4段階の分析チャート(利益/リスク = 15/1)
28  NewStrategy_Final_Report.html  インターネットファイル  戦略テスト報告