
単一インスツルメント上で異なるExpert Advisorsを使ったトレーディングのためのORDER_MAGICの使用
はじめに
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で構成されていると理解できるかもしれません。 ひじょうに複雑なシステムを作成するにはこれで十分だと思います。
ではここにあるものをまとめます。システムにどんな良い条件を入れるか、です。
- 3つ以上のExpert Advisorsを一つのインスツルメントで動作させ、干渉しない機能
- 3つ以上のExpert Advisorsを別のインスツルメントで動作させ、お互いに補い合う機能
- 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 の動作
このEAを実行してみますが。、ひとつのインスツルメントの異なる時間枠で行います。(この実験のため、ランダムなインスツルメントを選びました。EURUSD: oです。)
図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を起動します。
図4 2つの同じExpert Advisorsに異なるマジックナンバーを割り当てます。(インスツルメントはひとつ。異なる時間枠使用)二番目の Expert Advisorを起動します。
図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のインストール
図7 結果は、異なるマジックを伴う3つのExpert Advisorsの確執なしのトレーディングです。
Expert Advisorsメッセージの印刷からわかるように、3つの Expert Advisorsはすべて問題なく導入され、確執を呈していません。
おわりに
トレーディング処理に魔法の注文を割り当てる機会を提供することで、MQL5の作成者はExpert Advisorのプログラマの生活をかなり豊かにしました。しかし、開発者はインスツルメントを提供することができるにすぎません。ダイアモンドを手にするのは実際にはみなさんなのです。
成功をお祈りしています。また会う日まで!
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/112





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
現在のオープンポジションを 構成するトレードのリストを作成するMQL5関数があればいいのに。つまりネット取引だ。そうすれば、どの取引がどのマジックで現在ポジションに存在しているかを常に知ることができる。現在、1つのマジックを持つExpert Advisorがポジションをオープンし、2つ目のマジックを持つExpert Advisorがそのポジションに追加した場合、トレーダーはその手でポジションの一部をクローズし、どのマジックが食いちぎられたかを知る方法はない。
HistorySelectByPosition()が間違っている?
念のため、このライブラリ(クラス)を使おうと思っている人がいたら、https://www.mql5.com/ru/forum/171241。
私はこれを使いたかったのですが、問題にぶつかりました。
いつでも微調整が可能であることは明らかだが、それにもかかわらず
こんにちは!
今日、面白い不具合を見つけました。Expert Advisorを仮想 ポジションに切り替えたところ、テスト中の履歴でポジションに2回エントリーしているのを発見しました。仮想ポジションのアルゴリズムは正常に動作するのですが、スクリプトがこの場所で1秒以内に2回エントリーすることに成功しました。すべてが通常のアルゴリズムで動作し、私はちょうど楽器の現在の位置を選択し、それで動作します。新しいティック - 履歴をTimeCurrent にロード - magikの取引を選択 - 取引なし - ポジションをオープン - 新しいティック - magikの取引を選択 - 取引なし(取引が通過したことは知っているが) - ポジションをオープン - 新しいティック - 履歴をTimeCurrent にロード - magikの取引を選択 - そしてダブルボリュームの取引がある。他の取引ではこのようなことは見られません。この不具合の原因は何だと思われますか?