考えられる.EAをリアルタイムで最適化するためのインジケータの使用法

6 11月 2018, 09:12
Dmitriy Gizlyk
0
77

コンテンツ

イントロダクション

我々はEAで起動するたびに、最適なパラメータを選択する問題に直面します。 そのパラメータを見つけるために、ヒストリーデータでストラテジを最適化します。 しかし、周知のように、相場にはある一定の動きがあります。 時間が経過すると、選択したパラメータの優位性が失われます。

したがって、EA の再最適化が必要です。 このサイクルは一定です。 すべてのユーザーが自分自身で再最適化のタイミングを決定する必要があります。 しかし、そのプロセスを自動化することは不可能でしょうか。 解決策は何でしょうか。 おそらく、カスタム構成ファイルを使用してターミナルを実行することによって、標準ストラテジーテスターのプログラム制御を考えたことがあるかもしれません。 今回は、型破りなアプローチを提供し、インジケータにテスターの機能を割り当てたいと思います。

1. アイディア

もちろん、このインジケータは決してストラテジーテスターではありません。 では、EA の最適化にどのように役立つことができるでしょうか。 このアイデアは、インジケータに EA の操作ロジックを実装し、リアルタイムで仮想トレードの収益性を追跡することにあります。 ストラテジーテスタで最適化を行う場合、指定されたパラメータに対して一連のテストを繰り返し行います。 ストラテジーテスターのパスと同様の異なるパラメータを持つ単一のインジケータの複数のインスタンスを同時に起動することによって、同じことを行います。 決定を下す際には、EAはインジケータを起動し、最適なパラメータを選択します。

なぜこのホイールを再発明するか疑問に思うかもしれません。 長所と短所を分析してみましょう。 確かに、このアプローチの主な利点は、ほとんどリアルタイムの条件で EA の最適化です。 2番目の利点は、テストがブローカーのリアルティックで実行されるということです。 一方で、統計データが収集されるまで待たなければならないので、リアルタイムでのテストは巨大な欠点があります。 もう一つの利点は、インジケータは全体のヒストリーを再計算せず現在のティックだけ再計算しますが、ストラテジーテスターの場合は最初からヒストリーに沿って実行されます。 このアプローチは、適切なタイミングで高速な最適化を提供します。 したがって、ほとんどすべての足で最適化を行うことができます。

このアプローチの短所は、テストのティックヒストリーの欠如があります。 もちろん、 CopyTicks またはCopyTicksRangeを使用することができます。 しかし、ティックのヒストリーをダウンロードする時間が必要であり、膨大なデータ量の再計算、コンピューティングパワーと時間が必要です。 メタトレーダー5上で単一のシンボルのタスクで稼働させるインジケータを使用することを忘れないようにしましょう。 したがって、ここでは別の制限があります-あまりにも多くのインジケータは、ターミナルが遅くなる可能性があります。

説明された欠点のリスクを最小限に抑えるには、次の仮定を行います。

  1. テスターインジケーターを初期化する場合、ヒストリーはM1 OHLC 価格によって計算されます。 オーダーの利益/損失を計算するときに、SL は最初の高値/安値 (オーダーの種類に応じて) によって TP に続いてチェックされます。
  2. ポイント1に続いて、オーダーはロウソクの始値でのみ開かれています。
  3. 実行中のテストインジケータの合計数を減らすには、意味のあるメソッドを適用して、使用するパラメータを選択します。 ここでは、インジケータロジックに従って、最小ステップとフィルタパラメータを追加できます。 たとえば、MACD を使用している間に、高速および低速の MA のパラメータ範囲が重なっている場合、テスターインジケータは、EA の操作ロジックと矛盾するため、低速 MAの期間が高速MA1に満たないパラメータのセットに対しては起動されません。 また、最初に多くのダマシシグナルを含むオプションを破棄して、ピリオドの間に最小の不一致を追加することができます。

2. トレーディングストラテジ

このメソッドをテストするため、3つの標準インジケータ WPR、RSI と ADX に基づいて簡単なストラテジを使用してみましょう。 WPR が売られ過ぎのレベルを上方 (レベル-80) を越えると、買いシグナルが作動します。 RSI は、買われ過ぎ領域 (レベル70の上) にあるべきではありません。 両方のインジケータがオシレータであるため、その使用はレンジな動きで正当化されます。 レンジの存在は、レベル40を超えてはならない ADX インジケータによってチェックされます。

買いエントリポイント

売りシグナルは対称になります。 WPR インジケータは、-20の買われ過ぎレベルをクロスし、RSIが30の売られ過ぎのラインを超える必要があります。 ADX は、買いと同じように、レンジを管理します。

売りエントリポイント

前述したように、相場のエントリは、シグナルに続く新しいロウソク足で実行されます。 決済は、固定SL/TP によって実行されます。

損失管理のため、一度に1つのポジションを取ることはありません。

3. テスターインジケータの準備

3.1. 仮想トレードのクラス

トレードストラテジを定義したので、テストインジケータを開発します。 まず、インジケータで追跡する仮想オーダーを準備する必要があります。 この記事 [1] には既に仮想オーダークラスが記述されています。 わずかな追加でこれ活用することができます。 前に説明したクラスには、現在の Ask と Bid の価格を使用してオーダーがクローズされた瞬間をチェックするティックメソッドがあります。 このメソッドは、リアルタイムでのみ機能する場合に適用され、ヒストリーデータのチェックには適用できません。 そのパラメータに価格とスプレッドを追加して、前述の関数を少し変更してみましょう。 操作の実行後、このメソッドはオーダーの状態を返します。 この追加の結果として、このメソッドは次の形式になります。

bool CDeal::Tick(double price, int spread)
  {
   if(d_ClosePrice>0)
      return true;
//---
   switch(e_Direct)
     {
      case POSITION_TYPE_BUY:
        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+=spread*d_Point;
        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;
     }
   return IsClosed();
  }

添付ファイルにクラスコード全体があります。

3.2. インジケータのプログラミング

次に、インジケータ自体をコーディングしてみましょう。 テスターインジケータはEAの役割を何らかの形で果たしているため、そのインプットはEAのパラメータに似ています。 最初に、テスト期間、およびインジケータパラメータの SL および TP レベルを設定します。 次に、適用されるインジケータのパラメータを指定します。 最後に、トレードの方向性と統計データの平均期間を示します。 各パラメータの使用についての詳細は、インジケータコードで使用されている間に提供されます。

input int                  HistoryDepth      =  500;           //ヒストリーデプス (足)
input int                  StopLoss          =  200;           //SL (ポイント)
input int                  TakeProfit        =  600;           //TP (ポイント)
//---RSI インジケータパラメータ
input int                  RSIPeriod         =  28;            //RSI の期間
input double               RSITradeZone      =  30;            //買われ過ぎ売られ過ぎゾーンサイズ
//---WPR インジケータパラメータ
input int                  WPRPeriod         =  7;             //期間 WPR
input double               WPRTradeZone      =  30;            //買われ過ぎ売られ過ぎ ゾーンサイズ
//---ADX インジケータパラメータ
input int                  ADXPeriod         =  11;            //ADX 期間
input int                  ADXLevel          =  40;            //レンジレベル ADX
//---
input int                  Direction         =  -1;            //トレードの方向  "-1 "-すべて、 "0 "-買い、 "1 "-売り
//---
input int                  AveragePeriod     =  10;            //平均期間

EA との計算とデータ交換に、以下のデータを含む9つのインジケータバッファを作成します。

1. 収益性の高いトレードの確率。

double      Buffer_Probability[];

2. テスト期間の利益率。

double      Buffer_ProfitFactor[];

3. SL と TP レベル。 2つのバッファは、EA でインジケータハンドルと指定されたレベルを照合する配列を作成するか、トレードの実行時にハンドルによってインジケータパラメータをリクエストすることによって除外できます。 しかし、現在の解決策は、最も簡単なものと思われます。

double      Buffer_TakeProfit[];
double      Buffer_StopLoss[];

4. テストされた期間内に実行されたトレードの合計数とその収益性の数値を計算するためのバッファ。

double      Buffer_ProfitCount[];
double      Buffer_DealsCount[];

5. 次の2つのバッファは、前の値を計算するための補助で、現在の足に対してのみ同様のデータを格納します。

double      Buffer_ProfitCountCurrent[];
double      Buffer_DealsCountCurrent[];

6. そして、最後に、EAに取引を実行するためのシグナルを送信するバッファ。

double      Buffer_TradeSignal[];

指定したバッファに加えて、開いている情報を格納するための配列、直近のトレードの時刻を記録するための変数、インジケータハンドルを格納するための変数、およびグローバル変数ブロック内のインジケータから情報を取得するための配列を宣言します。

CArrayObj   Deals;
datetime    last_deal;
int         wpr_handle,rsi_handle,adx_handle;
double      rsi[],adx[],wpr[];

関数の先頭にあるインジケータを初期化します。

int OnInit()
  {
//---RSI インジケータハンドルを取得
   rsi_handle=iRSI(Symbol(),PERIOD_CURRENT,RSIPeriod,PRICE_CLOSE);
   if(rsi_handle==INVALID_HANDLE)
     {
      Print("Test Indicator",": Failed to get RSI handle");
      Print("Handle = ",rsi_handle,"  error = ",GetLastError());
      return(INIT_FAILED);
     }
//---WPR インジケータハンドルを取得
   wpr_handle=iWPR(Symbol(),PERIOD_CURRENT,WPRPeriod);

   if(wpr_handle==INVALID_HANDLE)
     {
      Print("Test Indicator",": Failed to get WPR handle");
      Print("Handle = ",wpr_handle,"  error = ",GetLastError());
      return(INIT_FAILED);
     }
//---ADX インジケータハンドルを取得
   adx_handle=iADX(Symbol(),PERIOD_CURRENT,ADXPeriod);
   if(adx_handle==INVALID_HANDLE)
     {
      Print("Test Indicator",": Failed to get ADX handle");
      Print("Handle = ",adx_handle,"  error = ",GetLastError());
      return(INIT_FAILED);
     }

次に、インジケータバッファを動的配列に関連付けます。

//---インジケータバッファのマッピング
   SetIndexBuffer(0,Buffer_Probability,INDICATOR_CALCULATIONS);
   SetIndexBuffer(1,Buffer_DealsCount,INDICATOR_CALCULATIONS);
   SetIndexBuffer(2,Buffer_TradeSignal,INDICATOR_CALCULATIONS);
   SetIndexBuffer(3,Buffer_ProfitFactor,INDICATOR_CALCULATIONS);
   SetIndexBuffer(4,Buffer_ProfitCount,INDICATOR_CALCULATIONS);
   SetIndexBuffer(5,Buffer_TakeProfit,INDICATOR_CALCULATIONS);
   SetIndexBuffer(6,Buffer_StopLoss,INDICATOR_CALCULATIONS);
   SetIndexBuffer(7,Buffer_DealsCountCurrent,INDICATOR_CALCULATIONS);
   SetIndexBuffer(8,Buffer_ProfitCountCurrent,INDICATOR_CALCULATIONS);

すべての配列に時系列プロパティを割り当てます。

   ArraySetAsSeries(Buffer_Probability,true);
   ArraySetAsSeries(Buffer_ProfitFactor,true);
   ArraySetAsSeries(Buffer_TradeSignal,true);
   ArraySetAsSeries(Buffer_DealsCount,true);
   ArraySetAsSeries(Buffer_ProfitCount,true);
   ArraySetAsSeries(Buffer_TakeProfit,true);
   ArraySetAsSeries(Buffer_StopLoss,true);
   ArraySetAsSeries(Buffer_DealsCountCurrent,true);
   ArraySetAsSeries(Buffer_ProfitCountCurrent,true);
//--- 
   ArraySetAsSeries(rsi,true);
   ArraySetAsSeries(wpr,true);
   ArraySetAsSeries(adx,true);

関数の最後に、トレードの配列をリセットし、直近のトレードの日付だけでなく、インジケータに名前を割り当てる。

   Deals.Clear();
   last_deal=0;
//---
   IndicatorSetString(INDICATOR_SHORTNAME,"Test Indicator");
//---
   return(INIT_SUCCEEDED);
  }

インジケータの現在のデータは、GetIndValue 関数でダウンロードされます。 インプットでは、指定された関数は、読み込まれたデータのヒストリーの必要な深さを受信し、出力時に読み込まれた要素の数を返します。 インジケータのデータは、グローバルに宣言された配列に格納されます。

int GetIndValue(int depth)
  {
   if(CopyBuffer(wpr_handle,MAIN_LINE,0,depth,wpr)<=0 || CopyBuffer(adx_handle,MAIN_LINE,0,depth,adx)<=0 || CopyBuffer(rsi_handle,MAIN_LINE,0,depth,rsi)<=0)
      return -1;
   depth=MathMin(ArraySize(rsi),MathMin(ArraySize(wpr),ArraySize(adx)));
//---
   return depth;
  }

相場参入シグナルをチェックするには、BuySignal および SellSignal 関数を作成します。 添付ファイル内に関数のコードがあります。

よくあるインジケータのように、主な関数は、OnCalculate 関数に集中します。 関数の操作は、論理的に2つのフローに分けることができます。

  1. 複数の足を再計算する場合 (初期化後の最初の起動または新しい足のオープン)。 このフローでは、M1 のタイムフレームのヒストリーデータに基づいて、各計算された足と未処理のトレードの処理ストップオーダーのトレードを開始するための相場参入シグナルをチェックします。
  2. 新しい足はまだ形成されていませんが、各新しいティックのオープンポジションのストップオーダーのアクティベーションをチェックしてください。

関数の最初に、関数の直近の起動以降の新しい足の数を確認します。 インジケータを初期化した後の最初の起動である場合、インジケータの再計算の幅がテストの深さを超えないように設定し、インジケータバッファを初期状態に戻します。

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   int total=rates_total-prev_calculated;
   if(prev_calculated<=0)
     {
      total=fmin(total,HistoryDepth);
//---
      ArrayInitialize(Buffer_Probability,0);
      ArrayInitialize(Buffer_ProfitFactor,0);
      ArrayInitialize(Buffer_TradeSignal,0);
      ArrayInitialize(Buffer_DealsCount,0);
      ArrayInitialize(Buffer_ProfitCount,0);
      ArrayInitialize(Buffer_TakeProfit,TakeProfit*_Point);
      ArrayInitialize(Buffer_StopLoss,StopLoss*_Point);
      ArrayInitialize(Buffer_DealsCountCurrent,0);
      ArrayInitialize(Buffer_ProfitCountCurrent,0);
     }

次に、新しいロウソク足が開いたときに最初のロジックフローの操作が来ます。 まず、適用されたインジケータの現在のデータをダウンロードします。 データのダウンロードエラーが発生した場合は、インジケータが再計算されるのを待機している次のティックまで関数を終了します。

   if(total>0)
     {
      total=MathMin(GetIndValue(total+2),rates_total);
      if(total<=0)
         return prev_calculated;

次に、[時系列] プロパティを受信価格配列に割り当てます。

      if(!ArraySetAsSeries(open,true) || !ArraySetAsSeries(high,true) || !ArraySetAsSeries(low,true) || !ArraySetAsSeries(close,true)
         || !ArraySetAsSeries(time,true) || !ArraySetAsSeries(spread,true))
         return prev_calculated;

各足を再計算するためのメインループは次のようになります。 ループの開始時に、再計算された足のインジケータバッファを初期化します。

      for(int i=total-3;i>=0;i--)
        {
         Buffer_TakeProfit[i]=TakeProfit*_Point;
         Buffer_StopLoss[i]=StopLoss*_Point;
         Buffer_DealsCount[i]=Buffer_DealsCountCurrent[i]=0;
         Buffer_ProfitCount[i]=Buffer_ProfitCountCurrent[i]=0;

その後、まだ計算された足に別の取引を開いたかどうかを確認します。 そうでない場合は、以前に作成した関数を呼び出してインジケータのエントリシグナルをチェックします。 相場参入シグナルがある場合は、仮想トレードを作成し、シグナルバッファに対応するシグナルを書き込みます。

         if(last_deal<time[i])
           {
            if(BuySignal(i))
              {
               double open_price=open[i]+spread[i]*_Point;
               double sl=open_price-StopLoss*_Point;
               double tp=open_price+TakeProfit*_Point;
               CDeal *temp=new CDeal(_Symbol,rates_total-i,POSITION_TYPE_BUY,time[i],open_price,sl,tp);
               if(temp!=NULL)
                  Deals.Add(temp);
               Buffer_TradeSignal[i]=1;
              }
            else /*BuySignal*/
            if(SellSignal(i))
              {
               double open_price=open[i];
               double sl=open_price+StopLoss*_Point;
               double tp=open_price-TakeProfit*_Point;
               CDeal *temp=new CDeal(_Symbol,rates_total-i,POSITION_TYPE_SELL,time[i],open_price,sl,tp);
               if(temp!=NULL)
                  Deals.Add(temp);
               Buffer_TradeSignal[i]=-1;
              }
            else /*SellSignal*/
               Buffer_TradeSignal[i]=0;
           }

オープンポジションで動作するようにしましょう。 まず、現在の時間枠を確認します。 インジケーターがM1で動作する場合は、OnCalculate 関数パラメーターで取得した時系列データによって、ストップオーダーのアクティベーションがチェックされます。 まず、現在の時間枠を確認します。 ...

         if(Deals.Total()>0)
           {
            if(PeriodSeconds()!=60)
              {
               MqlRates rates[];
               int rat=CopyRates(_Symbol,PERIOD_M1,time[i],(i>0 ? time[i-1] : TimeCurrent()),rates);

クオートをダウンロードした後、各分のバーでオープンディールのストップオーダーのアクティベーションをチェックするためのループを調整します。 再計算されたバーの適切なインジケーターバッファーに、クローズおよび収益性のある取引を合計します。 この配列は CheckDeals 関数で処理されます。 チェックされた分足のデータは関数パラメータで渡されます。 関数演算アルゴリズムは以下のように考えられます。

               int closed=0, profit=0;
               for(int r=0;(r<rat && Deals.Total()>0);r++)
                 {
                  CheckDeals(rates[r].open,rates[r].high,rates[r].low,rates[r].close,rates[r].spread,rates[r].time,closed,profit);
                  if(closed>0)
                    {
                     Buffer_DealsCountCurrent[i]+=closed;
                     Buffer_ProfitCountCurrent[i]+=profit;
                    }
                 }

同様の代替ブロックが次に来ます。 M1 のタイムフレーム上の分のクオートやインジケータ操作のダウンロードが失敗した場合には、現在のタイムフレームのデータでトレードをチェックします。

               if(rat<0)
                 {
                  CheckDeals(open[i],high[i],low[i],close[i],spread[i],time[i],closed,profit);
                  Buffer_DealsCountCurrent[i]+=closed;
                  Buffer_ProfitCountCurrent[i]+=profit;
                 }
              }
            else /* PeriodSeconds()!=60 */
              {
               int closed=0, profit=0;
               CheckDeals(open[i],high[i],low[i],close[i],spread[i],time[i],closed,profit);
               Buffer_DealsCountCurrent[i]+=closed;
               Buffer_ProfitCountCurrent[i]+=profit;
              }
           } /* Deals.Total()>0 */

最後に、ストラテジ運用統計を分析してみましょう。 テストされた期間内に開いたトレードの数と、そのうちのどれくらいが利益で決済したかを計算します。

         Buffer_DealsCount[i+1]=NormalizeDouble(Buffer_DealsCount[i+2]+Buffer_DealsCountCurrent[i+1]-((i+HistoryDepth+1)<rates_total ? Buffer_DealsCountCurrent[i+HistoryDepth+1] : 0),0);
         Buffer_ProfitCount[i+1]=NormalizeDouble(Buffer_ProfitCount[i+2]+Buffer_ProfitCountCurrent[i+1]-((i+HistoryDepth+1)<rates_total ? Buffer_ProfitCountCurrent[i+HistoryDepth+1] : 0),0);
         Buffer_DealsCount[i]=NormalizeDouble(Buffer_DealsCount[i+1]+Buffer_DealsCountCurrent[i]-((i+HistoryDepth)<rates_total ? Buffer_DealsCountCurrent[i+HistoryDepth] : 0),0);
         Buffer_ProfitCount[i]=NormalizeDouble(Buffer_ProfitCount[i+1]+Buffer_ProfitCountCurrent[i]-((i+HistoryDepth)<rates_total ? Buffer_ProfitCountCurrent[i+HistoryDepth] : 0),0);

有効なトレードがある場合は、トレードで利益を得た確率と、テスト期間内のストラテジ利益率を計算します。 利益獲得確率の急激な変化を回避するために、このパラメータは、インジケータパラメータで設定された平均期間を使用して指数関数的な平均的な式に従ってスムージングされます。

         if(Buffer_DealsCount[i]>0)
           {
            double pr=2.0/(AveragePeriod-1.0);
            Buffer_Probability[i]=((i+1)<rates_total && Buffer_Probability[i+1]>0 && Buffer_DealsCount[i+1]>=AveragePeriod ? Buffer_ProfitCount[i]/Buffer_DealsCount[i]*100*pr+Buffer_Probability[i+1]*(1-pr) : Buffer_ProfitCount[i]/Buffer_DealsCount[i]*100);
            if(Buffer_DealsCount[i]>Buffer_ProfitCount[i])
              {
               double temp=(Buffer_ProfitCount[i]*TakeProfit)/(StopLoss*(Buffer_DealsCount[i]-Buffer_ProfitCount[i]));
               Buffer_ProfitFactor[i]=((i+1)<rates_total && Buffer_ProfitFactor[i+1]>0 ? temp*pr+Buffer_ProfitFactor[i+1]*(1-pr) : temp);
              }
            else
               Buffer_ProfitFactor[i]=TakeProfit*Buffer_ProfitCount[i];
           }
        }
     }

各ティックの処理フローには同様のロジックが含まれているため、ここでは完全な説明を提供しても意味がありません。 添付ファイル内ですべてのインジケータ関数のコード全体を詳しく見ることができます。

以前、既存の取引のストップオーダーの活発化の確認が CheckDeals 関数で遂行されることを述べました。 その操作アルゴリズムを考えてみましょう。 パラメータでは、関数は、分析された足のクオート、クローズドと収益性の高いトレードの数を返すための変数への2つのリンクを取得しました。

関数の開始時に、返された変数をリセットし、結果の論理変数を宣言します。

bool CheckDeals(double open,double high,double low,double close,int spread,datetime time,int &closed, int &profit)
  {
   closed=0;
   profit=0;
   bool result=true;

さらに、配列内のすべてのトレードを繰り返し処理するループが関数に配置されます。 取引オブジェクトへのポインタは、ループ内で1つずつ取得されます。 オブジェクトへの誤ったポインタがある場合は、配列からこのトレードを削除し、次に進みます。 操作の実行中にエラーが発生した場合は、結果の変数を ' false ' に設定します。

   for(int i=0;i<Deals.Total();i++)
     {
      CDeal *deal=Deals.At(i);
      if(CheckPointer(deal)==POINTER_INVALID)
        {
         if(Deals.Delete(i))
            i--;
         else
            result=false;
         continue;
        }

次に、ロウソク足が開いた時点でトレードが開かれたかどうかを確認します。 そうでない場合は、次の取引に移動します。

      if(deal.GetTime()>time)
         continue;

最後に、各価格のチェックされたトレードのティックメソッドを呼び出すことによって、始値、高値、安値、終値のトレードストップオーダーのアクティベーションを確認します。 このメソッドのアルゴリズムは、現在のセクションの先頭に記述されています。 チェックシーケンスは、売買取引によって異なることに注意してください。 まず、SL のアクティベーションがチェックされ、TP それにが続きます。 このアプローチは、ある程度トレード結果を過小評価するかもしれませんが、未来のトレードで損失を低減します。 ストップオーダーのいずれかがトリガされると、クローズドトレードの数が増加し、利益の場合には、同様に収益性の高いトレードの数が増加します。 トレードを決済すると、再計算を行わないように配列から削除されます。

      if(deal.Tick(open,spread))
        {
         closed++;
         if(deal.GetProfit()>0)
            profit++;
         if(Deals.Delete(i))
            i--;
         if(CheckPointer(deal)!=POINTER_INVALID)
            delete deal;
         continue;
        }
      switch(deal.Type())
        {
         case POSITION_TYPE_BUY:
            if(deal.Tick(low,spread))
              {
               closed++;
               if(deal.GetProfit()>0)
                  profit++;
               if(Deals.Delete(i))
                  i--;
               if(CheckPointer(deal)!=POINTER_INVALID)
                  delete deal;
               continue;
              }
            if(deal.Tick(high,spread))
              {
               closed++;
               if(deal.GetProfit()>0)
                  profit++;
               if(Deals.Delete(i))
                  i--;
               if(CheckPointer(deal)!=POINTER_INVALID)
                  delete deal;
               continue;
              }
           break;
         case POSITION_TYPE_SELL:
            if(deal.Tick(high,spread))
              {
               closed++;
               if(deal.GetProfit()>0)
                  profit++;
               if(Deals.Delete(i))
                  i--;
               if(CheckPointer(deal)!=POINTER_INVALID)
                  delete deal;
               continue;
              }
            if(deal.Tick(low,spread))
              {
               closed++;
               if(deal.GetProfit()>0)
                  profit++;
               if(Deals.Delete(i))
                  i--;
               if(CheckPointer(deal)!=POINTER_INVALID)
                  delete deal;
               continue;
              }
           break;
        }
     }
//---
   return result;
  }

インジケータとそのすべての関数の完全なコードが添付ファイルに用意されています。

4. EAの作成

テスターインジケータを作成したので、EA を開発しましょう。 EA のパラメータでは、静的な変数の数 (すべてのパスに共通) を設定し、ストラテジーテスタと同様に、変更されたパラメータの初期値と終了値、および値の変更ステップを定義します。 また、EA パラメータでは、相場参入シグナルを選択するための基準も指定していますが、テスト期間の利益と最低利益率を実現する最小確率です。 また、得られた統計データの客観性を維持するために、テスト期間に必要な最低トレード数を明記しておきましょう。

input double               Lot                     =  0.01;
input int                  HistoryDepth            =  500;           //ヒストリーデプス (足)
//---RSI インジケータパラメータ
input int                  RSIPeriod_Start         =  5;             //RSI の期間
input int                  RSIPeriod_Stop          =  30;            //RSI の期間
input int                  RSIPeriod_Step          =  5;             //RSI の期間
//---
input double               RSITradeZone_Start      =  30;            //買われ過ぎ売られ過ぎ ゾーンサイズ開始
input double               RSITradeZone_Stop       =  30;            //買われ過ぎ売られ過ぎ ゾーンサイズのストップ
input double               RSITradeZone_Step       =  5;             //買われ過ぎ売られ過ぎ ゾーンサイズステップ
//---WPR インジケータパラメータ
input int                  WPRPeriod_Start         =  5;             //期間 WPR 開始
input int                  WPRPeriod_Stop          =  30;            //期間 WPR ストップ
input int                  WPRPeriod_Step          =  5;             //期間 WPR ステップ
//---
input double               WPRTradeZone_Start      =  20;            //買われ過ぎ売られ過ぎ ゾーンサイズ開始
input double               WPRTradeZone_Stop       =  20;            //買われ過ぎ売られ過ぎ ゾーンサイズのストップ
input double               WPRTradeZone_Step       =  5;             //買われ過ぎ売られ過ぎ ゾーンサイズステップ
//---ADX インジケータパラメータ
input int                  ADXPeriod_Start         =  5;             //ADX 期間開始
input int                  ADXPeriod_Stop          =  30;            //ADX 期間ストップ
input int                  ADXPeriod_Step          =  5;             //ADX 期間ステップ
//---
input int                  ADXTradeZone_Start      =  40;            //レンジレベル ADX 開始
input int                  ADXTradeZone_Stop       =  40;            //レンジレベル ADX ストップ
input int                  ADXTradeZone_Step       =  10;            //レンジレベル ADX ステップ
//---設定
input int                  TakeProfit_Start        =  600;           //テイクプロフィットスタート
input int                  TakeProfit_Stop         =  600;           //テイクプロフィットストップ
input int                  TakeProfit_Step         =  100;           //テイクプロフィットステップ
//---
input int                  StopLoss_Start          =  200;           //ストップロス スタート
input int                  StopLoss_Stop           =  200;           //ストップロスストップ
input int                  StopLoss_Step           =  100;           //ストップロス ステップ
//---
input double               MinProbability          =  60.0;          //最小確率
input double               MinProfitFactor         =  1.6;           //最小プロフィットファクタ
input int                  MinOrders               =  10;            //ヒストリーの中でのトレードの最小数

グローバル変数で、トレーディングオペレーションクラスのインスタンスとテスターインジケータのハンドルを格納するための配列を宣言します。

CArrayInt   ar_Handles;
CTrade      Trade;

EA の OnInit 関数では、テストされたパラメータのすべてのオプションを繰り返し処理するための一連のネストされたループを配置し、売買取引の別のテストを追加します。 このアプローチは、テストされたストラテジによって追跡されないグローバルトレンドの影響を考慮することができます。 テスターインジケータはループ内で初期化されます。 インジケータのダウンロードが失敗した場合は、INIT_FAILED の結果で関数を終了します。 インジケータが正常に読み込まれた場合は、そのハンドルを配列に追加します。

int OnInit()
  {
//---
   for(int rsi=RSIPeriod_Start;rsi<=RSIPeriod_Stop;rsi+=RSIPeriod_Step)
      for(double rsi_tz=RSITradeZone_Start;rsi_tz<=RSITradeZone_Stop;rsi_tz+=RSITradeZone_Step)
         for(int wpr=WPRPeriod_Start;wpr<=WPRPeriod_Stop;wpr+=WPRPeriod_Step)
            for(double wpr_tz=WPRTradeZone_Start;wpr_tz<=WPRTradeZone_Stop;wpr_tz+=WPRTradeZone_Step)
               for(int adx=ADXPeriod_Start;adx<=ADXPeriod_Stop;adx+=ADXPeriod_Step)
                  for(double adx_tz=ADXTradeZone_Start;adx_tz<=ADXTradeZone_Stop;adx_tz+=ADXTradeZone_Step)
                     for(int tp=TakeProfit_Start;tp<=TakeProfit_Stop;tp+=TakeProfit_Step)
                        for(int sl=StopLoss_Start;sl<=StopLoss_Stop;sl+=StopLoss_Step)
                          for(int dir=0;dir<2;dir++)
                             {
                              int handle=iCustom(_Symbol,PERIOD_CURRENT,"::Indicators\\TestIndicator\\TestIndicator.ex5",HistoryDepth,
                                                                                                                        sl,
                                                                                                                        tp,
                                                                                                                        rsi,
                                                                                                                        rsi_tz,
                                                                                                                        wpr,
                                                                                                                        wpr_tz,
                                                                                                                        adx, 
                                                                                                                        adx_tz,
                                                                                                                        dir);
                              if(handle==INVALID_HANDLE)
                                 return INIT_FAILED;
                              ar_Handles.Add(handle);
                             }

すべてのテスターインジケータが正常に起動したら、トレード操作のクラスを初期化し、関数の実行を完了します。

   Trade.SetAsyncMode(false);
   if(!Trade.SetTypeFillingBySymbol(_Symbol))
      return INIT_FAILED;
   Trade.SetMarginMode();
//---
   return(INIT_SUCCEEDED);
  }

トレーディングシグナルはソートされ、トレーディングオペレーションは OnTick 関数で実行されます。 以前に足のオープニングでのみポジションを開くことを決めたので、関数の冒頭にこのイベントの発生を確認します。

void OnTick()
  {
//---
   static datetime last_bar=0;
   datetime cur_bar=(datetime)SeriesInfoInteger(_Symbol,PERIOD_CURRENT,SERIES_LASTBAR_DATE);
   if(cur_bar==last_bar)
      return;

2番目の制限は一度に1つ以上のオープン取引です。 したがって、開いているポジションがある場合は、関数の実行をストップします。

   if(PositionSelect(_Symbol))
     {
      last_bar=cur_bar;
      return;
     }

コントロールポイントをチェックした後、シグナルの検索ですべてのインジケータを繰り返し処理のメインループに進みます。 ループの開始時に、インジケータのシグナルバッファを読み込みます。 インジケータがまだ再計算されていない場合、またはトレードシグナルがない場合は、次のインジケータに進みます。

   int signal=0;
   double probability=0;
   double profit_factor=0;
   double tp=0,sl=0;
   bool ind_caclulated=false;
   double temp[];
   for(int i=0;i<ar_Handles.Total();i++)
     {
      if(CopyBuffer(ar_Handles.At(i),2,1,1,temp)<=0)
         continue;
      ind_caclulated=true;
      if(temp[0]==0)
         continue;

次のステップは、受信したシグナルが他のインジケータから以前に受信したシグナルと矛盾しないかどうかを確認することです。 相反するシグナルの存在は、エラーの確率を増加させるので、次のロウソクの形成が始まる前に、関数を終了します。

      if(signal!=0 && temp[0]!=signal)
        {
         last_bar=cur_bar;
         return;
        }
      signal=(int)temp[0];

次に、テスト期間内の最小必要なトレード数の有無を確認します。 サンプルが不十分な場合は、次のインジケータに移動します。

      if(CopyBuffer(ar_Handles.At(i),1,1,1,temp)<=0 || temp[0]<MinOrders)
         continue;

さらに、収益性の高いトレード確率は、同様の方法で検証されます。

      if(CopyBuffer(ar_Handles.At(i),0,1,1,temp)<=0 || temp[0]<MathMax(probability,MinProbability))
         continue;

分析されたインジケータによる収益性の高いトレードの確率の不一致と以前にチェックしたものが 1% 未満の場合は、利益率と利益/リスク比に基づいて、2つのパスから最適なものが選択されます。 最適なパスデータは、次のタスクに保存されます。

      if(MathAbs(temp[0]-probability)<=1)
        {
         double ind_probability=temp[0];
//---
         if(CopyBuffer(ar_Handles.At(i),3,1,1,temp)<=0 || temp[0]<MathMax(profit_factor,MinProfitFactor))
            continue;
         double ind_profit_factor=temp[0];
         if(CopyBuffer(ar_Handles.At(i),5,1,1,temp)<=0)
            continue;
         double ind_tp=temp[0];
         if(CopyBuffer(ar_Handles.At(i),6,1,1,temp)<=0)
            continue;
         double ind_sl=temp[0];
         if(MathAbs(ind_profit_factor-profit_factor)<=0.01)
           {
            if(sl<=0 || tp/sl>=ind_tp/ind_sl)
               continue;
           }
//---
         probability=ind_probability;
         profit_factor=ind_profit_factor;
         tp=ind_tp;
         sl=ind_sl;
        }

収益性の高いトレードを取得する確率が明らかに大きい場合は、パスの利益率の要件がチェックされます。 すべての要件が満たされている場合、パスデータはさらに保存されます。

      else /* MathAbs(temp[0]-probability)<=1 */
        {
         double ind_probability=temp[0];
//---
         if(CopyBuffer(ar_Handles.At(i),3,1,1,temp)<=0 || temp[0]<MinProfitFactor)
            continue;
         double ind_profit_factor=temp[0];
         if(CopyBuffer(ar_Handles.At(i),5,1,1,temp)<=0)
            continue;
         double ind_tp=temp[0];
         if(CopyBuffer(ar_Handles.At(i),6,1,1,temp)<=0)
            continue;
         double ind_sl=temp[0];
         probability=ind_probability;
         profit_factor=ind_profit_factor;
         tp=ind_tp;
         sl=ind_sl;
        }
     }

1つのテスターインジケータがすべてをチェックした後に再計算されない場合は、インジケータが再計算されるのを待機している次のティックまで関数を終了します。

   if(!ind_caclulated)
      return;

インジケータが正常にチェックされ、アクティブなトレードシグナルがない場合は、新しい足が形成される前に関数を終了します。

   last_bar=cur_bar;
//---
   if(signal==0 || probability==0 || profit_factor==0 || tp<=0 || sl<=0)
      return;

関数の最後に、エントリシグナルがある場合は、ベストパスに従ってオーダーを送信します。

   if(signal==1)
     {
      double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
      tp+=price;
      sl=price-sl;
      Trade.Buy(Lot,_Symbol,price,sl,tp,"Real Time Optimizator");
     }
   else
      if(signal==-1)
        {
         double price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
         tp=price-tp;
         sl+=price;
         Trade.Sell(Lot,_Symbol,price,sl,tp,"Real Time Optimizator");
        }
  }

添付ファイルで完全な EA のコードを詳しく見ることができます。

5. アプローチのテスト

このメソッドを示すために、得られたEAは標準EAの並列最適化され、同様の期間の可変パラメータを使用したフォワードテストされます。 条件を等しく保つために、統計データによってフィルタリングすることなく、1つだけテスターインジケータを起動し、すべてのシグナルの情報のEA を作成しました。 その構造は、シグナル・コンシステンシ・フィルタリング・ブロックを除き、上記で作成された EA に似ています。 完全な EA コードは添付ファイル (ClassicExpert.mq5) にあります。

テストは、2018年の7ヶ月間、H1で実施されています。 テストされた期間の1/3 は標準的な EA のフォワードテスト用に取っておきました。

テストパラメータ

インジケータ計算期間が最適化パラメータとして選択されました。 5から30までの1つの範囲の値をすべてのインジケータに使用しました。

テストパラメータ

最適化の結果は提案手法の矛盾を示しました。 最適化中に小さな利益を示したパラメータ値は、フォワードテスト中に損失が発生することが判明しました。 全体では、パスのいずれも分析期間内に利益を示しました。

最適化の結果

最適化とフォワードテストのグラフィカルな分析の結果は、WPR インジケータ期間による収益性ゾーンのシフトにつながる価格の動きの構造の変化を示しました。

WPR 最適化チャートWPR フォワードテストチャート

提案手法に従って開発された EA をテストするために、解析期間を同じに保ちながら同様のテストパラメータを指定しました。 相場参入シグナルを選別するために、最低利益率を 60%、テスト期間中の最低プロフィットファクタを2として指定しました。 テストの深さは500ロウソク足です。

提案手法のテスト

テスト中に、このEAは、分析期間内に1.66 のプロフィットファクタで利益を示しました。 ビジュアルモードでのテスト中に、テストエージェントは 1250 MB の RAM を占有していました。

結論

著書稿では、EAをリアルタイムに最適化して開発するメソッドを提案しました。 テストは、実際のトレードのアプローチの実行可能性を示しています。 提案手法に基づくEAは、ストラテジの収益性期間中に利益を得て、損失の発生時に活動をストップさせます。 同時に、計算資源の面でもそのメソッドが求められています。 RAMは、すべての適用されるインジケータを含める必要がある一方、CPU 速度はすべての読み込まれたインジケータを再計算することができます。

レファレンス

  1. インジケータにエントリを解決する技術を使用して新しいトレーディングストラテジを作成する

記事で使用するプログラム

#
名称
タイプ
詳細
1 Deal.mqh クラスライブラリ 仮想トレードのクラス
2 TestIndicator.mq5 インジケータ テスターインジケータ
3 RealTimeOptimization.mq5 EA 提案手法に基づく EA
4 ClassicExpert.mq5 EA 比較最適化の標準法に基づくEA

MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/5061

添付されたファイル |
MQL5.zip (360.42 KB)
特定のディストリビューション法によるカスタムシンボルを用いた時系列モデリング 特定のディストリビューション法によるカスタムシンボルを用いた時系列モデリング

この記事では、カスタムシンボルを作成および操作するためのターミナルの機能の概要を示し、カスタムシンボル、トレンド、さまざまなチャートパターンを使用してトレードヒストリーをシミュレートするための手法を提供します。

リバーシング: 聖杯や危険な妄想? リバーシング: 聖杯や危険な妄想?

この記事では、リバーシングマーチンゲール技術を研究し、トレード戦略を向上させることができるかどうかということはもちろん、使用する価値があるかどうかを判断します。 ヒストリカルデータを操作し、リバーシングテクニックに最適なインジケータを確認するEAを作成します。 また、独立したトレードシステムとしてのインジケータなしで使用できるかどうかもチェックします。 また、リバーシングが、負けトレードから勝ちトレードに変えられるかを確かめます。

ソーシャルトレーディング収益性の高いシグナルをさらに良くすることはできるでしょうか? ソーシャルトレーディング収益性の高いシグナルをさらに良くすることはできるでしょうか?

ほとんどのサブスクライバーは、バランス曲線の美しさとサブスクライバーの数で取引シグナルを選択しています。そのため、多くのプロバイダーは今日、シグナルの実際の質よりも、美しい統計により気を配り、多くの場合、トランザクションの量を多くして、人為的にバランス曲線を理想的な形にしています。この記事では、信頼性の基準と、プロバイダーがシグナルの品質を向上させる方法をご紹介します。特定のシグナルの履歴、またプロバイダーがより収益を上げ、リスクを低くするための方法の例をあげていきます。

MQL5レシピ - オープンヘッジポジションのプロパティを取得しましょう MQL5レシピ - オープンヘッジポジションのプロパティを取得しましょう

MetaTrader 5プラットフォームでは、マルチマーケットだけでなく、さまざまなポジション計算システムの使用も可能です。このような機能は、取引アイデアの実装と形式化のためのツールを大幅に拡大します。この記事では、ポジションが独立してカウントされたとき(『ヘッジ』)のポジションのプロパティの処理と考慮の方法について説明します。派生クラスの提案と、ヘッジポジションのプロパティの処理と取得の例を提示します。