English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
単一インスツルメント上で異なるExpert Advisorsを使ったトレーディングのためのORDER_MAGICの使用

単一インスツルメント上で異なるExpert Advisorsを使ったトレーディングのためのORDER_MAGICの使用

MetaTrader 5 | 6 10月 2015, 13:18
710 0
Mykola Demko
Mykola Demko

はじめに

MQL5には、指値注文にマジックナンバーを割り当てる機能があり、この情報を注文の特定に使用します。 これは、異なるExpert Advisors間でのやりとりや、より複雑なシステムの開発に可能性を開きます。本稿では、マジックナンバーの過小評価されているメリットについてみなさんにお知らせしたいと思います。

本稿の主題に関わる内容に進む前に、マジックナンバーは何で構成されているのかもう少し理解する必要があります。数字において魔法になりうるものは?何がどのExpert Advisorがそれを設定したと判断するのでしょうか?『奇跡』は機会で始まります。それは開発者がマジックナンバーによって宣言される ulong, タイプに設定するものです。

タイプ ulong は最も長い

整数タイプ longを詳細に見ると、このタイプの最大値はただ驚異的であることが判ります。

タイプ バイトサイズ 最小値 最大値 C++ 言語の類似体
long 8 -9 223 372 036 854 775 808 9 223 372 036 854 775 807 __int64
ulong 8 0 18 446 744 073 709 551 615 unsigned __int64

表1  データタイプlongとulongのプロパティ

ここで、タイプ ulongは正と負の仮数の組合せによりそれをしのいでいます。

そのため指定の長さはかなり大きな値になりますが、以前にはそれはどのように使用されていたのでしょうか?

mql 4で作業した 経験では 、多くの開発者によるマジックナンバーのコーディングにはセンスが感じられない、と思うことがよくありました。マジックナンバーはうまく使われていますが、コーディングはばかげて見えるのです。マジックナンバー12345の独立性について言われることは、そういったマジックはほとんど前半で使われているということです。後半ではマジックナンバー55555、33333、77777が使われ、これで完璧な一式です。みなさんのコンピュータは1,000以上Expert Advisorsを持つことはないため、1000という数字はExpert Advisorsの個人名をエンコードするのに十分な数字だということに着目していただきたいと思います。

1000 は全体でただ3つのカテゴリーです。では ulongで使用可能な残り15のカテゴリーはどうすればよいのでしょうか? 答えは簡単です。それらをエンコードすればよいのです。

コードという言葉についてウィキペディアではなんと言っているでしょうか。

コード ルール(アルゴリズム)、 記号 (文字) (または信号)のきっちりとした特定の組合せの個別メッセージについての比較。

よってここでもルールを作ります。マジックナンバーのコードに規定を作ることを提案します。 Expert AdvisorのIDだけでなく、それを実行するインスツルメントもです。Expert Advisorが実行されている事実、たとえば EURUSDについて、は Expert Advisorがそのインスツルメント上でのみ注文を表示することを意味するものではありません。また、「あなたの/外部の」などExpert Advisorsの連携コードを書くことは便利だと思います。それでポジションを確認するとき、, Expert Advisorは現在の注文が使いやすいExpert Advisorで構成されていると理解できるかもしれません。 ひじょうに複雑なシステムを作成するにはこれで十分だと思います。

ではここにあるものをまとめます。システムにどんな良い条件を入れるか、です。

  1. 3つ以上のExpert Advisorsを一つのインスツルメントで動作させ、干渉しない機能
  2. 3つ以上のExpert Advisorsを別のインスツルメントで動作させ、お互いに補い合う機能
  3. Expert Advisorを動作させながらインスツルメントによって注文を特定する機能

これでタスクは設定されました。では実装を始めます。

シンプルなExpert Advisor

シンプルなExpert Advisorのコードの原版を作ります。たとえば、「動向」の方向にあるポジションを保持するなどです。マジックナンバーを解析しようと決めた読者は、すでに『初心者のためのAdvisor in MQL5でプログラミングをする段階的ガイド』を読まれていると思います。まだお読みでないなら、一読を強くお勧めします。なぜならExpert Advisorの作成についてはここで詳しく述べないからです。基本的にExpert Advisorは 一度にポジションをオープンし、他の時間すべてについてそれを返します。そのため、ポジションをオープンする関数が必要です。トレーディングリクエスト(トレーディング注文)をセットするためにです。

補助クラスを作成します。それはトレーディングリクエストストラクチャのフィールドに書き込むパラメータを計算します。

//+------------------------------------------------------------------+
//| The class provides auxiliary trading calculations                |
//+------------------------------------------------------------------+
class CProvision
  {
protected:
   MqlTradeRequest   trades;                 // pointer to the request structure of OrderSend
public:
   int               TYPE(const double &v[]);  // determines the type, in respect to the  readings of the moving
   double            pricetype(int type);     // calculates the level of the opening, in respect to the type 
   double            SLtype(int type);        // calculates the level of the stop-loss in respect to the type
   double            TPtype(int type);        // calculates the level of the take-profit, in respect to the type
   long              spread();               // returns the spread of the current instrument
   int               SendOrder(ENUM_ORDER_TYPE type,double volume);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume)
  {
   trades.action          =TRADE_ACTION_DEAL;       // Type of the implemented actions
   trades.magic           =magic;                 // Stamp of the Expert Advisor (identifier of the magic number)
   trades.symbol          =_Symbol;                // Name of the trading instrument
   trades.volume          =volume;                // Request the volume of the trade in lots
   trades.price           =pricetype((int)type);  // Price       
   trades.sl              =SLtype((int)type);     // Level of Stop Loss order
   trades.tp              =TPtype((int)type);     // Level of Take Profit order         
   trades.deviation=(int)spread();                // Maximum acceptable deviation from the requested price
   trades.type=type;                              // Order type
   trades.type_filling=ORDER_FILLING_FOK;
   if(OrderSend(trades,res)){return(res.retcode);}
   return(-1);
  }
//+------------------------------------------------------------------+
//| Determines the type, in respect to the reading of the moving     |
//+------------------------------------------------------------------+
int CProvision::TYPE(const double &v[])
  {
   double t=v[0]-v[1];
   if(t==0.0)t=1.0;
   return((int)(0.5*t/fabs(t)+0.5));
  }
//+------------------------------------------------------------------+
//| Calculates the level of opening in respect to the type           |
//+------------------------------------------------------------------+
double CProvision::pricetype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.ask);
      if(type==1)return(tick.bid);
     }
   return(-1);
  }
//+------------------------------------------------------------------+
//| Calculates the level of stop-loss in respect to the type         |
//+------------------------------------------------------------------+
double CProvision::SLtype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.bid-SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
      if(type==1)return(tick.ask+SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Calculates the level of timeframe in respect to the type         |
//+------------------------------------------------------------------+
double CProvision::TPtype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.bid+TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
      if(type==1)return(tick.ask-TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Returns the spread                                               |
//+------------------------------------------------------------------+
long CProvision::spread() 
  {
   return(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));
  }

そのようなクラスを持つことで問題なくシンプルなExpert Advisorのコードを書くことができます。

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+

//--- Input parameters
input ulong              magic       =1;           // magic
input int                SL          =300;         // Stop Loss
input int                TP          =1000;        // Take Profit
input int                MA_Period   =25;         // MA period
input double             lot         =0.1;         // Volume of position
input int                MA_shift    =0;          // Shift of indicator
input ENUM_MA_METHOD     MA_smooth   =MODE_SMA;     // Smoothing type
input ENUM_APPLIED_PRICE price       =PRICE_OPEN;    // Price type 

//--- We will store the indicator's handle
int
MA_handle,     // Handle of the indicator
type_MA,       // Type that specify the direction of MA
rezult;        // The variable takes the value of the result of the OrderSend operation
double v[2];    // Buffer for receiving values of MA

MqlTradeResult   res;   // Pointer to the structure of responding by OrderSend
MqlTick         tick;  // Pointer to the structure of last market information
CProvision      prov;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication

   if(PositionSelect(_Symbol))// If there is an open position 
     {
      if(PositionGetInteger(POSITION_TYPE)!=type_MA)// Check if its time to close
        {
         Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME),
               " reverse position of type ",PositionGetInteger(POSITION_TYPE)," by ",type_MA);
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,PositionGetDouble(POSITION_VOLUME)+lot);
         // reverse the position
         if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("#",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME),
            " open position of type ",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);
      // open position 
      if(rezult!=-1)Print("#",magic," Code of operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  } 

それを実行しExpert Advisorは収益性に違いを生じていないが、取引はまさに特定のロジックにしたがっていることを確認します。 それが正確にそこから得る必要のあるものなのです。

図1 単一のインスツルメントでの一つのExpert Advisor の動作

図1 単一のインスツルメントでの一つのExpert Advisor の動作

このEAを実行してみますが。、ひとつのインスツルメントの異なる時間枠で行います。(この実験のため、ランダムなインスツルメントを選びました。EURUSD: oです。)

図2 同一のインスツルメント上で異なる時間枠におけるの2つのExpert Advisorsの確執

図2 同一のインスツルメント上で異なる時間枠におけるの2つのExpert Advisorsの確執

どちらのExpert Advisorsも同一インスツルメントで上で実行されており、コードは共有するポジションを特定しないので、両者ともトレーディングポジションを 修正しようとしています。それはインディケータの読み取りに依存し、この結果確執が生じます。M1で実行されているExpert Advisorは「セル」内のポジションを返そうとしています。しかし、もう一方 のExpert Advisorはそれを止めようとしています。個別のポジション計算が必要なのは明らかです。それがこれからやっていこうとしていることです。


ポジションかバーチャルポジションか?

MetaTrader 5 の開発者は注文からポジションの考察に移動するので、ポジションを記録することに関連する関数をもっと詳細に考えるべきでしょう。

// Returns the number of open positions.
int     PositionsTotal();

// Returns the symbol of the open position by the number in the list of positions.
string  PositionGetSymbol(int  index);

// Selects the open position for further working with it.
bool    PositionSelect(string  symbol, uint timeout=0);

// Function returns the requested property of the open position.
double  PositionGetDouble(ENUM_POSITION_PROPERTY  property_id);

// The function returns the requested property of the open position.
long    PositionGetInteger(ENUM_POSITION_PROPERTY  property_id);

// The function returns the requested property of the open position.
string  PositionGetString(ENUM_POSITION_PROPERTY  property_id);

compliantポジションプロパティのリクエストを取得する関数数値enumerationsの識別子 PositionGetDouble PositionGetInteger PositionGetString。これらは表2~4に記載されています。

識別子 説明 タイプ
POSITION_VOLUME ポジションボリューム ダブル
POSITION_PRICE_OPEN ポジション価格 ダブル
POSITION_SL オープンポジションのストップロスレベル ダブル
POSITION_TP オープンポジションのテイクプロフィットレベル ダブル
POSITION_PRICE_CURRENT シンボルの現在価格 ダブル
POSITION_COMMISSION コミッション ダブル
POSITION_SWAP 集積スワップ ダブル
POSITION_PROFIT 現在収益 ダブル

表2 数値 ENUM_POSITION_PROPERTY_DOUBLE

識別子 説明 タイプ
POSITION_TIME ポジションオープン時刻 日時
POSITION_TYPE ポジションタイプ ENUM_POSITION_TYPE
POSITION_MAGIC ポジションのマジックナンバー ( ORDER_MAGIC 参照) ロング
POSITION_IDENTIFIER ポジションの特定 -これは、再開されたポジションに割り当てられるユニークな番号で、ライフサイクル中変更されません。 ポジションのターンオーバー によってIDが変わることはありません。 ロング

表3 数値 ENUM_POSITION_PROPERTY_INTEGER

識別子 説明 タイプ
POSITION_SYMBOL ポジションをオープンするシンボル ストリング
POSITION_COMMENT ポジションに関するコメント ストリング

表4 数値 ENUM_POSITION_PROPERTY_STRING

関数から言語は「だれが注文を出したか」に基づくポジション分割を含まないことは明らかです。しかし、そのような記録は可能です。なぜなら ORDER_MAGIC POSITION_MAGIC DEAL_MAGICは正確に同じ番号で、ユーザーが示すマジックナンバーから取得されるからです。 POSITION_MAGIC DEAL_MAGICから取得されます。これはポジションをオープンします。また、一方 DEAL_MAGICは出された注文の ORDER_MAGICから取得されます。

注文、処理、ポジションを特定することは問題なく行えますが、特定のマジックナンバーによってポジションに導くことは不可能です。ここからこの短所を克服していきます。内蔵関数の類似体を作成しますが、マジックナンバーの特定により行います。 マジックナンバーのバーチャルポジションで動作クラスを宣言します。

OOPで作業する機会があるので、ここでのストラクチャも宣言します。(客観的にプログラムを書く練習にもなります。)

//+------------------------------------------------------------------+
//| Structure of the CPositionVirtualMagic class                     |
//+------------------------------------------------------------------+
struct SPositionVirtualMagic
  {
   double            volume; // volume of virt. position
   ENUM_POSITION_TYPE type;  // type of virt. position
  };
//+--------------------------------------------------------------------------------+
//| The class calculates the virtual position of an Expert Advisor by magic number |
//+--------------------------------------------------------------------------------+
class CPositionVirtualMagic
  {
protected:
   SPositionVirtualMagic pvm;
public:
   double               cVOLUME(){return(pvm.volume);}
   // Returns the volume of virtual position of an Expert Advisor
   ENUM_POSITION_TYPE   cTYPE(){return(pvm.type);}
   // Returns the type of virtual position of an Expert Advisor
   bool              PositionVirtualMagic(ulong Magic,
                                          string symbol,
                                          datetime CurrentTime
                                          );
   // the method of calculation virt. position returns the presence or absence of virt. position
private:
   void              prHistory_Deals(ulong &buf[],int HTD);
   // Fills the array of tickets
  };
//+-------------------------------------------------------------------------------------+
//| Method of calculation of virt. position, returns true if there is a virt. position  |
//+-------------------------------------------------------------------------------------+
bool  CPositionVirtualMagic::PositionVirtualMagic(ulong Magic,
                                                  string symbol,
                                                  datetime CurrentTime
                                                  )
  {
   int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP));
   if(DIGITS<0)DIGITS=0;
   ulong Dticket=0;
   int History_Total_Deals=-1;
   double volume=0,volume_BUY=0,volume_SELL=0;
   ulong DTicketbuf[];

   do
     {
      if(HistorySelect(0,TimeCurrent()))
        {
         History_Total_Deals=HistoryDealsTotal();
         prHistory_Deals(DTicketbuf,History_Total_Deals);
        }
      HistorySelect(0,TimeCurrent());
     }
   while(History_Total_Deals!=HistoryDealsTotal());

   for(int t=0;t<History_Total_Deals;t++)
     {
      Dticket=DTicketbuf[t];
      if(HistoryDealSelect(Dticket))
        {
         if(HistoryDealGetInteger(Dticket,DEAL_TIME)>=CurrentTime)
           {
            if(HistoryDealGetInteger(Dticket,DEAL_MAGIC)==Magic)
              {
               if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_BUY)
                 {
                  volume_BUY+=HistoryDealGetDouble(Dticket,DEAL_VOLUME);
                 }
               else
                 {
                  if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_SELL)
                    {
                     volume_SELL+=HistoryDealGetDouble(Dticket,DEAL_VOLUME);
                    }
                 }
              }
           }
        }
      else{HistorySelect(0,TimeCurrent());t--;}
      // if there is a fault, load history data and pass the step again
     }
   volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);
   if(volume<0)pvm.type=POSITION_TYPE_SELL;
   else
     {
      if(volume>0)pvm.type=POSITION_TYPE_BUY;
     }
   pvm.volume=fabs(volume);
   if(pvm.volume==0)return(false);
   else return(true);
  }

(CProvisionクラスのコードが与えられている)上述のテキストではすべてがどこからくるのか、どこへ行くのか説明されませんでした。その理由は Expert Advisorの開発は本稿で取り上げる話題ではないからです。

しかし、CPositionVirtualMagicクラスについては詳しく考察していきます。

このクラスには以下のストラクチャが与えられています。

struct SPositionVirtualMagic

これは計算結果を受け入れるために使われます。そのようなクラス内でのグローバル宣言はpvm(ストラクチャの変数)のおかげで 、このストラクチャはクラスのどんなメソッドでもどこででも使用可能です。

次にクラスの2つのメソッドを見ていきます。

double               cVOLUME(){return(pvm.volume);} // Returns the volume of the virtual position of the EA
ENUM_POSITION_TYPE   cTYPE()  {return(pvm.type);}   // Returns the type of the virtual position of the EA

これらメソッドはパブリックとして宣言されているため、コールされたクラス変数をとおしてプログラムのあらゆる場所で使用可能で、依頼されたロケーションのストラクチャ変数のアウトプットのために設計されています。

これはまた以下のメソッドが宣言されるセクションでもあります。

bool              PositionVirtualMagic(ulong Magic,string symbol,datetime CurrentTime);

これはクラスの主要関数で、のちにその詳細分析を行います。いまのところは、先回りして実行しプライベートアクセスの指定子の元、関数記述を行います。

void              prHistory_Deals(ulong &buf[],int HTD);

このメソッドは配列にトランザクションのチケット記録を行います。それは基本的にサイクルで、コールされた関数に記述可能ですが、TPositionVirtualMagic()関数のサイズを減らしたかった(コードを読みやすくするため)ので、このサイクルを関数限界を超えて移動し、個人アクセスの指定子の使い方を示しました。

では PositionVirtualMagic()関数にもどります。この関数はそもそも、正確性の一行計算を行います。それには計算されたポジションボリュームのダブル値を四捨五入する必要があります。

int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)); if(DIGITS<0)DIGITS=0;

これはゼロとの比較処理を行うために必要です。それがないと、小数点以下8桁目におけるバランスがゼロに等しくならず実行エラーが生じます。

ポジションボリュームは最小ステップに四捨五入されます。最小ステップが1より大きければ、四捨五入は整数部分で行われます。次はサイクル whileですが、これは(mql4とは異なる)新しい方法で使用されます。truthful expressionの確認がサイクルの最初ではなく最後で行われるためです。

    do
     {
      if(HistorySelect(0,TimeCurrent()))
        {
         History_Total_Deals=HistoryDealsTotal();
         prHistory_Deals(DTicketbuf,History_Total_Deals);
        }
      HistorySelect(0,TimeCurrent());  
     }
   while(History_Total_Deals!=HistoryDealsTotal());

そのようなアプローチは、真実性の式がサイクル内で計算され、その導入時この確認のためにまだ準備されていないために使用されます。

サイクルは履歴のアップロード を含んでおり、これは 履歴作業を行う内蔵関数の動作が確実に行われるために要求される条件であることに注意をはらっていただきたいと思います。

HistorySelect(0,TimeCurrent())

ここで変数名を選択するシステムについて説明が必要かと思います。

注意して読まれている読者の方は、クラス名が最初の文字 "C"で定義されているとお気づきでしょう。これはシンタックスによって要求されているものではなく、どんな名前でも構いませんが、このようにした方が読みやすいと思います。文字"C" が名前の前にあると、すぐにこれがクラス名であると判ります。また"S"が名前の前にあればストラクチャ名であると判ります。変数が内蔵関数の値を取る場合、ただストラクチャ名の一部を変え変数名を取得すればよいだけです。例えば以下のようにです。

CurrentTime = TimeCurrent();

シンプルで読みやすく、変数に何が含まれているか即座にわかります。特にMetaEditorはコードの特定部分を指定のロケーションにドラッグ する関数を持つからです。

コードについてもっとよく見ると、履歴のアップロード後関数呼び出しが続くことがわかります。

History_Total_Deals=HistoryDealsTotal();

これは変数へのトランザクションナンバーの保管を伴います。同様の条件により、サイクルを抜けるための照合を実装します。この照合はなんのために必要なのでしょうか?そしてまた、なぜただ履歴をアップロードしそこからトランザクションを復元するだけではだめなのでしょうか?

問題はExpert Advisorsの動作中、履歴はそれぞれのEAから個別に要求され、そのためExpert Advisorsが異なる時間に実行し、また履歴の深さもまちまちだという事実にあります。そして、このことが意味するのは、Expert Advisorがサイクルに入り、その期間の履歴をアップロードするとき、サイクルの終わりに到達する前にこの履歴は別のExpert Advisorからの要求によってすでにアップロードされていることを発見し、そのため真性照合が必要になる、ということです。

それは照合の最良タイプではないでしょうが、それでも役には立ちます。それでは先に進みます。サイクルでは、クラスメソッドを呼びます。これはトランザクションのチケットの値を特別に準備されたバッファに入力します。prHistory_Deals ()関数を呼んだ後、再び履歴のアップロードを行います。

これが、prHistory_Deals ()関数の動作中、トレード履歴に変更があるかないかについての認証が作成される方法です。 変更がなければ、History_Total_Dealsの変数はHistoryDealsTotal ()と等しくなり、サイクルからシングルパスに抜けます。変更があれば、システムは二番目のサイクルを立ち上げ、チケット履歴がエラーなくアップロードされるまで繰り返します。(最後に";"を入れるのを忘れないでください。)

while(History_Total_Deals!=HistoryDealsTotal());

さらにサイクル forでは、バーチャルポジションの計算が行われます。

トランザクションが無事フィルター(トランザクション時間とトランザクションのマジックナンバー)を通り抜けたら、トランザクションが属するタイプのバーチャルポジションのその部分のボリュームが増します。

私はExpert Advisorの起動からのみのバーチャルポジション計算を記録します。他の方法を選ぶことも可能です。

ここで、ポジションがどのように正確に計算されるかお知らせします。いにしえより今日まで使ってきたレコードブックにより、支出と収益、バランス計算がそれらの値の差として保持されています。同じスキームがポジション計算にも適用されます。ロットを0.2の売り0.3の買いでオープンしたら、これは0.1買いポジションを保持していることを意味します。オープン時刻とレベル差は収益カテゴリーですが、ここで保持しているポジションは買いタイプ0.1です。

このことは単純にすべてのトランザクションを要約します。Expert Advisorにより行われる買い、また別の売り、それからそれらを比較し一般ポジションを得ます。(実際、これは検証される関数が関わるものです。)

ポジションボリュームの計算

volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);

ストラクチャ内の値のアウトプットを伴うポジションタイプの認識

if(volume<0)pvm.type=POSITION_TYPE_SELL;
   else
     {
      if(volume>0)pvm.type=POSITION_TYPE_BUY;
     }

ストラクチャへのボリュームアウトプット

pvm.volume=fabs(volume);

関数値のアウトプット:ポジションボリュームが0であれば、false、それ以外、ポジションが存在していればtrue

if(pvm.volume==0)return(false);
   else return(true);

バーチャルポジションの関数により、Expert Advisorのコードのドローアップが簡単に行えます。それは「近隣」と確執しません。

スペースの節約のため、コードの特定部分を提供します。それは上記にはないコードの全体です。

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+

//--- input parameters
input ulong              magic       =1;           // magic
input int                SL          =300;         // Stop Loss
input int                TP          =1000;        // Take Profit
input int                MA_Period   =25;          // MA period
input double             lot         =0.1;         // Volume of position
input int                MA_shift    =0;          // Shift of indicator
input ENUM_MA_METHOD     MA_smooth   =MODE_SMA;     // Smoothing type
input ENUM_APPLIED_PRICE price       =PRICE_OPEN;    // Price type 
//--- we will store the indicator's handle
int MA_handle,type_MA,rezult;
double v[2];
datetime  CurrentTime;  // The variable stores the time of start of the Expert Advisor
MqlTradeResult    res;   // Pointer to the structure of responding by OrderSend
MqlTick          tick;  // Pointer to the structure of last market information
CPositionVirtualMagic cpvm;
CProvision prov;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication

   if(cpvm.PositionVirtualMagic(magic,_Symbol,CurrentTime))// If there is ab open position 
     {
      if((int)cpvm.cTYPE()!=type_MA)// Check if it is time to close
        {
         Print("#",magic,"Position by magic number has volume ",cpvm.cVOLUME(),
               " reverse position of type ",(int)cpvm.cTYPE()," by ",type_MA);
         //cpvm.cVOLUME() - volume of virtual position
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,cpvm.cVOLUME()+lot);// reverse the poistion
         if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("#",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Poistion by magic number has volume ",cpvm.cVOLUME()," open position of type ",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position 
      if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  }

Expert Advisorを単一のインスツルメントで異なる時間枠について3回実行します。また毎回異なるマジックナンバーを割り当てます。

図3 2つの同じExpert Advisorsに異なるマジックナンバーを割り当てます。(インスツルメントはひとつ。異なる時間枠使用)最初の Expert Advisorを起動します。

図3 2つの同じExpert Advisorsに異なるマジックナンバーを割り当てます。(インスツルメントはひとつ。異なる時間枠使用)最初の Expert Advisorを起動します。

図4 2つの同じExpert Advisorsに異なるマジックナンバーを割り当てます。(インスツルメントはひとつ。異なる時間枠使用)二番目の Expert Advisorを起動します。

図4 2つの同じExpert Advisorsに異なるマジックナンバーを割り当てます。(インスツルメントはひとつ。異なる時間枠使用)二番目の Expert Advisorを起動します。

図5 結果、一つのインスツルメントにおいて確執なしにExpert Advisorsが動作しました。

図5 結果、一つのインスツルメントにおいて確執なしにExpert Advisorsが動作しました。

お試し実行は成功です。Expert Advisorsはお互いに思いやりをもって譲りあい確執は見られません。

技術仕様の最初のポイントは導入されましたが、また他にもあります。


マジックのコーディング

以下の部分を実装するにはメソッドクラスを開発する必要があります。それは、情報をエンコードまたはデコードするものです。内蔵関数から値を復元し指定のフォーマットに変換することもします。

このために、コーディングの用語を繰り返します。(いわば開発のための技術仕様です)

  • メソッドはExpert Advisor名(これをデジタル名とします)をエンコードする必要があります。
  • 自分のまたは外部のを認識するコード(これを連携コードとします)
  • Expert Advisorを実行する対象シンボルコード (EAが動作するもとのトランザクションから決定することができるように)

まず、新規クラス名を選択します。マジック(遺伝的名前)としましょう。ここでの列挙を割り当て、コードが視覚的に理解しやすいようにします。

enum Emagic
  {
   ENUM_DIGITAL_NAME,    // digital name if the Expert Advisor
   ENUM_CODE_INTERACTION,// code of interaction
   ENUM_EXPERT_SYMBOL    // symbol, on which the EA is launched
  };

列挙はただ役に立ちます。コンマを使わずに名前を記述します。するとコンパイラーがそれに連番を割り当てます。

まず、異なるタイプの変数(これは番号には適用しません)を割り当てたら、列挙からのパラメータを指定するとき、コンパイラ中にエラーが発生します。また明晰さを取得します。ただ単に 0 を割り当てません。割当てのコマンド ENUM_DIGITAL_NAME を与えます。

ストラクチャやクラスの作成には簡単な名前を選んで列挙します。わたしは一般的に選択された名前に E を追加しました。そして Emagic を取得しました。以下もそれぞれストラクチャが Smagic そしてクラスが Cmagic です。

これは必ずしもする必要はないことに注意してください。列挙はEnumerator、ストラクチャはStructurer、クラスはClassifierと呼ぶこともできます。しかしこれでは名前に共通性がありあません。このタイプのコードを読むのも快適ではありません。

次に、コードを保管するストラクチャを作成します。

struct Smagic
  {
   ulong             magicnumber;      // magic in an assembled form - how it is written in the order
   int               digital_name;     // digital name
   int               code_interaction; // code of interaction
   int               expert_symbol;    // symbol, on which the Expert Advisor is launched
  };

このあと、以前のExpert Advisor からのメソッドを含むマジックをエンコードまたはデコードするメソッドすべてを登録するCmagicクラスを宣言します。

class Cmagic
  {
protected:
   Smagic            mag;
   SPositionVirtualMagic pvm;
public:
// the function returns the assembled magic, assembled from the incoming data
   ulong             SetMagic_request(int digital_name=0,int code_interaction=0);

// the function obtains the assembled magic and divides it according to the assembly logic
   ulong             SetMagic_result(ulong magicnumber);    

// the function obtains the return identification and returns the requested part of the assembled magic
   ulong             GetMagic_result(Emagic enum_); 

// the function obtains the return identification and returns the textual interpretation of the request part of the assembled magic
   string            sGetMagic_result(Emagic enum_);

// returns the voulme of the virtual position of the Expert Advisor
   double            cVOLUME(){return(pvm.volume);}
   
// returns the type of the virtual position of the Expert Advisor
   ENUM_POSITION_TYPE  cTYPE(){return(pvm.type);}
                                           
// method of calculating the virtual position, returns the presence of absence of the virtual position   
   bool              PositionVirtualMagic(Emagic enum_,
                                          string symbol,
                                          datetime CurrentTime);
private:
// function divides the magic into three parts  of three charges, and returns the part to which the category points to
   int               decodeMagic_result(int category); 

// interpretor of instrument symbols into the digital code                                                      
   int               symbolexpert();     
   
// interpretor of the digital code into the prescribed text (Expert Advisors)
   string            expertcode(int code);    
                                 
// interpretor of the digital code into the prescribed text (interaction)   
   string            codeinterdescript(int code);

// interpretor of the digital code into the instrument symbol                                         
   string            symbolexpert(int code);

// cycle of recording tickets into the buffer
   void              prHistory_Deals(ulong &buf[],int HTD);    
  };  

ここからはメソッド開発です。

クラスにおける最初のメソッドです。

//+------------------------------------------------------------------+
//| Function returns the assembled magic, assembled from the input data    |
//+------------------------------------------------------------------+
ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)
  {
   if(digital_name>=1000)Print("Incorrectly specified digital name of the Expert Advisor (more than 1000)");
   if(code_interaction>=1000)Print("Incorrectly specified the code of recognizing yours-foreign (more than 1000)");
   mag.digital_name     =digital_name;
   mag.code_interaction =code_interaction;
   mag.expert_symbol    =symbolexpert();
   mag.magicnumber      =mag.digital_name*(int)pow(1000,2)+
                         mag.code_interaction*(int)pow(1000,1)+
                         mag.expert_symbol;
   return(mag.magicnumber);
  }

このメソッドは2つの値を受け取ります。Expert Advisorのデジタル名と連携コードです。

ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)

そして即座にそれらの正確性を確認します。

if(digital_name>=1000)Print("Incorrectly specified the digital name of the Expert Advisor(more than 1000)");
   if(code_interaction>=1000)Print("Incorrectly specifies the code of recognizing yours-foreign (more than 1000)");

しかしユーザーの行動に対する報復措置はありません。エラーの場合でもです。こ作業は従順に続きます。

次は入力データのストラクチャへの割当てです。それはユーザーが指定します。ただし、インスツルメントシンボルは指定しません。それは個人メソッドから取得します。

int Cmagic::symbolexpert()

それについてのコードは提供しません。なぜならとてつもなく長いからです。そのコードは添付ファイルとして提供されています。このメソッドは基本的にただのテーブルであることを述べさせてください。それは『マーケットビュー』ウィンドウからのシンボルすべてに対応する番号を振ります。たとえばEURUSDには1というぐあいです。

このデータは『マーケットビュー』ウィンドウにどの通貨があるか調査するコードを書くことで動的にかならず取得可能です。しかし、解決は問題の複雑さに応じるべきです。ウィンドウを呼ぶことで取引をすることに意味はありません。そこで簡単な方法を使います。通貨リストを構成し、それぞれをインデックスに割り当てるのです。

最後にメソッドすべてにわたる最も基本的な行です。

mag.magicnumber      =mag.digital_name*(int)pow(1000,2)+
                      mag.code_interaction*(int)pow(1000,1)+
                      mag.expert_symbol;

マジック全体の異なるパーツが組み立てられます。 これは、Expert Advisorの注文に割り当てられるMagicです。

次はクラスのパブリックメソッドです。

//+------------------------------------------------------------------+
//| Function obtains the assembled magic                                   |
//| and divides it according to the logic of the assembly                  |
//+------------------------------------------------------------------+
ulong Cmagic::SetMagic_result(ulong magicnumber)
  {
   mag.magicnumber      =magicnumber;
   mag.expert_symbol    =decodeMagic_result(1);
   mag.code_interaction =decodeMagic_result(2);
   mag.digital_name     =decodeMagic_result(3);
   return(mag.magicnumber);
  }

実際にはこのメソッドはシェルの役割をします。ストラクチャじゅうにひとつの個人メソッドを3回呼んだ結果を配布します。そのように指定子下で宣言するのはよいことです。クラス変数を呼ぶとき、全作業がパブリック関数によって行われたような印象を与え、ポップアップのすばやいメッセージに表示されるのではないからです。

しかしここでプライベート関数に戻ります。

//+------------------------------------------------------------------+
//| Function divides the magic into three parts of three charges              |
//| and returns the part, which the category points to             |
//+------------------------------------------------------------------+
int Cmagic::decodeMagic_result(int category)
  {
   string string_value=(string)mag.magicnumber;
   int rem=(int)MathMod(StringLen(string_value),3);
   if(rem!=0)
     {
      rem=3-rem;
      string srem="0";
      if(rem==2)srem="00";
      string_value=srem+string_value;
     }
   int start_pos=StringLen(string_value)-3*category;
   string value=StringSubstr(string_value,start_pos,3);
   return((int)StringToInteger(value));
  }

見た目には、このメソッドは指定のフィールドから3桁の数字を読むように表すことができます。たとえば、Magic 123456789 を持っているとすると、それら | 123 | 456 | 789 | と表すことができます。指定したフィールドが 1 ならば、結果は 789 です。これはフィールドが右から左に番号づけされるたれるためです。

これで、呼び出されたメソッドで3つのフィールドをすべて使ったあと、取得した全データのストラクチャに配布します。これはMagicをミナスキュールタイプの ストリングにもっていく手順の間行われます。

string string_value=(string)mag.magicnumber;

そのあと個別の行構成部位のソートが行われます。

次は2つの似た関数を見てみます。これらは基本スイッチ switchにあります。両者はアウトプット値のタイプが異なるだけです。

//+------------------------------------------------------------------+
//| Function obtains the identifier of the return                          |
//| and returns the requested part of the assembled magic                   |
//+------------------------------------------------------------------+
ulong Cmagic::GetMagic_result(Emagic enum_)
  {
   switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : return(mag.digital_name);     break;
      case ENUM_CODE_INTERACTION : return(mag.code_interaction); break;
      case ENUM_EXPERT_SYMBOL    : return(mag.expert_symbol);    break;
      default: return(mag.magicnumber); break;
     }
  }
//+------------------------------------------------------------------------------+
//| Function obtains the identifier of the return and returns                    |
//| a textual interpretation of the requested type of the assembled magic        |
//+------------------------------------------------------------------------------+
string Cmagic::sGetMagic_result(Emagic enum_)
  {
   switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : return(expertcode(mag.digital_name));            break;
      case ENUM_CODE_INTERACTION : return(codeinterdescript(mag.code_interaction)); break;
      case ENUM_EXPERT_SYMBOL    : return(symbolexpert(mag.expert_symbol));         break;
      default: return((string)mag.magicnumber); break;
     }
  }

関数はマジックの一部を返します。それはEmagicタイプのパラメータを指定します。最初の関数は ulong形式で結果を返します。それは計算に使われます。二番目の関数は stringタイプの結果を返します。それは視覚化に使用することができます。, which can be used for visualization.

GetMagic_result () 関数では、すべてはシンプルにまとめられています。 switchブランチじゅうにストラクチャの値を配布します。 一方 sGetMagic_result () はすこしばかり複雑です それぞれのブランチ case はテーブル関数を呼びます。それはストラクチャの値を目に見える形式に変換します。値が mag.expert_symbol = 1 であれば、最初の関数は, 1 を、二番目の関数は EURUSD を返します。

情報をエンコードまたはデコードするテーブル関数の利便性についてはすでに述べました。それぞれのケースは テーブルレスメソッドの実装のむずかしさによって個別に考える必要があること、テーブル記述に必要な時間への利点についてお話します状態のテーブル記載が簡単であれば複雑な問題は必要ありません。しかしテーブルの記載に多くの時間がかかるなら、あきらかに手順に参照が必要です。スペースを省くため、ここではテーブルを提供することはしません。(添付ファイルにありますので参照ください。)

基本はそれだけです。クラスは作成されましたが、まだ以前のExpert Advisorを開発に使用した関数がまだ4つ残っています。

単純にそれらを新規クラスで宣言しました。特にそれらは少し変更の必要があることを考慮します。

それではメインのメソッドです。

bool  Cmagic::PositionVirtualMagic(Emagic enum_,
                                   string symbol,
                                   datetime CurrentTime)

Cmagic クラスメソッドとして宣言されるだけでなく、これは異なるパラメータセットも有しています。

Magicの代わりにポジション計算を行うMagicのフィールドで特定します。また、シンボルが最後のオプションにあっても、それはシンボルによってロットのステップ情報を取得するのに使われるにすぎません。それはフィルタに指定されており、ポジションカウントのフィルタ内で他と対等に共有することができます。

これで得られるものは?トランザクションを同時にフィルタすることができます。それは同一Expert Advisorによって異なるインスツルメントでオープンとなっています。そうすると、それらは異なるインスツルメントで実行されている似たような他のExpert Advisorsと混同することはありません。正直、この新しい計算システムを使用する異なる方法を網羅することは簡単ではありません。読者はそのような複雑なシステムは何のために必要なのか個人的に判断してください。私が強調したいのは、シンプルに書けるところでインスタンスを複雑化しないことです。そして、あきらかに必要とあれば複雑にすることを辞さないことです。

クラスが設計されたので、新しいExpert Advisorで検証します。

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+
//--- input parameters
input ulong              digital_name_       =4;           // Digital name of Expert Advisor
input ulong              code_interaction_   =1;           // Code of interaction
input Emagic             _enum               =0;           // Model of magic number  
input int                SL                  =300;         // Stop Loss
input int                TP                  =1000;        // Take Profit
input int                MA_Period           =25;          // MA period
input double             lot                 =0.4;         // Volume of position
input int                MA_shift            =0;           // Shift of indicator
input ENUM_MA_METHOD     MA_smooth           =MODE_SMA;      // Smoothing type
input ENUM_APPLIED_PRICE price               =PRICE_OPEN;    // Price type 

//--- we will store the indicator's handle
int MA_handle,type_MA,rezult;
static ulong magic;
double v[2];
datetime  CurrentTime;// The variable stores the time of start of the Expert Advisor

MqlTradeResult  res;   // Pointer to the structure of responding by OrderSend
MqlTick         tick;  // Pointer to the structure of last market information
CProvision prov;
Cmagic mg;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   magic=mg.SetMagic_request(digital_name_,code_interaction_);
// Stamp of Expert Advisor (the magic number identifier) the magic variable is declared at the global scope
// used in int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume)
   CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication
   mg.SetMagic_result(magic);// put the information into the structure
   if(mg.PositionVirtualMagic(_enum,_Symbol,CurrentTime))// If three is an open position 
     {
      if((int)mg.cTYPE()!=type_MA)// Check if it is time to close
        {
         mg.SetMagic_result(magic);// put the information into the structure
         Print("#",mg.GetMagic_result(_enum),"Position by magic number has volume ",mg.cVOLUME(),
               " reverse position of type ",(int)mg.cTYPE()," by ",type_MA);
         //cpvm.cVOLUME() - volume of virtual position
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,mg.cVOLUME()+lot);// reverse position
         if(rezult!=-1)Print("№",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("№",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Position by magic number has volume  ",mg.cVOLUME()," open position of type",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position 
      if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  }

前にもお話したとおり、このExpert Advisorは極端にシンプルで異なる機能をお見せするためだけに作成されています。一つのインスタンスをで3回実行します。

図6 異なるグラフに異なるマジックを持つ3つのExpert Advisorsのインストール

図6 異なるグラフに異なるマジックを持つ3つのExpert Advisorsのインストール

図7 結果は、異なるマジックを伴う3つのExpert Advisorsの確執なしのトレーディングです。

図7 結果は、異なるマジックを伴う3つのExpert Advisorsの確執なしのトレーディングです。

Expert Advisorsメッセージの印刷からわかるように、3つの Expert Advisorsはすべて問題なく導入され、確執を呈していません。


おわりに

トレーディング処理に魔法の注文を割り当てる機会を提供することで、MQL5の作成者はExpert Advisorのプログラマの生活をかなり豊かにしました。しかし、開発者はインスツルメントを提供することができるにすぎません。ダイアモンドを手にするのは実際にはみなさんなのです。

成功をお祈りしています。また会う日まで!

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

添付されたファイル |
magic_exp0_en.mq5 (8.34 KB)
magic_exp1_en.mq5 (11.84 KB)
magic_exp2_en.mq5 (25.39 KB)
Google Chart APIからチャートを構築するためのライブラリ Google Chart APIからチャートを構築するためのライブラリ
さまざまなタイプのダイアグラムの構築がマーケット状況を分析しトレーディングシステムを検証する主要部分です。往々にしてみばえのよいダイアグラムを構築するにはデータのアウトプットをファイルに整理することが必要です。その後 MS Excelなどアプリケーションで使用していくのです。これはあまり便利な方法ではなく、動的にデータを更新する機能を奪います。Google Charts APIは、サーバーに特別な依頼を送るとオンラインでチャートを作成する手段を提供してくれます。本稿では、そのような依頼を作成し、チャートをGoogleサーバーから取得するプロセスを自動化していきます。
インディケータの経済的計算原理 インディケータの経済的計算原理
自動化されたトレーディングシステムのプログラムコードではユーザーとテクニカルインディケータの呼び出しはほとんどスペースを取りません。たいてい数行のコード行ですむ簡単なものです。ただこの数行のコードがExpert Advisorの検証に必要な時間の多くを費やす、ということはよくあることです。インディケータ内でデータ計算に関連することはすべて、一見したよりもずっと綿密に配慮される必要があります。本稿ではこの件に関して詳しく述べていきます。
名前つきパイプを使用したMetaTrader 5端末間コミュニケーションにDLLを使用しないソリューション 名前つきパイプを使用したMetaTrader 5端末間コミュニケーションにDLLを使用しないソリューション
本稿は名前つきパイプを使用したMetaTrader 5端末同士のプロセス間コミュニケーションの実装方法について述べていきます。名前付きパイプを使用するにはCNamedPipesクラスが作成されます。 その使用検証と接続計測のために、ティックインディケータ、サーバー、クライアントスクリプトが提供されます。リアルタイムのクオートには名前付きパイプの使用で十分です。
MQL5における移動平均計算パフォーマンス検証 MQL5における移動平均計算パフォーマンス検証
最初に「移動平均」インディケータが作成されて以来、多くのインディケータが登場してきました。それらの多くは類似の平滑化手法を使用していますが、異なる移動平均アルゴリズムの性能については研究されていません。本稿ではMQL5で「移動平均」を使用する可能な方法について考察し、それらのパフォーマンスを比較していきます。