English Русский 中文 Español Deutsch Português
マルチモジュールEAの作成

マルチモジュールEAの作成

MetaTrader 5トレーディング | 6 7月 2018, 14:25
1 482 0
Sergey Pavlov
Sergey Pavlov

概論

今日、プログラミングには、モジュラー、オブジェクト指向、構造化などいくつかのアプローチがあります。この記事では、トレーディングロボットに関するモジュラ―プログラミングについてお話しします。

モジュール式プログラミングは、プログラムを独立したモジュールに分割するプログラム開発方法です。

モジュール式プログラミングの主なルールは、『分割と統御』の原則です。モジュール式アーキテクチャを使用する利便性は、システムの残りの部分を変更する必要なく、モジュールを更新(交換)することができることです。

モジュラープログラミングの基礎には、3つの基本概念があります。

  • パーナスの情報隠蔽の法則。モジュールとは、特定の問題を解決するための情報とアルゴリズムを非表示にし、将来、別のモジュールに置き換えることができる。
  • コーエンのモジュール公理。モジュールとは、プログラムの特定の機能を実行するために機能する独立したプログラムユニットである。
  • ツェイチンのアセンブリプログラミング。モジュールとは、プログラムを構築するプログラムの「レンガ」である。

モジュールに代わる唯一のものは、モノリシックプログラムです。これはあまり便利ではありません。プログラムの機能の一部を変更または補足する必要がある場合、EAのコードを編集する必要があります。コードの作成者または経験豊富なプログラマーであれば、ほとんどの場合これを行うことができると思います。モノリシックプログラムがさらにコンパイルされた場合、製品は著作権者によってのみ編集可能であり、製作者と同意すればいいというものではありません。このような背景から、プログラムの重要な機能を変更する為に、製作者に問い合わせるのではなく、自分自身や第三者の開発者のサービスを使用する方がよっぽど便利に思えます。


図 1. モジュール式取引ロボットの抽象化

マルチモジュールの原則

モジュール式プログラミングは、タスクを複数のサブタスクに分割し、別々のモジュール(ファイル)として実装する技術です。一般的にはプログラムモジュールとは、別個のプログラム、または機能的に完全に自律してコンパイルするソフトウェアユニットであり、何らかの形で呼び出されたモジュールと結合および識別するものです。言い換えれば、モジュールは機能的に完成したプログラムの断片であり、別のコンパイル済みファイルとして設計されていて、他のプログラムでの使用を意図しています。

特定のアルゴリズムの機能を実装するモジュールのセットを決定する際には、以下のことを考慮する必要があります。

  • 各モジュールは親モジュールによって呼び出され、処理が完了すると、呼び出したモジュールに制御が返される。
  • アルゴリズムにおける主要な決定は、階層内の最高レベルで行われる。
  • モジュールはお互いにデータから独立している。
  • モジュールは、モジュールへのアクセスの履歴に依存しない。

したがって、上記のすべてを要約すると、モジュラープログラムは、他の部分に変更を加えることなく論理構造の任意の部分を変更できるプログラムということになります。

モジュールの主な特性

  • 1つの入力と1つの出力 ー 入力時にプログラムモジュールは、所定のセットの初期データを受け取り、それらを処理し、また1組の結果データを返す、すなわち、 IPO(Input-Process-Output)の原則が導入されている。
  • 機能的完全性 ー 別個の機能を実行するために、モジュールは、開始された処理を完了するのに十分で調整された操作のリストを全て実行する。
  • 論理的独立 ー ソフトウェアモジュールの動作結果はソースデータにのみ依存し、他のモジュールの動作には依存しない。
  • 他のソフトウェアモジュールとの情報リンクの弱さ - モジュール間の情報の交換は可能な限り最小限に抑えるべきである。

MQL5言語では、EA、インジケータ、スクリプトの3種類のプログラムを記述できます。メインモジュールには、すべてのモジュールの管理が整理され、取引機能が配置されるEA形式が最適です。しかし、他のモジュールは、インジケータの形式などで実装することもできます。実際にインジケータはモジュールを構成するのに理想的なものです。与えられたアルゴリズムで計算されたデータはインジケータバッファに格納され、必要に応じてマルチモジュールアドバイザーに転送されます。EAは、タスクに応じて、このデータを使用または無視できます。いくつかのプロジェクトでは、EAを外部モジュールとして使用することは正当化されるかもしれませんが、同時にデータ交換の仕組みを詳細に考える必要があります。

恐らく、あなたが自分のEAの中で、取引シグナルのフィルタリングや生成モジュールとしてカスタムインジケータを使用したことは何度もあると思います。

私の意見としての最も合理的な結論は、すべての基本機能はメインモジュールに集中させ、外部モジュールを使用する必要はないというものです。異なる市場条件に応じて、取引ストラテジーを改善するために外部モジュールは必要です。プログラム機能のセットは、コードやストラテジーの作成者ではなく、取引ロボットのトレーダーユーザーによって決定されます。お互いに法的な権利に違反しないように注意をすることが重要になります。

メインモジュールはEAです。

プロジェクト全体の管理が集中しているメインモジュールは、EAの階層において最も重要なものです。あらゆる取引ストラテジーが無意味になる為、ここには絶対に取引機能を設置しなければなりません。

この記事では、CodeBaseから取得した特定のマルチモジュールアドバイザーの作成をご紹介します。初期アドバイザーは、チャネルの境界線上でポジションを変えるiBandsインジケータのチャネルに固定されたロットで取引します。EAは完全に自立しており、外部プログラムは必要ありません。

すべてのEAはマルチモジュールではありませんが、こういった機能を持っています。

では、モジュラープロジェクトに変える為に、コードに何を追加する必要があるでしょうか?

  1. ユーザが後で使用できる外部モジュール(インジケータ)を宣言します。 
  2. 統合に必要な機能を追加します。
  3. 外部モジュールの開発者用のドキュメントを準備します(別個のファイルにドキュメントを生成する機能を有効にする)。外部モジュールの開発には、メインモジュールが正しく使用できるデータ構造に関する情報が必要です。この例では、マネーマネジメントモジュールはロットサイズをEAにキャストし、ポジショントラッキングモジュールは現在の価格からのポイント単位でキャストをする必要があります。

この変換の結果、モジュラーアドバイザーを獲得でき、最大7つの外部モジュールを統合することができます。

  • モジュール① ー マネーマネジメントのモジュール。終了時に、ロットのサイズを出します。
  • モジュール② ー SLの設置とポジションを追跡するためのモジュール。終了時に、ポジションの開始価格からポイントでストップロスまでの距離を出します。
  • モジュール③ ー TPの設置とポジションを追跡するためのモジュール。終了時に、ポジションの開始価格からポイントで利食いまでの距離を出します。
  • モジュール④ ー トレーリングストップの設置とポジションを追跡するモジュール。終了時に、現在の価格からポイントでストップロスまでの距離を出します。
  • モジュール⑤ ー 取引シグナルを生成するモジュール。終了時に、シグナルの値を出します。
  • モジュール⑥ ー 取引シグナルをフィルタリングするためのモジュール。終了時に、フィルタの値を返します。
  • モジュール⑦ ー 損失のないレベルの設定とポジションの追跡をするためのモジュール。終了時に、ポジションの開始価格からストップロスまでの距離を出します。



 

図2. OnInit()関数と外部モジュールの初期化


図3.OnTick()関数と外部モジュールからのデータの読み取り


図4.OnTrade()関数と外部モジュールからのデータの読み取り



図5. 取引シグナルを生成し、外部モジュールからデータを読み取る機能

 

//****** project (module expert): test_module_exp.mq5
//+------------------------------------------------------------------+
//|          The program code is generated Modular project generator |
//|                      Copyright 2010-2017, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project TEST Main module."
#property link      "The project uses 7 external modules."
//---
#include <Trade\Trade.mqh>
//---
MqlTick    last_tick;
CTrade     trade;
//---
input int                  e_bands_period=80;            // Moving average period
int                        e_bands_shift=0;              // shift
input double               e_deviation=3.0;              // Number of standard deviations
input ENUM_APPLIED_PRICE   e_applied_price=PRICE_CLOSE;  // Price type
input bool                 on_module=false;              // whether or not to use plug-ins
//---
double lot=0.01;           // Fixed lot
double min_lot=0.01;       // Minimum allowable lot
bool   on_trade=false;     // Trade function flag
//--- Variable for storing the indicator iBands handle 
int    handle_Bands;
//--- module 1
bool   on_lot=false;
int    handle_m1;
//--- module 2
bool   on_SL=false;
int    handle_m2;
//--- module 3
bool   on_TP=false;
int    handle_m3;
//--- module 4
bool   on_Trail=false;
int    handle_m4;
//--- module 5
bool   on_signals=false;
int    handle_m5;
//--- module 6
bool   on_Filter=false;
int    handle_m6;
//--- module 7
bool   on_Breakeven=false;
int    handle_m7;
//+------------------------------------------------------------------+
//| Structure of trading signals                                     |
//+------------------------------------------------------------------+
struct sSignal
  {
   bool              Buy;    // Buy signal
   bool              Sell;   // Sell signal
  };
//+------------------------------------------------------------------+
//| Trading signals generator                                        |
//+------------------------------------------------------------------+
sSignal Buy_or_Sell()
  {
   sSignal res={false,false};
//--- MODULE 5
   if(on_signals)
     { // If there is an additional module
      double buffer_m5[];
      ArraySetAsSeries(buffer_m5,true);
      if(CopyBuffer(handle_m5,0,0,1,buffer_m5)<0) return(res);
      if(buffer_m5[0]<-1) res.Sell=true;
      if(buffer_m5[0]>1) res.Buy=true;
     }
//--- MODULE 6
   if(on_Filter)
     { // If there is an additional module
      double buffer_m6[];
      ArraySetAsSeries(buffer_m6,true);
      if(CopyBuffer(handle_m6,0,0,1,buffer_m6)<0) return(res);
      lot=buffer_m6[0];
      if(buffer_m6[0]<1) res.Buy=false;
      if(buffer_m6[0]>-1) res.Sell=false;
     }
//---
//--- Indicator buffers
   double         UpperBuffer[];
   double         LowerBuffer[];
   double         MiddleBuffer[];
   ArraySetAsSeries(MiddleBuffer,true); CopyBuffer(handle_Bands,0,0,1,MiddleBuffer);
   ArraySetAsSeries(UpperBuffer,true);  CopyBuffer(handle_Bands,1,0,1,UpperBuffer);
   ArraySetAsSeries(LowerBuffer,true);  CopyBuffer(handle_Bands,2,0,1,LowerBuffer);
//--- Timeseries
   double L[];
   double H[];
   ArraySetAsSeries(L,true); CopyLow(_Symbol,_Period,0,1,L);
   ArraySetAsSeries(H,true); CopyHigh(_Symbol,_Period,0,1,H);
   if(H[0]>UpperBuffer[0]&& L[0]>MiddleBuffer[0]) res.Sell=true;
   if(L[0]<LowerBuffer[0] && H[0]<MiddleBuffer[0]) res.Buy=true;
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create the indicator handle
   handle_Bands=iBands(_Symbol,_Period,e_bands_period,e_bands_shift,e_deviation,e_applied_price);
   if(handle_Bands==INVALID_HANDLE)
      return(INIT_FAILED);
   else
      on_trade=true;
   if(on_module)
     {
      //--- MODULE 1
      //--- check: whether there is an external module?
      handle_m1=iCustom(NULL,0,"Market\\test_module_MM");
      if(handle_m1!=INVALID_HANDLE)
         on_lot=true;
      //--- MODULE 2
      //--- check: whether there is an external module?
      handle_m2=iCustom(NULL,0,"Market\\test_module_SL");
      if(handle_m2!=INVALID_HANDLE)
         on_SL=true;
      //--- MODULE 3
      //--- check: whether there is an external moduleь?
      handle_m3=iCustom(NULL,0,"Market\\test_module_TP");
      if(handle_m3!=INVALID_HANDLE)
         on_TP=true;
      //--- MODULE 4
      //--- check: whether there is an external module?
      handle_m4=iCustom(NULL,0,"Market\\test_module_Trail");
      if(handle_m4!=INVALID_HANDLE)
         on_Trail=true;
      //--- MODULE 5
      //--- check: whether there is an external module?
      handle_m5=iCustom(NULL,0,"Market\\test_module_signals");
      if(handle_m5!=INVALID_HANDLE)
         on_signals=true;
      //--- MODULE 6
      //--- check: whether there is an external module?
      handle_m6=iCustom(NULL,0,"Market\\test_module_Filter");
      if(handle_m6!=INVALID_HANDLE)
         on_Filter=true;
      //--- MODULE 7
      //--- check: whether there is an external module?
      handle_m7=iCustom(NULL,0,"Market\\test_module_Breakeven");
      if(handle_m7!=INVALID_HANDLE)
         on_Breakeven=true;
     }
//--- Minimum allowable volume for trading operationsn
   min_lot=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
//--- MODULE 1
   if(on_lot)
     { // If there is an additional module
      double buffer_m1[];
      ArraySetAsSeries(buffer_m1,true);
      if(CopyBuffer(handle_m1,0,0,1,buffer_m1)<0) return;
      lot=buffer_m1[0];
     }
//--- MODULE 4
   if(on_Trail)
      if(PositionSelect(_Symbol))
         if(PositionGetDouble(POSITION_PROFIT)>0)
           { // If there is an additional module
            double buffer_m4[];
            ArraySetAsSeries(buffer_m4,true);
            if(CopyBuffer(handle_m4,0,0,1,buffer_m4)<0) return;
            double TR=buffer_m4[0];
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
               if(PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point>PositionGetDouble(POSITION_PRICE_OPEN))
                 {
                  double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point;
                  if(price_SL>PositionGetDouble(POSITION_SL))
                     trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0);
                 }
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
               if(PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point<PositionGetDouble(POSITION_PRICE_OPEN))
                 {
                  double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point;
                  if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL)==NULL)
                     trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0);
                 }
           }
//--- MODULE 7
   if(on_Breakeven)
      if(PositionSelect(_Symbol))
         if(PositionGetDouble(POSITION_PROFIT)>0)
           { // If there is an additional module
            double buffer_m7[];
            ArraySetAsSeries(buffer_m7,true);
            if(CopyBuffer(handle_m7,0,0,1,buffer_m7)<0) return;
            double TRB=buffer_m7[0];
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
               if(PositionGetDouble(POSITION_PRICE_CURRENT)-TRB*_Point>PositionGetDouble(POSITION_PRICE_OPEN))
                 {
                  double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-5*_Point;
                  if(price_SL>PositionGetDouble(POSITION_SL))
                     trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),PositionGetDouble(POSITION_TP));
                 }
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
               if(PositionGetDouble(POSITION_PRICE_CURRENT)+TRB*_Point<PositionGetDouble(POSITION_PRICE_OPEN))
                 {
                  double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+5*_Point;
                  if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL)==NULL)
                     trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),PositionGetDouble(POSITION_TP));
                 }
           }
//---
   if(lot<min_lot) lot=min_lot;
//---
   if(on_trade)
     {
      sSignal signal=Buy_or_Sell();
      //--- The value of the required and free margin
      double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
      //--- BUY
      if(signal.Buy)
        {
         if(!PositionSelect(_Symbol))
           {
            SymbolInfoTick(_Symbol,last_tick);
            if(OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,NormalizeDouble(lot,2),last_tick.ask,margin))
               if(margin<Equity)
                  trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(lot,2),last_tick.ask,0,0,"BUY: new position");
           }
         else
           {
            if(PositionGetDouble(POSITION_PROFIT)<0) return;
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               trade.PositionClose(_Symbol);
               SymbolInfoTick(_Symbol,last_tick);
               if(OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,NormalizeDouble(lot,2),last_tick.ask,margin))
                  if(margin<Equity)
                     trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,NormalizeDouble(lot,2),last_tick.ask,0,0,"BUY: reversal");
              }
           }
        }
      //--- SELL
      if(signal.Sell)
        {
         if(!PositionSelect(_Symbol))
           {
            SymbolInfoTick(_Symbol,last_tick);
            if(OrderCalcMargin(ORDER_TYPE_SELL,_Symbol,NormalizeDouble(lot,2),last_tick.bid,margin))
               if(margin<Equity)
                  trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(lot,2),last_tick.bid,0,0,"SELL: new position");
           }
         else
           {
            if(PositionGetDouble(POSITION_PROFIT)<0) return;
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               trade.PositionClose(_Symbol);
               SymbolInfoTick(_Symbol,last_tick);
               if(OrderCalcMargin(ORDER_TYPE_SELL,_Symbol,NormalizeDouble(lot,2),last_tick.bid,margin))
                  if(margin<Equity)
                     trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,NormalizeDouble(lot,2),last_tick.bid,0,0,"SELL: reversal");
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
   if(on_SL && on_TP) // If there is an additional module
     {
      //--- MODULE 2
      double buffer_m2[];
      ArraySetAsSeries(buffer_m2,true);
      if(CopyBuffer(handle_m2,0,0,1,buffer_m2)<0) return;
      double SL=buffer_m2[0];
      //--- MODULE 3
      double buffer_m3[];
      ArraySetAsSeries(buffer_m3,true);
      if(CopyBuffer(handle_m3,0,0,1,buffer_m3)<0) return;
      double TP=buffer_m3[0];
      //--- Position modification
      if(PositionSelect(_Symbol))
         if(PositionGetDouble(POSITION_SL)==0)
           {
            //--- BUY
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)+TP*_Point;
               double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)-SL*_Point;
               trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),NormalizeDouble(priceTP,Digits()));
              }
            //--- SELL
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)-TP*_Point;
               double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)+SL*_Point;
               trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),NormalizeDouble(priceTP,Digits()));
              }
           }
     }
  }

プログラムに指定されたカタログに必要なモジュール(ファイル)が含まれていない場合、取引ロボットはデフォルト機能を使用します。したがって、外部モジュールがないことで、EAの動作に重大な影響を与えることはありません。

最も重要なモジュール

マルチモジュールアドバイザーをモノリシックプログラムから作るにはどうすればよいでしょうか?モジュラープロジェクトは、一般的なタスクの分析と、コンパイルされたモジュールの形式で形式化することができる機能的に閉じられたフラグメントの選定から始まります。この場合、EAの作業を大幅に変更し、さまざまなアルゴリズムに基づく最も一般的な機能を選択する必要があります。ほとんどのEAが同じ手順を踏んでいることはよく知られています。 

  • 資本(リスク)管理のモジュール。
  • ポジションを追跡するためのモジュール(SLおよびTP)。
  • トレーリングストップモジュール。
  • 取引シグナルを生成するためのモジュール。
  • シグナルフィルタリングモジュール。

リストアップされた各モジュールを実装するための多くのオプションがあります。この記事では、私たちにとってモジュラープログラミングの技術がマルチライン機能よりも重要であるため、最も原始的な方法を記述します。

補助モジュールを作成する技術

補助(外部)モジュールは、特定の機能を実行し、出力データをインジケータバッファに格納するインジケータです。メインモジュールは必要に応じてこのデータを使用します。したがって、EAはこの取引ストラテジーを使用するトレーダーの要件に適応します。特定の金融商品またはブローカーごとに、同じソースアドバイザーを再構築することができます。実際、トレーダーの元にはトレーディングロボットを無制限に収集することができるコンストラクタが現れます。

プログラミングは時間のかかるプロセスです。創造的なコンポーネントが存在するにもかかわらず、ここには自動化した方がよいルーティーンやワンタイプ操作がとてもたくさん含まれています。自動化は、プログラミングのパフォーマンスを向上させ、エラーを低減します。

幸いなことに、モジュールジェネレータは、複数のモジュールプロジェクトに接続された最大8つのファイルを数秒で生成することができます。これにより、開発や構築のプロセスが大幅に簡素化され、スピードアップできます(ビデオ参照)。

マルチモジュールプロジェクトのジェネレータコントロールパネル

ビデオ1マルチモジュールプロジェクトのジェネレータコントロールパネル

コントロールパネルでは、新規プロジェクト用に生成するモジュールを指定できます。この例では、「test」プロジェクトが作成されています。選択したモジュールの組み合わせに応じて、ジェネレータは不要なブロックやファイルができないように、コードを自動的に生成します。

生成されたファイルは "Files"フォルダに置かれます。(図2参照)メインモジュール "test_module_exp.mq5"の名前は、プロジェクト "test"の名前と接頭辞 "_module_exp.mq5"で構成されています。これを"Experts"フォルダに移し、外部モジュールの残りのファイルは、"Indicators \\ Market"フォルダに移動する必要があります。

図6. 生成されたプロジェクトファイル "test"。

図6. 生成されたプロジェクトファイル "test"。

その後、すべてのファイルをコンパイルし、マルチモジュールプロジェクトのテストに進みます。

ビデオ2生成されたプロジェクトファイル "test"のコンパイル
  

同様のプロジェクトを手動で作成するには1時間以上かかりますが、メインモジュールから開始する必要があるのは明らかです。プロジェクトに接続できる外部モジュールを決定したら、設計とプログラミングに進みます。コントロールすべき主なものは、メインモジュールが待つ補助モジュールの出力データです。モジュールはインジケータであるため、 インジケータバッファが排他的実数型の値を含む場合、メインモジュールでは、実数型からアルゴリズムに対応する型への変数の変換を提供する必要があります。

入力パラメータなしで、つまりデフォルトで外部モジュールをメインモジュールで呼び出すことができるように設計する必要があります。このような呼出しの構図は、外部データ管理システムの設計を単純化します。

取引ストラテジーの中でどの外部モジュールが最も需要が高いかをより詳細に見ていきましょう。

例1:マネー管理モジュール

この外部モジュールは、次の注文を開くロットの金額を計算します。以下は取引量の計算の最も簡単なバージョン(利用可能な資金のパーセンテージ)です。

//****** project (module MM): test_module_MM_ind.mq5
//+------------------------------------------------------------------+
//|          The program code is generated Modular project generator |
//|                      Copyright 2010-2017, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project test module MM"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots   1
//---
input double   lot_perc=0.1;  // Percentage of Equity value
double         Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ArraySetAsSeries(Buffer1,true);
   SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
   return(INIT_SUCCEEDED);
  };
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
//--- calculation of the lot of equity
   Buffer1[0]=NormalizeDouble(Equity*lot_perc/1000.0,2); // Lot size determination function
   if(Buffer1[0]<0.01) Buffer1[0]=0.01;
   return(rates_total);
  };

このモジュールをデフォルトで(入力パラメータの値を指定せずに)呼び出すと、メインモジュールは承認されたロットのサイズを受け取り、使用可能な資金の0.1%の額でトランザクションを完了します。メインプログラムからこのモジュールを呼び出す例:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   double Equity=AccountInfoDouble(ACCOUNT_EQUITY);
//--- MODULE 1
   if(on_lot)
     { // If there is an additional module
      double buffer_m1[];
      ArraySetAsSeries(buffer_m1,true);
      if(CopyBuffer(handle_m1,0,0,1,buffer_m1)<0) return;
      lot=buffer_m1[0];
     }
  
  ...
  
  }

例2:ポジション追跡モジュール(SL、TPおよびトレーリング)

ストップロス(SL)とテイクプロフィット(TP)の導入は、オープンポジションを維持する方法の1つです。異なる取引ストラテジーでSLとTPの計算と設定のさまざまな組み合わせが使用されるため、SLとTPに対して別々に2つのモジュールに分割するオプションが提案されます。しかし、SLとTPを1つのモジュールで組み合わせるようにするなら、それらの値は異なるインジケータバッファに置く必要があります。

SL設定モジュール

//****** project (module SL): test_module_SL_ind.mq5
//+------------------------------------------------------------------+
//|          The program code is generated Modular project generator |
//|                      Copyright 2010-2017, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project test module SL"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots   1
//---
double      Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ArraySetAsSeries(Buffer1,true);
   SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
   return(INIT_SUCCEEDED);
  };
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   double SL=100; // SL in points
//--- calculation of the SL
   Buffer1[0]=SL;
   return(rates_total);
  };

TP設定モジュール

//****** project (module TP): test_module_TP_ind.mq5
//+------------------------------------------------------------------+
//|          The program code is generated Modular project generator |
//|                      Copyright 2010-2017, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project test module TP"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots   1
//---
double      Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ArraySetAsSeries(Buffer1,true);
   SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
   return(INIT_SUCCEEDED);
  };
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   double TP=100; // TP in points
//--- calculation of the TP
   Buffer1[0]=TP;
   return(rates_total);
  };

コードでは、SLおよびTP値をポイントで計算するという、最も分かりやすいオプションが示されています。正確には、それは計算されないが、定数によって与えられます。この時、値はプログラムで直接指定され、入力パラメータでは指定されません。これは、入力データなしで外部モジュールの実装をデモンストレーションするために行われました。このようなコードは、どんな初心者プログラマでも書くことができます。

ここで取り上げたモジュールの呼び出しは、OnTrade関数に配置することをお勧めします。これは次のようになります。

//+------------------------------------------------------------------+
//| Trade function                                                   |
//+------------------------------------------------------------------+
void OnTrade()
  {
   if(on_SL && on_TP) // If there is an additional module
     {
      //--- MODULE 2
      double buffer_m2[];
      ArraySetAsSeries(buffer_m2,true);
      if(CopyBuffer(handle_m2,0,0,1,buffer_m2)<0) return;
      double SL=buffer_m2[0];
      //--- MODULE 3
      double buffer_m3[];
      ArraySetAsSeries(buffer_m3,true);
      if(CopyBuffer(handle_m3,0,0,1,buffer_m3)<0) return;
      double TP=buffer_m3[0];
      //--- Position modification
      if(PositionSelect(_Symbol))
         if(PositionGetDouble(POSITION_SL)==0)
           {
            //--- BUY
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)+TP*_Point;
               double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)-SL*_Point;
               trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),NormalizeDouble(priceTP,Digits()));
              }
            //--- SELL
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               double priceTP=PositionGetDouble(POSITION_PRICE_OPEN)-TP*_Point;
               double priceSL=PositionGetDouble(POSITION_PRICE_OPEN)+SL*_Point;
               trade.PositionModify(_Symbol,NormalizeDouble(priceSL,Digits()),NormalizeDouble(priceTP,Digits()));
              }
           }
     }
  }

ポジションが開かれた直後に設定される静的SLおよびTP値に加えて、トレーリングストップまたはフローティングSLが使用されることがよくあります。これはポジションが利益を上げた後に最も頻繁に設定されます。最も分かりやすい実装例を見てみましょう。現在の価格からSLの距離をパラグラフに設定します。

//****** project (module Trail): test_module_Trail_ind.mq5
//+------------------------------------------------------------------+
//|          The program code is generated Modular project generator |
//|                      Copyright 2010-2017, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project test module Trail"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots   1
//---
double      Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ArraySetAsSeries(Buffer1,true);
   SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
   return(INIT_SUCCEEDED);
  };
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   double TR=50;  // Trail in points
//--- calculation of Trail
   Buffer1[0]=TR;
   return(rates_total);
  };

ここでは、前回のSLとTPのコードと同様に、プログラムと読み込みを簡略化するために、トレーリングストップを計算する距離を定数として与えています。

トレーリングストップモジュールの呼び出しは、OnTick関数内で実装する必要があります。なぜなら、現在の価格はすべてのティックで変化し、ストップレベルは継続的に監視されなければならないからです。しかし、それを変更するかどうかは、すでにメインモジュールによって決定されています。インデントの値をポイントで受け取ったアドバイザーは、ポジションを変更し、SLレベルを利益の成長に向けて動かします。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

   ...

//--- MODULE 4
   if(on_Trail)
      if(PositionSelect(_Symbol))
         if(PositionGetDouble(POSITION_PROFIT)>0)
           { // If there is an additional module
            double buffer_m4[];
            ArraySetAsSeries(buffer_m4,true);
            if(CopyBuffer(handle_m4,0,0,1,buffer_m4)<0) return;
            double TR=buffer_m4[0];
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
               if(PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point>PositionGetDouble(POSITION_PRICE_OPEN))
                 {
                  double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-TR*_Point;
                  if(price_SL>PositionGetDouble(POSITION_SL))
                     trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0);
                 }
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
               if(PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point<PositionGetDouble(POSITION_PRICE_OPEN))
                 {
                  double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+TR*_Point;
                  if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL)==NULL)
                     trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),0);
                 }
           }

   ...

  }

ポジションを追跡するもう1つの方法があります。SLを「損益分岐点」に置くことです。 SLがトリガーされた場合、ポジションはゼロまたは所定の利益でもって終了します。モジュールは次のようになります。

//****** project (module Breakeven): test_module_Breakeven_ind.mq5
//+------------------------------------------------------------------+
//|          The program code is generated Modular project generator |
//|                      Copyright 2010-2017, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project test module Breakeven"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots   1
//---
double      Buffer1[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ArraySetAsSeries(Buffer1,true);
   SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
   return(INIT_SUCCEEDED);
  };
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   double Breakeven=100; // Breakeven in points
//--- calculation of the Breakeven
   Buffer1[0]=Breakeven;
   return(rates_total);
  };

このモジュールでは、現在価格のポジションの開始価格からの距離は、SLを「損益分岐点」に置くためのポイントに指定されます。損益分岐点モジュールの呼び出しも、OnTick関数に配置する必要があります。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

   ...

//--- MODULE 7
   if(on_Breakeven)
      if(PositionSelect(_Symbol))
         if(PositionGetDouble(POSITION_PROFIT)>0)
           { // If there is an additional module
            double buffer_m7[];
            ArraySetAsSeries(buffer_m7,true);
            if(CopyBuffer(handle_m7,0,0,1,buffer_m7)<0) return;
            double TRB=buffer_m7[0];
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
               if(PositionGetDouble(POSITION_PRICE_CURRENT)-TRB*_Point>PositionGetDouble(POSITION_PRICE_OPEN))
                 {
                  double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)-5*_Point;
                  if(price_SL>PositionGetDouble(POSITION_SL))
                     trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),PositionGetDouble(POSITION_TP));
                 }
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
               if(PositionGetDouble(POSITION_PRICE_CURRENT)+TRB*_Point<PositionGetDouble(POSITION_PRICE_OPEN))
                 {
                  double price_SL=PositionGetDouble(POSITION_PRICE_CURRENT)+5*_Point;
                  if(price_SL<PositionGetDouble(POSITION_SL) || PositionGetDouble(POSITION_SL)==NULL)
                     trade.PositionModify(_Symbol,NormalizeDouble(price_SL,Digits()),PositionGetDouble(POSITION_TP));
                 }
           }

   ...
   
  }

例3:取引シグナルを生成するためのモジュール

おそらくこれは実装が最も複雑なモジュールです。また取引を実行するためのシグナル(注文の発注、ポジションの閉鎖など)が来なければなりません。その設計の複雑さは、ほぼすべてのインジケータが取引条件に適合しなければならないという点にあります。実際には、異なる金融商品の操作シグナルを生成する全く同じ入力パラメータを持つインジケータなどほぼ存在しません。

モジュラープロジェクトの動作が遅延するほど多くのシグナルモジュールを作成するため、メインプログラム自体がシグナルモジュールを構成すべきでないことは明らかです。したがって、取引シグナルを生成するインジケータは、プロジェクト全体に接続する前に事前に準備する必要があります。これについては後ほど、マルチモジュールのEAの最適化に関するセクションで説明します。ここでは、取引シグナルのモジュールを見てみましょう。

//****** project (module signals): test_module_signals_ind.mq5
//+------------------------------------------------------------------+
//|          The program code is generated Modular project generator |
//|                      Copyright 2010-2017, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project test module Trading signals"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots   1
//---
double      Buffer1[];
//--- Variable for storing the indicator iBands handle 
int    handle_Bands;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   handle_Bands=iBands(_Symbol,_Period,20,0,3.5,PRICE_CLOSE);
//---
   ArraySetAsSeries(Buffer1,true);
   SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
   return(INIT_SUCCEEDED);
  };
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   double signal=0.0;
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
//--- Indicator buffers
   double         UpperBuffer[];
   double         LowerBuffer[];
   ArraySetAsSeries(UpperBuffer,true);  CopyBuffer(handle_Bands,1,0,1,UpperBuffer);
   ArraySetAsSeries(LowerBuffer,true);  CopyBuffer(handle_Bands,2,0,1,LowerBuffer);
//--- calculation of the Trading signals
   if(high[0]>UpperBuffer[0]) signal=-2.0;
   if(low[0]<LowerBuffer[0]) signal=2.0;
   Buffer1[0]=signal;
   return(rates_total);
  };

シグナルモジュールのインジケータバッファには、以下の値が書き込まれます。

  • 2.0 ー BUYシグナルが生成された場合。
  • 0.0 ー 取引シグナルがない場合。
  • -2.0 ー SELLシグナルが生成された場合。

取得された取引シグナルのモジュールの値は、メインモジュールの特別な機能で最もよく使用されます。例は以下のようになります。

//+------------------------------------------------------------------+
//| Trading signals generator                                        |
//+------------------------------------------------------------------+
sSignal Buy_or_Sell()
  {
   sSignal res={false,false};
//--- MODULE 5
   if(on_signals)
     { // If there is an additional module
      double buffer_m5[];
      ArraySetAsSeries(buffer_m5,true);
      if(CopyBuffer(handle_m5,0,0,1,buffer_m5)<0) return(res);
      if(buffer_m5[0]<-1) res.Sell=true;
      if(buffer_m5[0]>1) res.Buy=true;
     }
//--- MODULE 6
   if(on_Filter)
     { // If there is an additional module
      double buffer_m6[];
      ArraySetAsSeries(buffer_m6,true);
      if(CopyBuffer(handle_m6,0,0,1,buffer_m6)<0) return(res);
      lot=buffer_m6[0];
      if(buffer_m6[0]<1) res.Buy=false;
      if(buffer_m6[0]>-1) res.Sell=false;
     }
//---
//--- Indicator buffers
   double         UpperBuffer[];
   double         LowerBuffer[];
   double         MiddleBuffer[];
   ArraySetAsSeries(MiddleBuffer,true); CopyBuffer(handle_Bands,0,0,1,MiddleBuffer);
   ArraySetAsSeries(UpperBuffer,true);  CopyBuffer(handle_Bands,1,0,1,UpperBuffer);
   ArraySetAsSeries(LowerBuffer,true);  CopyBuffer(handle_Bands,2,0,1,LowerBuffer);
//--- Timeseries
   double L[];
   double H[];
   ArraySetAsSeries(L,true); CopyLow(_Symbol,_Period,0,1,L);
   ArraySetAsSeries(H,true); CopyHigh(_Symbol,_Period,0,1,H);
   if(H[0]>UpperBuffer[0]&& L[0]>MiddleBuffer[0]) res.Sell=true;
   if(L[0]<LowerBuffer[0] && H[0]<MiddleBuffer[0]) res.Buy=true;
//---
   return(res);
  }

多くの取引ストラテジーがあり、それぞれに独自のシグナルがあります。したがって、取引シグナルのモジュールの作業を所定のストラテジーに適合させるように整理する必要があります。EAへの記述には、シグナルモジュール開発者がモジュラープロジェクトの技術要件に従って行動するように取引ストラテジーを記述する必要があります。

例4:シグナルフィルタモジュール

取引ロボットの収益性を高めるために、取引シグナルのフィルタを使用することがよくあります。これには、例えば、傾向、取引時間、ニュース、追加のシグナルインジケータなどをあげることができます。

//****** project (module Filter): test_module_Filter_ind.mq5
//+------------------------------------------------------------------+
//|          The program code is generated Modular project generator |
//|                      Copyright 2010-2017, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010-2017, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project test module Filter"
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots   1
//---
double      Buffer1[];
//--- Variable for storing the indicator iBands handle 
int    handle_Bands;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   handle_Bands=iBands(_Symbol,_Period,35,0,4.1,PRICE_CLOSE);
//---
   ArraySetAsSeries(Buffer1,true);
   SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
   return(INIT_SUCCEEDED);
  };
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   double filtr=0.0;
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(low,true);
//--- Indicator buffers
   double         MiddleBuffer[];
   ArraySetAsSeries(MiddleBuffer,true);  CopyBuffer(handle_Bands,0,0,1,MiddleBuffer);
//--- calculation of the filter
   if(high[0]<MiddleBuffer[0]) filtr=2.0;
   if(low[0]>MiddleBuffer[0]) filtr=-2.0;
   Buffer1[0]=filtr;
   return(rates_total);
  };

外部モジュールの実装例とこれをモジュラーアドバイザーに統合する原則を紹介しました。

マルチモジュールエキスパートの最適化

マルチモジュールアドバイザの最適化はおそらく基礎的な問題の1つです。実際、ストラテジーテスターで外部モジュールの入力パラメータを最適化する方法はあるでしょうか?それらがメインモジュールに登録されていない場合は、どうにもならない(あるいはほとんど何もできない)ことが分かります。外部モジュールの入力データを個別に指定し、EAをテストすることができます。しかし、この厄介で、おそらく、無意味な仕事は私たちに合っていません。それではどのようにしたらよいでしょうか?

可能なオプションの1つとしては、自己最適化インジケータを外部モジュールとして使用することです。自動最適化のトピックでは、多くの記事や例が書かれています。私はこのトピックの発展にも貢献したいと思います。『インジケータとシグナルの収益性の視覚的テスト』という記事のアイディアを使用したいと思います。この著者は、仮想取引の価格としてBUYのポジションをオープンするためのロウソクの最大値と、SELLの最小値を使用することを提案しています。その結果、最悪の取引条件が選択され、価格に対するこのアプローチによって、入力パラメータが最適化されます。実際の取引で得られる最適値では、結果は(過去のデータの同じ場所で)これより悪くなることはないと考えられます。実際の取引では、最適化後の利益を保証することはできません。

EAの戦略は、ボリンジャーインディケーターチャネルの内部で取引を行い、その境目にポジションを置くことに基づいています。このインジケータを置き換え、インジケータEnvelopeにチャネルを作成しましょう。移動平均のインジケータから、MAから一定距離に等距離の境界を形成します。新しいシグナルインジケータは使用前に自動的に自己最適化されます。入力パラメータとして、最大利益が示された最適値が使用されます。最適化のために、MA期間と移動平均からの境界の距離の2つのパラメータが選択されます。

自動最適化機能付きシグナルインジケータを作成するためのアルゴリズム:

  1. 最適化のパラメータと基準を決定します。今回の場合、パラメータはMA期間および境界変位の距離であり、基準は最大利益となります。
  2. インジケータに最適化ブロックを作成します。この例では、固定ステップで指定された範囲内の入力データの完全な検索が行われます。移動平均の周期は10ステップで10〜100であり、シフトに対しては1000〜10000の範囲で1000ステップでスクロールします。
//+------------------------------------------------------------------+
//|                           Copyright 2018, Sergey Pavlov (DC2008) |
//|                              http://www.mql5.com/ru/users/dc2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, Sergey Pavlov (DC2008)"
#property link      "http://www.mql5.com/ru/users/dc2008"
#property link      "1.00"
#property link      "Example of a multimodule expert: project test module Trading signals"
//---
#include <MovingAverages.mqh>
//--- Display indicator in the chart window
#property indicator_chart_window
//--- Number of buffers to calculate the indicator
#property indicator_buffers 1
//--- Number of graphic series in the indicator
#property indicator_plots   1
//+------------------------------------------------------------------+
 // | 最適化結果の構造                                                               |
//+------------------------------------------------------------------+
struct Opt
  {
   intvar1;// 1パラメータの最適値
   intvar2;// 2パラメータの最適値
   doubleprofit;// 利益
  };
//---
double      Buffer1[];
bool        optimum=false;
Opt         test={NULL,NULL,NULL};
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ArraySetAsSeries(Buffer1,true);
   SetIndexBuffer(0,Buffer1,INDICATOR_DATA);
   optimum=false;
   return(INIT_SUCCEEDED);
  };
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
   double signal=0.0;
   Buffer1[0]=signal;
//--- optimization of input parameters
   if(!optimum)
     {
      ArraySetAsSeries(close,false);
      int count=rates_total;
      int total=0;
      int total_profit=0;
      for(int d=1000;d<10001;d+=1000)
         for(int j=10;j<101;j+=10)
           {
            double shift=d*_Point;
            bool open_buy=false;
            bool open_sell=false;
            double price_buy=0;
            double price_sell=0;
            double profit=0;
            int order=0;
            for(int i=j+1;i<count;i++)
              {
               double ma=SimpleMA(i,j,close);
               double sell=ma+shift;
               double buy=ma-shift;
               //--- BUY
               if(buy>close[i] && !open_buy)
                 {
                  price_buy=high[i]+spread[i]*_Point;
                  if(order==0) profit=0;
                  else profit+=price_sell-price_buy;
                  order++;
                  open_buy=true;
                  open_sell=false;
                 }
               //--- SELL
               if(sell<close[i] && !open_sell)
                 {
                  price_sell=low[i]-spread[i]*_Point;
                  if(order==0) profit=0;
                  else profit+=price_sell-price_buy;
                  order++;
                  open_sell=true;
                  open_buy=false;
                 }
               //---
              }
            if(profit>0)
               if(profit>test.profit)
                 {
                  test.var1=j;
                  test.var2=d;
                  test.profit=profit;
                  total_profit++;
                 }
            //---
            Comment"入力パラメータの最適化...""パス =",total,"// profitable ="、total_profit);
            total++;
           }
      //---
      Print「最適化が完了しました:」、test.var1、""、test.var2);
      Comment「最適化が完了しました」);
      optimum=true;
     }
//---
   if(optimum)
      if(test.profit>0)
        {
         ArraySetAsSeries(close,true);
         double ma=SimpleMA(0,test.var1,close);
         double sell=ma+test.var2*_Period;
         double buy=ma-test.var2*_Period;
         //--- calculation of the Trading signals
         if(buy>close[0]) signal=2.0;
         if(sell<close[0]) signal=-2.0;
        }
//--- Indicator buffers
   Buffer1[0]=signal;
   return(rates_total);
  };

もちろん、最適化には若干時間がかかり、その間EAは取引できなくなります。モジュラー取引ロボットが24時間稼動している場合、自動最適化の遅延はトータルの取引時間に大きな影響を与えるべきではありません。

まとめ

  1. MQL5でマルチモジュールのエキスパートアドバイザーを作成することは可能であり、場合によっては、取引的に有利である上に便利です。
  2. この記事では、外部モジュールを備えた取引ロボットの概念を紹介しました。モジュラプログラミングの技術により、第三者の開発者の興味を引くようなかなり複雑なプロジェクトを作成することができます。彼らはモジュール作成時に、コードを公開しないようにして、アルゴリズムに対する著作権を保護しています。
  3. モジュラープロジェクトの最適化の問題は未解決のまま残りますが、シグナルやフィルタモジュールとして使用されるシグナルインジケータの自己最適化は、開発が必要なトピックです。

注:記事に添付されているファイルを使用することで、必要な構成でモジュラープロジェクトのソースコードを生成できます。

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

添付されたファイル |
グラフィカルインタフェ-スを備えたエキスパ-トアドバイザ:パネルの作成(第1部) グラフィカルインタフェ-スを備えたエキスパ-トアドバイザ:パネルの作成(第1部)
多くのトレーダーが依然として手作業を好むという事実にもかかわらず、ここではルーティンで行う作業の自動化を完全に避けることはできないでしょう。この記事では、手動取引のためのマルチシンボルシグナルエキスパートアドバイザーの作成例を示します。
任意のインジケータの計算部分をEAのコードに転送する方法 任意のインジケータの計算部分をEAのコードに転送する方法
インジケータコードをEAに転送する理由は様々です。しかし、このアプローチの長所と短所はどのように評価するべきでしょうか?この記事では、インジケータコードをEAに転送する技術をご紹介します。EAの動作スピードを評価するためにいくつかの実験を行いました。
モンテカルロ法を適用したトレーディング戦略の最適化 モンテカルロ法を適用したトレーディング戦略の最適化
トレード口座でロボットを起動する前に、通常はテストを行い、ヒストリー上で最適化します。 しかし、ここで合理的な質問が発生します: 過去の結果は、未来で役に立つだろうか。 この記事では、モンテカルロ法を適用してトレード戦略の最適化のカスタム基準を構築するメソッドについて説明します。 さらに、EA の安定性基準を考慮します。
パネルを改善してみましょう(CAppDialog / CWndClientからの継承、背景の色の変更、透明性の追加) パネルを改善してみましょう(CAppDialog / CWndClientからの継承、背景の色の変更、透明性の追加)
CAppDialogの使用の学習を続けます。ここでは、グラフィックパネルの背景の色、枠線、タイトルを設定する方法を学びます。順を追って、チャート上でアプリケーションウィンドウを移動するときに、アプリケーションウィンドウに透明性を追加する方法を見ていきます。次に、CAppDialogまたはCWndClientから子孫を作成し、コントロールを操作する際の新しい特徴を見ていきます。最後に、新しいプロジェクトを新しい視点から見ていきます。