パターン検索への総当たり攻撃アプローチ(第III部): 新しい水平線

21 4月 2021, 10:57
Evgeniy Ilin
0
442

目次


はじめに

前の記事では、単純な総当たり攻撃メカニズムを使用して収益性の高いエキスパートアドバイザーを作成できることを示しました。このアプローチは他のアプローチに劣らないからです。このトピックは、実際には私の「取引システムの開発と分析への最適なアプローチ」というタイトルの記事(特に「最適な検索の背後にある数学」セクション)に非常に近いものです。このソフトウェアを作成するにあたっては、主にこの記事で定式化されたアイデアを使用しました。一般に、この記事は、見つかったオプションの品質を向上させることを目的としたプログラムアルゴリズムのさらなる近代化を提供すること、およびさまざまな曜日とさまざまな期間でのより詳細な市場調査を目的としています。

私のエキスパートアドバイザーのいくつは、このアプローチの実行可能性を証明しています。異なる通貨ペアには異なるパターンが存在します。また、すべてのペアで機能する狭い期間があります。詳細な分析をすることが必要になります。これが、この記事で行うことです。このような包括的な分析は、追加のソフトウェアなしでは実行できません。私は自分のソフトウェアが最善の解決策であるとは考えていません。

このプログラムは研究用ソフトウェアとして登場し、さまざまな資産を分析するための補助ツールキットとして機能しましたが、その機能ははるかに広がっています。したがって、このアプローチを最大限に活用することができます。やってみましょう。正直なところ、ニューラルネットワーク、人工知能、勾配ブースティングなどのアプローチは、はるかに進歩的だと思います。それにもかかわらず、総当たり攻撃アプローチはこれらの分析方法と競合する可能性があることが判明しました。今のところ、アプローチは非常に単純な数学に基づいています。アルゴリズムのパフォーマンスがより良いと思います。


新しい概念

プログラムがプロトタイプ段階にあったので、そのような単純な総当たり攻撃方式で何ができるかを確認するために、プログラムを最大限に活用したいと思いました。主な問題は、信頼性の高いグローバルパターン検索では、1回のパスの容量が非常に小さい一方、長期の相場の分析が必要であったことでした。たとえば、任意の通貨ペアのM5相場を10年間分析すると、プログラムは優れたシングルコアで1時間あたり約700〜2000のバリアントを生成しました(中程度の設定でも)。

30コアのサーバを使用したとしても、数日待つ必要があります。唯一の解決策は、分析するデータの量を減らすことです。これは、期間全体を等間隔に分割し、一部を計算せずにスキップすることで行うことができます。このような間隔は、分、時間、日、月、年です。さらに、これらの条件付き領域は、特定の取引パラメータを特徴づけます。つまり、加速に加えて、曜日、時間、分をパターンで詳細に調べることができます。このパターンが何に関連しているかを心配する必要はありません。

一般に、パターンの数は無限であるため、すべてのパターンを分類することは不可能で、人間の脳はそれらすべてを理解して説明することはできません(これは、式がある場合は実際には必要ありません)。これは数学です。一定の期間と一定の日数を調査することも、すべてのデータを1回調査することもできます。これは、次の場合と比較できます。変数が1つの関数があり、これは変数が複数の関数の特殊なケースであることがわかりますが、これについては知りませんでした。


標本のセグメント化と標本の予測可能性

異なる長さの標本が分析されるため、パターン検索モードの上位機能が大きく異なります。よって、このオプションを分析することは興味深いと思います。計算の大幅な高速化に加えて、最初の標本に基づいて、より有用な(より小さな)標本を取得できます。この得られた関数は、簡単な式または多項式を使用してさらに分析できます。これは、市場が世界の時間と厳密に関連しているためです。

取引活動は、主にトレーダーの日常活動に基づいて形成されます。ここでは、エキスパートアドバイザーを紹介することもできます。多くのエキスパートアドバイザーは予測可能なプレーヤーと見なすことができますが、すべてではありません。たとえば、多くの開発者は時間ベースのナイトスカルパーを作成します。価格変動の性質は、過去の価格だけでなく、特定の日、週、月、場合によっては年によっても決定されることがわかります。これらの値はそれぞれ量子化されています。つまり、特定の固定値があります。

もちろん、値を分割することもできます。たとえば、時間枠は、時間や分ではなく、新しい日の始まりから経過した秒数で測定することができます。どの量が測定しやすいか次第です。これらの量子化された量は、さらに無限大に分割できます。私のプログラムでは、セグメント化は日と週ごとのみで発生します。年ごとのセグメント化はそれほど効率的ではないと思います。数ヶ月は、後で関連する機能を追加するかもしれません。次の図は、上記のアイデアを示しています。

セグメント化図

元の標本セグメント化に対して2つの任意のオプションを表示しました。以下は、総当たり攻撃が無期限に長く続いた場合に何が起こるかを示します。1番目は、説明のために考えられるすべての数学的パターンを実装した場合であり、2番目は、実装された唯一の総当たり攻撃法(多次元テイラー多項式)から何が得られるかを示しています。標本の予測可能性は常に異なります。また、各多項式タイプに最適なセグメント化されたセクションが存在します。このようなセグメントは無数にありますが、非常に正確に検出できます。バーを分析するので、秒は考慮しません。

したがって、セグメント化パラメータの組み合わせごとに、開発サイクル全体の出力で取得できる取引パラメータごとの関数を作成できます。たとえば、ペイオフの期待値と利益率は次のとおりです。

  • Ma=Ma(M,D,Ts,Te)
  • PrF=PrF(M,D,Ts,Te)
  • M - 月
  • D - 曜日
  • Ts - 期間開始時刻
  • Te - 期間終了時刻(0:00をまたいだ移行、つまり翌日になる可能性あり) 

関数は離散的です。したがって、引数は厳密に固定された値を取ることができます。月と曜日に関連付けられた変数を修正することにより、これら2つの関数がどのように見えるかを大まかに想像できます。変更の最大数は3つです。そのため、2つの自由変数のみで有益なグラフを作成できます。ここでは「Ts」と「Te」を使用します。

3Dグラフ

将来を予測しようとする式やアルゴリズムにはそれぞれ、将来の取引システムのすべての定量的特性について、そのような一意のグラフがあります。ここではそれらのうちの2つだけを示して、PFが最大である場合に必ずしもペイオフ期待値が最大であるとは限らないことを示しました。ほとんどの場合、スプレッド、手数料、スワップがあるため、ペイオフ期待値とPFのバランスを取る必要があります。これらの極値の数とその値は、数式ごとに異なります。多次元極値検索は、手動で実行することはできませんがプログラムを使用して実行できます。

ここで1つのことを述べたいと思います。自分のソフトウェアを使用して市場を調査しているときに、多くの通貨ペアが予測可能性の値を過大評価している興味深いセクションに気づきました。23:30から0:30くらいまでの間隔です。これは間違いなく日付の変更に関連しています。ただし、MetaTrader 4で優れたPFを示したストラテジーの収益性は、MetaTrader5でテストしたときには確認されませんでした。その理由はスプレッドです。スプレッド内にあるパターンは常に再確認してください。見つかったパターンのほとんどはスプレッドの内側にあります。


検索アルゴリズムの最終変更

最終的な変更の目的は、プログラムの操作を高速化することとと検索の変動性と効率を最大化することでした。次は変更点のリストです。

  1. 一定の時間間隔でパターンを検索する機能の追加
  2. 選択した日にのみ取引する機能の追加
  3. 選択した日に基づいて新しいバリアントごとにランダムな日のセットを生成する機能の追加
  4. 可能な最小および最大ウィンドウ期間を分単位で指定することにより、ランダムなサーバー時間ウィンドウを生成する機能の追加
  5. これらの設定のいずれかを組み合わせる機能
  6. マルチスレッドモードで2番目の最適化タブが機能
  7. 最適化モードの最適化

更新されたウィンドウは次のようになります。

総当たり攻撃タブ

2番目のタブは変わっていません。

最適化タブ

以下が3番目のタブです。

エキスパートアドバイザー生成タブ

インターフェイスはまだ非常に生々しいため、設定はフォームにほとんどはまりません。次の記事で紹介する次のプログラムバージョンでは、インターフェイスを完全に改訂し、プログラムを使用してエキスパートアドバイザーを作成するプロセス全体を含むビデオを録画します。どれほど簡単で高速なのかが分かると思います。インターフェイスの設定を理解し、少し練習するだけで済みます。


テンプレートと補助エキスパートアドバイザーの最適化

新しいタスクを実装するには、エキスパートアドバイザーのテンプレートも変更する必要がありました。ロボットは、プログラムが便利に読み取ることができる希望の形式で相場を生成します。MetaTrader 5の相場を生成するEAのコードは次のようになりました。

string FileNameString;
uint Handle0x;
datetime Time0=0;

double Open[];
double Close[];
double High[];
double Low[];
datetime Time[];


void WriteEnd()
   {
   FileWriteString(Handle0x,"EndBars"+"\r\n");
   MqlDateTime T;
   TimeToStruct(Time[1],T);
   FileWriteString(Handle0x,IntegerToString(int(T.year))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.mon))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.day)));
   }

void OpenAndWriteStart()
   {
   FileDelete(FileNameString);
   Handle0x=FileOpen(FileNameString,FILE_WRITE|FILE_TXT|FILE_COMMON|FILE_ANSI,'\t',CP_UTF8);
   FileSeek(Handle0x,0,SEEK_SET);
   FileWriteString(Handle0x,"DataXXX"+" "+Symbol()+" "+IntegerToString(Period())+"\r\n");
   FileWriteString(Handle0x,DoubleToString(_Point,8)+"\r\n");
   MqlDateTime T;
   TimeToStruct(Time[1],T);
   FileWriteString(Handle0x,IntegerToString(int(T.year))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.mon))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.day))+"\r\n");             
   }
      
void WriteBar()
   {
   FileWriteString(Handle0x,"\r\n");
   FileWriteString(Handle0x,DoubleToString(Close[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(Open[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(High[1],8)+"\r\n");
   FileWriteString(Handle0x,DoubleToString(Low[1],8)+"\r\n");         
   FileWriteString(Handle0x,IntegerToString(int(Time[1]))+"\r\n");
   MqlDateTime T;
   TimeToStruct(Time[1],T);
   FileWriteString(Handle0x,IntegerToString(int(T.hour))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.min))+"\r\n");
   FileWriteString(Handle0x,IntegerToString(int(T.day_of_week))+"\r\n");         
   //FileClose(Handle0x);
   }      

void CloseFile()
   {
   FileClose(Handle0x);
   }

bool bNewBar()
   {
   ArraySetAsSeries(Close,false);                        
   ArraySetAsSeries(Open,false);                           
   ArraySetAsSeries(High,false);                        
   ArraySetAsSeries(Low,false);                              
   CopyOpen(_Symbol,_Period,0,2,Open);
   CopyClose(_Symbol,_Period,0,2,Close);
   CopyHigh(_Symbol,_Period,0,2,High);
   CopyLow(_Symbol,_Period,0,2,Low);
   ArraySetAsSeries(Close,true);                        
   ArraySetAsSeries(Open,true);                           
   ArraySetAsSeries(High,true);                        
   ArraySetAsSeries(Low,true);                                 
   if ( Time0 < Time[1] )
      {
      if (Time0 != 0)
         {
         Time0=Time[1];
         return true;
         }
      else
         {
         Time0=Time[1];
         return false;
         }
      }
   else return false;
   }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
  ArrayResize(Close,2,0);
  ArrayResize(Open,2,0);   
  ArrayResize(Time,2,0);
  ArrayResize(High,2,0);
  ArrayResize(Low,2,0);  
  FileNameString="DataHistory"+" "+Symbol()+" "+IntegerToString(Period());
  OpenAndWriteStart();
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
  WriteEnd();
  CloseFile();
  }

void OnTick()
  {
  ArraySetAsSeries(Time,false);
  CopyTime(_Symbol,_Period,0,2,Time);
  ArraySetAsSeries(Time,true);
  if ( bNewBar()) WriteBar();
  }

簡単な関数が2、3個あるだけです。テストの最初に何かを書き込む関数もあれば、最後に相場に関する情報を追加する関数もあります。また、メイン関数は、バーが表示されたときにその情報を書き込むだけです。EAには入力パラメータがありません。履歴データで実行すると、プログラムで読み取ることができる適切な形式の相場ファイルが生成されます。これはあまり良い解決策ではありません。おそらくですが、将来的には、ターミナルからの直接相場読み取りを実装できるでしょう。上記の解決策は、これまでのところ非常に便利です。少なくとも私にとっては便利です。

datetimeを曜日、時間、分に変換する一時的な関数を使用する代わりに、追加のバー情報として相場に書き込みました。ENUM_DAY_OF_WEEKを除いて、これらはすべて整数値です。C#コード内にそのような番号付きリストを実装し、データが同じ形式で返されるようにテンプレートに提供するだけで済みました。時間関数を回避すれば、C#コード側での不要な計算を回避できます。また、時間の不一致も回避できます。危険なので、避けるべきです。

結果の相場ファイルは、任意のテキストエディタで開くことができます。次のシンプルで明確な構造が表示されます。

履歴データファイル


変数が登録されているテンプレートのヘッダーには、以前は生成時に自動入力するためのフィールドが含まれていました。取引時間と取引日の変数がリストに追加されました。

事前入力は以下のようになります。

double C1[] = { %%%CVALUES%%% };//Brutted Values
int CNum=%%%CNUMVALUE%%%;//Bars To Equation
int DeepBruteX=%%%DEEPVALUE%%%;//Max Pow Of Polynomial
int DatetimeStart=%%%DATETIMESTART%%%;//Help Datetime
input bool bInvert=%%%INVERT%%%;//Invert Trade(or sign of values as the same)
input int DaysToFuture=%%%DAYSFUTURE%%%;//Days To Future
int DaysToTrade[]={ %%%DAYS%%% };//Days To Trade
input double ValueOpenE=%%%OPTVALUE%%%;//Open Signal
input bool bUseTimeCorridorE=%%%TIMECORRIDORVALUE%%%;//Use Time Corridor
input int TradeHour=%%%HOURSTARTVALUE%%%;//Start Trading Hour
input int TradeMinute=%%%MINUTESTARTVALUE%%%;//Start Trading Minute
input int TradeHourEnd=%%%HOURENDVALUE%%%;//End Trading Hour
input int TradeMinuteEnd=%%%MINUTEENDVALUE%%%;//End Trading Minute

ここにあるすべての値は、ロボットの作成時にプログラムによって入力されるため、ロボットはデフォルト設定ですぐに動作し、MetaEditorを開かなくても終了時にコンパイルできます。すべての設定と配列はエキスパートアドバイザーに組み込まれています。便利だと思います。エキスパートアドバイザーがたくさんあって、設定ファイルが異なると想像してみてください。MetaTrader 4の総当たり攻撃ソフトウェアプロトタイプで、自分が時々設定を混乱させることがわかりました。テキストファイルと比較すると機能性が低下する可能性もありますが、この方法の方が信頼性が高くなります。

メイン関数も変更されました。

bool bDay()//Day check
   {
   MqlDateTime T;
   TimeToStruct(Time[0],T);
   for ( int i=0; i<ArraySize(DaysToTrade); i++ )
      {
      if ( T.day_of_week == DaysToTrade[i] ) return true;
      }
   return false;
   }

void Trade()//Trade Function
   {
   double Value;
   Value=PolinomTrade();
   MqlTick LastTick;
   SymbolInfoTick(Symbol(),LastTick);
   MqlDateTime tm;
   TimeToStruct(LastTick.time,tm);
   int MinuteEquivalent=tm.hour*60+tm.min;
   int BorderMinuteStartTrade=HourCorrect(TradeHour)*60+MinuteCorrect(TradeMinute);
   int BorderMinuteEndTrade=HourCorrect(TradeHourEnd)*60+MinuteCorrect(TradeMinuteEnd);
   
   if ( Value > ValueCloseE)
      {
      if ( !bInvert ) CloseBuyF();
      else CloseSellF();
      }
      
   if ( Value < -ValueCloseE)
      {
      if ( !bInvert ) CloseSellF();
      else CloseBuyF();
      }   
   
   if ( !bUseTimeCorridorE )
      {
      if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value > ValueOpenE && Value <= ValueOpenEMax )
         {
         if ( !bInvert ) SellF();
         else BuyF();
         }
      
      if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value < -ValueOpenE && Value >= -ValueOpenEMax )
         {
         if ( !bInvert ) BuyF();
         else SellF();
         }      
      }
   else
      {
      if ( BorderMinuteStartTrade > BorderMinuteEndTrade && bDay() )
         {
         if ( !(MinuteEquivalent>=BorderMinuteEndTrade && MinuteEquivalent<= BorderMinuteStartTrade) )
            {
            if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value > ValueOpenE && Value <= ValueOpenEMax )
               {
               if ( !bInvert ) SellF();
               else BuyF();
               }
      
            if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value < -ValueOpenE && Value >= -ValueOpenEMax )
               {
               if ( !bInvert ) BuyF();
               else SellF();
               }
            }        
         }
      if ( BorderMinuteStartTrade <= BorderMinuteEndTrade && bDay() )
         {
         if ( MinuteEquivalent>=BorderMinuteStartTrade && MinuteEquivalent<= BorderMinuteEndTrade )
            {
            if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value > ValueOpenE && Value <= ValueOpenEMax )
               {
               if ( !bInvert ) SellF();
               else BuyF();
               }
      
            if ( double(TimeCurrent()-DatetimeStart)/86400.0 <= DaysToFuture && Value < -ValueOpenE && Value >= -ValueOpenEMax )
               {
               if ( !bInvert ) BuyF();
               else SellF();
               }
            }        
         }      
      }

   if ( bPrintValue ) Print("Value="+DoubleToString(Value));     
   }

ここでは、曜日とその日の時間間隔を制御するためのロジックのみを追加し、残りは変更していません。ちなみに、時間間隔は0〜24時間である必要はありません。始まった日と別の日に終わることもあります。したがって、スワップチャージを行うポイントを含めることができます。当初の考えによれば、ポジションは指定された期間に配置し、いつでも決済することができます。おそらく、より良い解決策は、別の操作として時間による強制的なポジション決済を追加することです。これまでのところ、現在のアプローチでは時間的期間の境界は曖昧にしているため、より安定したロボットが生成されるはずです。これは、見つかった期間とその式がパフォーマンスを突然停止するのではなく徐々に減衰するようにして、ランダムな結果が得られる可能性を減らすために行われます。


グローバルパターンの分析

ここに示されているすべての結果は、前の記事と同様に、2010.01.01〜2020.01.01の期間での訓練に基づいて取得されたものです。これは、結果を確認するためのフォワード期間として今年を残すために意図的に行われます。フォワード期間は2020.01-2020.12.01です。

結果の品質は大幅に向上しました。取引件数は減少しましたが、フォワード期間の結果は良くなっています。前回の記事で想定したように、初期テストの品質が向上すると、フォワード期間のパターン寿命が長くなります。以下で証明します。

EURCHF H1ペアから始めましょう。この銘柄の予測可能性は以前の分析で良好であることが示されたため、それから始めることにしました。最適化の結果に基づいて、3つのエキスパートアドバイザーを作成しました。さらに、MetaTrader 4からいくつかのテストを提供して、見つかったパターンのパフォーマンスがどれだけ向上したかを示します。最初のオプションを見てください。

ボット#1 EURCHF H1 2010.01.01-2020.01.01 MetaTrader 4

テストロットは0.1に設定されました。結果として得られるペイオフ期待値を以前のプログラムバージョンと比較すると、以前の8米ドルから54米ドルに増加しています。PFも大幅に増加し、少なくとも0.5(おそらくそれ以上)増加しています。前回の記事の同じパラメータは約1.14で、これは私が何とか得た最高のPFでした。曜日や操作時間による標本の構造化が結果にどのように影響したかがわかります。

ただし、これは最良の結果ではありません。タスクの詳細により、テストは非常に限られた時間で実施されました。時間枠は非常にきついものでした。合計で、すべてのテストに約2〜3日かかりました。テストは2コアで実行され、各バリアントには約5〜6時間かかりました。

また、テストプロセス中に、プログラムアルゴリズムにいくつかのエラーが見つかり、誤った結果が生成されていることがよくありました。エラーはすでに修正しましたが、発見されたパターンの数が減少しました。それにもかかわらず、この時間は許容できるオプションを見つけるのに十分でした。それでは、MetaTrader5でテストしてみましょう。

ボット#1 EURCHF H1 2010.01.01-2020.01.01 MetaTrader 5

より高い収益性を達成しようとしながらテストを安定させるためにテストのスプレッドを制限したため、このテストの取引は少なくなりました。ただし、実際に示されているように、これは必要ありません。記事の最後でその理由を説明します。取引数が非常に少ないにもかかわらず、このデータは非常に長い標本に基づいているため、信頼できると見なすことができます(最初のタブで式係数を総当たり攻撃したときに取得されました)。実際、最初のタブでは、ロードされたセグメントにあるすべてのバーを計算するため、これは最適化の理想的なベースとなります。大きな標本の結果の一部を小さな標本に取り込むと、最初の標本(注文)に含まれるデータが多いほど、小さなパターンが強くなります。

フォワード期間テストは次のとおりです。

将来1年

1年間で2回の取引しかありませんが、結果は良好です。

同じ間隔での別の結果は次のとおりです。

ボット#2 EURCHF H1 2010.01.01-2020.01.01 MetaTrader 4

スプレッド要件がない場合、この式はMetaTrader5で赤字になります。しかし、得られたペイオフ期待値に基づいてスプレッドを制限すると、グラフは見栄えが悪くなりますが、良好なシグナルが得られます。

ボット#2 EURCHF H1 2010.01.01-2020.01.01 MetaTrader 5

このグラフは全くだめです。どのしたら将来それから恩恵を受けることができるでしょうか。グラフは将来のすべての標本に力を与える巨大な基礎となる標本に基づいているので、そのようなグラフに悪いことは何もありません。とにかく、いくらかの利益は示しています。将来を確認しましょう。

将来1年

ご覧のとおり、取引はそれほど多くありませんが、結果は非常に良好です。実際、近年、ブローカーが新しいテクノロジーを使用しながらスプレッドを絶えず減らしているため、テスト結果がより良くなっています。さて、予測とその可能なパフォーマンスに関する結論は非常に良好です。ただし、最終的な結論を出すにはデータが不十分です。よって、異なる通貨ペアでさらにいくつかのテストを実行しました。後でお見せします。次に、同じ通貨ペアの3番目のロボットを見てみましょう。

MetaTrader4からのテストはもうありません。比較するには、2つの完全なバリアントで十分だと思います。これが3番目のバリアントです。

ボット#3 EURCHF H1 2010.01.01-2020.01.01 MetaTrader 5

グラフはそれほど悪くはありません。それでは、将来を確認しましょう。

将来1年

図は同じです。すべてのフォワード期間は肯定的な結果を示しています。3つのロボットはすべて同じ通貨ペアの同じデータ間隔で訓練されていて同じパターンを記述しているために結果が類似していると言うこともできれば、

通貨ペアが非常に優れているためにロボットが肯定的だということもできます。そうであっても、それから利益を得ることができます。しかし、その理由はもっと一般的です。理由が通貨ペアまたは間隔だけにあるのではないことを証明するために、別の時間枠で別の通貨ペアを確認しましょう。

EURUSD H4のテスト

ここではMetaTrader4のテストは提供していませんが、この通貨ペアのグラフは以前のテストと同じように滑らかで美しく見えます。この時間枠で2つの異なるボットを見つけました。まず1番目です。

ボット#1 EURUSD H4 2010.01.01-2020.01.01 MetaTrader 5

グラフは見苦しいですが、これまでの仮定によれば、これは非常に大きな標本の一部にすぎないため、動揺することはありません。将来を確認します。

将来1年

再度、このパターンは一年中機能します。1番目の最大の注文を考慮しなくても、残りの部分も肯定的です。

次に、2番目のEAを確認しましょう。

ボット#2 EURUSD H4 2010.01.01-2020.01.01 MetaTrader 5

これはすべての中で最も滑らかなグラフです。これがフォワードです。

将来1年

すべてが期待どおりに機能します。最初は大きな丘がありますが、グラフは直線に似てさえいます。2つの異なる通貨ペアで5つのエキスパートアドバイザーをテストしましたが、すべてのフォワードテストは肯定的でした。これは良い統計です。ただし、もっとデータが必要です。絶対に異なるペアと異なる時間枠を確認することにしました。

EURJPY M5のテスト:

EURJPY M5 2010.01.01-2020.01.01 MetaTrader 5

訓練間隔は奇妙に見えます。2015年には顕著な逆転が見られますが、全体的な結果は良好に見えます。フォワード期間は次のとおりです。

将来1年

結論を出すのは難しいですが、これが最初の否定的なフォワードテストであることは明らかです。 それでも、この結果は、すべてのアルゴリズムの安定性に関する仮定に反論しているとは思いません。バックテストで2015年に明確な逆転があったためです。結果を自分の市場のビジョンに適合させようとしているわけではありませんが、その逆転の理由がわかります。それどころか、2015年に起こったその逆転の継続に対処しているのです。

このような外部結果の影響を回避するには、パターン検索アルゴリズムにいくつかの変更を加える必要があります。このアルゴリズムの変更は、次のバージョンで実装される予定です。


結論

上記のテストは、MetaTrader4およびMetaTrader5ターミナルで機能するエキスパートアドバイザーの検索とテストに関していくつかの重要な結論を引き出すのに十分であると信じています。これら2つのターミナルのテスターは、動作が少し異なります。MetaTrader 4での相場はスプレッドデータなしで履歴を保存するため、スプレッドを「1」に設定できます。これは「0」とほぼ同じです。ほとんどのエキスパートアドバイザーにとって、スプレッドはまったく問題ではありませんが、あまり明白ではないパターンを検出して開発することはできます。これはMetaTrader5では不可能です。しかし、MetaTrader 5の相場にはスプレッド情報が含まれているため、システムの実際の収益性を評価できます。

私の経験によると、もともとMetaTrader 4用に書かれたシステムがMetaTrader 5用に適切に変換されなかった場合、そのようなシステムは利益を示す可能性がほとんどありません。見つけることができるパターンのほとんどはスプレッド内にあって役に立たないからです。場合によっては、すべてのシグナルの中で最小のスプレッドを持つシグナルを見つけることができるかもしれませんが、これが常に可能であるとは限りません。

多くの場合、必要なスプレッドを減らすとシグナルが悪化します。この場合、許容できるテストを取得するためにMetaTrader 5テストのスプレッドを手動で選択しましたが、これには非常に長い時間がかかります。これは、プログラムを変更することで自動的に実行できます。次の図は、最終段階までの現在のパターン検索プロセスと、スプレッド選択プロセスの可能な自動化を示しています。

開発サイクル

この図は、新しいEAの開発サイクル全体を最初から最後まで示しています。現在実装されているものは黒で示されています。可能な変更には灰色が使用されています。さらに可能な改善は6つのカテゴリに分けられます。

  1. 最も単純な数学関数を使用してバーデータを処理する
  2. グローバルパターンを実装するエキスパートアドバイザーのPFを高めるために、ロット変動メカニズムを追加する
  3. 超短期間で作業するために、マーチンゲールおよびリバースマーチンゲールメカニズムを追加する
  4. 取引頻度を最大にするために、1つのボットに優れたEAを組み合わせるメカニズムを追加する
  5. 相場にスプレッドを追加して、手動でスプレッドを選択することを回避する(開発サイクル全体の完全自動化)
  6. 検出されたオプティマイザエラーを修正する

ポイント1、5、6を強調したいと思います。総当たり攻撃式の変動性により、1つの総当たり攻撃最適化サイクルからエキスパートアドバイザーのより多くの異なるバリアントを作成し、結果の平均品質を向上させることができます。以前の記事へのコメントで、ユーザーが、フーリエ級数を使用して定期的な市場プロセスを説明することを提案しました。このタスクは後で変更するために残したかったのですが、その方法はそれほど難しくありません。私が使用するテイラー級数と同様に、関数を級数に変換する必要はなく、項の係数を見つけるだけで済みます。さらに、この方法の変動性は、テイラー級数の変動性よりもはるかに高くなります。私のアルゴリズムでは、最初の次数のみを使用しました。次数が増えると、計算の複雑さが不均衡に増加するため、品質が低下するためです。スプレッドも非常に重要です。自動的にできるのに、シグナルを手動でフィルタリングするのは何故でしょうか。バーの時間の不一致もありましたが、これらのエラーは修正されました。このような不一致は、テンプレートの機能とソフトウェアの機能の違いによるものでした。

このソフトウェアの最終的な目的は、両方のプラットフォームでのエキスパートアドバイザーの開発、テスト、最適化を含むプロセス全体を自動化することです。私がまだ手動で実行しているアクションも自動化できます。さらに、マシンは適切なバリアントをはるかに速く見つけることができます。正直なところ、これは、スプレッドサイズなどの基本的なものを手動で構成したくないという私の怠け心によるものです。このプロセスによって、取引システムの開発サイクルが大幅に増加します。重要なのは、時間単位ごとに得られる結果の量と質です。これは、さらなる利益に直接影響するためです。さらに、システムの構成に時間がかかりすぎると意味がなくなる可能性があります。見つかったエキスパートアドバイザーのほとんどは非常に限られた時間しか機能しないため、すべてを素早くやる必要があります。毎月オプティマイザを使用することもできますが、これはよい解決策ではありません。

アイデアにさらなる発展の可能性を見つけたら、それを発展させ続けます。アイデアが成功した場合、勾配ブースティング用の同様のプログラムを作成するか、データ分析の第3段階の形式で新しいモードを追加します。手法自体が勾配ブースティングの予測検索エンジンとして非常に優れているためです。したがって、新しい予測子を発明してテストする代わりに、この手法で見つかったものを使用するだけで済みます。ただし、これには、はるかに本格的で詳細な変更が必要なため、今はやりません。しかし、私が上で述べたいくつかの変更はすでに実装されており、現在テストして次の記事のためにデータを収集しています。「この続きはまた後で」ということになります。

以前の記事はすべて主にMetaTrader 4に焦点を当てていましたが、実践によってわかったように、自動化によりMetaTrader 5に切り替えることができます。このアプローチは、次の記事で完全に実装されます。

MetaTrader 5ははるかに進歩したプラットフォームであり、実際のティック履歴とスプレッドデータを提供します。これによってあらゆる取引システムの実際の収益性を評価できるため、このプラットフォームに焦点を当てるほうが良いです。

テストプロセス中に、生成されたエキスパートアドバイザーの品質とMetaTrader5の実際のティックデータでのテストに合格できるEAの割合を低下させる非常に重要な要因を見つけました。この要素は、機械学習ベースのシステムだけでなく、MetaTrader4で最初に作成された他のエキスパートアドバイザーすべてにとっても非常に重要です。これは、スプレッドと、それが訓練の最終データ標本にどのように影響するかと関連しています。多くのユーザーはスプレッドを無視するか、取引時にのみ考慮しますが、スプレッドには、機械学習プロセスに非常に悪影響を与える、目に見えないが非常に重要なコンポーネントが1つ含まれています。詳細は次の記事でお伝えします。


終わりに

この記事からの最も重要な結論は、プログラムの実際のパフォーマンスの可能性を高めたいなら自動売買ロボットをMQL5に実装する必要があるということです。MQL5プログラムは、実際のプログラムに非常に近い条件でテストできます。MetaTrader 5でのテストなしでは真に信頼できる結果を得ることができないことが詳細な分析によって示されています。

この記事が示すように、線形多項式や高次多項式などの非常に単純な数式を使用すると、限られた計算能力でも結果を生成できます。このプログラムは、単純な数学関数をさらに使用するための基礎を提供します。結果は、最も単純な関数の線形と累乗の組み合わせで優れた取引シグナルを提供できることを示しています。

また、結果は、論理条件の任意のセットを関数の形式で単一の定量的指標に減らすことができることを示しています。実際、式は、売買シグナルとして使用できる対称的な正と負の値を生成するインディケータ―を構築します。インディケーターの式は常に異なり、これにより手法の変動性が保証されます。多くのトレーダーは、売買の正確なタイミング知るためのインディケータ―を夢見ていると思います。この手法では、市場で許容される最大限の範囲でこの機会を実装します。次の記事でははるかに優れた結果を提供します。連載全体を通してなされたすべての結論を統合し、取引システムの手動作成と機械学習プロセスの両方に役立つ詳細についても説明しようと思います。

参照文献

  1. パターン検索への総当たり攻撃アプローチ
  2. パターン検索への総当たり攻撃アプローチ(第II部): イマージョン


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

添付されたファイル |
Bots.zip (880.06 KB)
自己適応アルゴリズムの開発(第II部): 効率の向上 自己適応アルゴリズムの開発(第II部): 効率の向上
この記事では、以前に作成したアルゴリズムの柔軟性を向上させることでトピックの開発を続けます。アルゴリズムは、分析期間内のローソク足の数の増加または上昇/下降ローソク足超過率のしきい値の増加によって、より安定しました。分析のためにより大きなサンプルサイズを設定するかより高いローソク足の超過率を設定して、妥協する必要がありました。
CatBoostアルゴリズムを使用した外国為替市場の季節によるパターンの特定 CatBoostアルゴリズムを使用した外国為替市場の季節によるパターンの特定
本稿では、時間フィルタを使用した機械学習モデルの作成について検討し、このアプローチの有効性について説明します。人的要因はモデルに特定の曜日の特定の時間に取引するように指示するだけで排除できるようになっています。パターン検索は、別のアルゴリズムで提供できます。
自己適応アルゴリズム(第III部):最適化の放棄 自己適応アルゴリズム(第III部):最適化の放棄
履歴データに基づく最適化を使用してパラメータを選択する場合、真に安定したアルゴリズムを取得することは不可能です。安定したアルゴリズムは、常時、どんな取引商品で作業していても、必要なパラメータを認識している必要があります。予測や推測ではなく、確実に知っているべきです。
市場とそのグローバルパターンの物理学 市場とそのグローバルパターンの物理学
本稿では、市場を少しでも理解してるシステムはどれでも世界規模で運用できるという前提を試してみます。理論やパターンは発明せずに既知の事実のみを使用し、これらの事実を徐々に数学的分析の言語に翻訳していきます。