
クロスプラットフォームEA:オーダー
目次
イントロダクション
MT4とMT5は、トレードリクエストで異なるルールを使用しています。この記事では、トレードプラットフォームとバージョンにかかわらず、クロスプラットフォームEAとして稼働する、クラスオブジェクトを使用します。
コンベンション
MT4とMT5のプロセスには多くの違いがあります。トレード・サーバによって処理されたリクエストの詳細を保存するには、両方のトレードプラットフォームでの3つのバージョン/モードを考慮する必要があります:(1)MT4、(2)MT5ネッティング、および(3)MT5ヘッジ モード。
MT4
EAが正常にオーダーを送信するとき、MT4ではそのオーダーのチケット番号、マジックナンバーを受け取ります。オーダーを閉じるときは、同じチケットが通常使用されています。
部分的にオーダーを閉じるときには、もう少し複雑です。オーダーの合計ロットサイズよりも小さいロットの数を指定することにより、 OrderClose関数を使用して行われます。このようなトレードリクエストが送信されると、オーダー(またはオーダーチケット)が関数呼び出しで示される特定のロットサイズの量で決済されます。残りのロットサイズは、その後、部分的に閉じられたオーダーと同じ型の新しいオーダーとして相場に残ります。OrderClose関数のみブール変数を返すので、アクティブなオーダーのリストを再検討する以外、新しいチケットを取得するための手っ取り早いメソッドはありません。OrderClose関数を使用してチケットを取得することはできないことに注意してください。OrderSendは有効なチケットを返します。
MT5(ネッティング)
MT5のトレードプロセスは、一見非常に複雑に見えますが、MT4に比べて管理がはるかに簡単です。これは、少なくともトレーダーの(非プログラマ)側に当てはまるでしょう。
MT5のデフォルトのモードは、ネッティングです。このモードでは、サーバで処理されたオーダーの結果は、単一のポジションに統合されています。この特定のポジションタイプのボリュームは、エントリーしたオーダーの量や種類に基づいて、時間の経過とともに変化させることができます。プログラマの側は、少し複雑です。オーダーの唯一の概念があるMQL4とは異なり、プログラマは、トレードに使用される3つの異なるタイプに対処する必要があります。次の表は、ネッティングMQL5モードとMQL4でのいくつかの比較を示しています。
アーティファクト | MQL5(ネッティング) | MQL4(ラフ) |
---|---|---|
オーダー | トレードリクエスト(保留中または相場) | トレードリクエスト(保留中または相場) |
ディール | ディール(複数可)が単一のオーダー(成行オーダー、または実行保留中のオーダー)に基づいて行われる。 | トレードターミナルに反映されるようなオーダー |
ポジション | トレード(連結) | トレードターミナル上のすべての成行オーダーの合計(オーダータイプが適用されます) |
MQL5では、実行オーダーが相場にあるMQL4に対し、いくつかのプロパティを変更することができ、クライアント側で不変です。すなわち、前者では、オーダーがサーバに送信されたトレードリクエストです。後者では、トレードリクエスト、ならびにそのようなリクエストの結果を表すために使用することができます。見かけの区別はトレードリクエストとトレードの結果との間で行われているので、この局面では、MQL5はプロセス全体がより複雑になりました。MQL5ですべてのトレードがさかのぼることができるのに対し、MQL4でのオーダーは、相場に参入し、異なる構成でそれを残すことができます。
トレードリクエストを送信すると、2つの結果が出ます。トレードが処理されなかった場合、トレードサーバーが(通常はエラーにより)何らかの理由でそれを処理することができなかったとして、トレードが存在しなかったことを意味します。MQL5でトレードが処理された場合、クライアントとサーバは取引したことになります。この場合、オーダーが完全に実行され、部分的に実行させることができます。
MT4には、このオプションがありません。
MT5で、このモードの欠点は、ヘッジを許可しないということです。与えられたシンボル上のポジションのタイプは変更することができます。例えば、場合0.9ロットのボリュームで、ショートポジションを変換します。1.0のボリューム売りオーダーをエントリーし、与えられたシンボルに0.1ロットロングポジションを持ちます。
MT5(ヘッジ)
MT5上のヘッジモードはMT4で使用される表記に似ています。単一のポジションにトレードを統合するよりも、モードをヘッジすることにより、2つ以上のポジションを可能にします。保留中のオーダーがトリガーした時はいつでもポジションが生成され、リクエストはトレードサーバによって処理されています。
アーティファクト | MQL5(ネッティング) | MQL4(ラフ) |
---|---|---|
オーダー | トレードリクエスト(保留中または相場) | トレードリクエスト(保留中または相場) |
ディール | ディールは、単一のオーダーに基づいて作られました。 | 成行オーダーのトレードターミナルに反映されるように。 |
ポジション | シングルトレードのリクエストに基づいたトレード(連結) | トレードターミナルに反映されたオーダー |
これらの違いに対応するクロスプラットフォームのEAができるようにするために、EAに相場に置かれた個々のトレードの詳細を持たせます。トレードが正常に配置されるたびに、オーダーの詳細のコピーは、クラスオブジェクト、COrderクラスに保存されます。次のコードは、その基底クラスの宣言を示しています。
class COrderBase : public CObject { protected: bool m_closed; bool m_suspend; long m_order_flags; int m_magic; double m_price; ulong m_ticket; ENUM_ORDER_TYPE m_type; double m_volume; double m_volume_initial; string m_symbol; public: COrderBase(void); ~COrderBase(void); //---ゲッターとセッター void IsClosed(const bool); bool IsClosed(void) const; void IsSuspended(const bool); bool IsSuspended(void) const; void Magic(const int); int Magic(void) const; void Price(const double); double Price(void) const; void OrderType(const ENUM_ORDER_TYPE); ENUM_ORDER_TYPE OrderType(void) const; void Symbol(const string); string Symbol(void) const; void Ticket(const ulong); ulong Ticket(void) const; void Volume(const double); double Volume(void) const; void VolumeInitial(const double); double VolumeInitial(void) const; //--- output virtual string OrderTypeToString(void) const; //---staticメソッド static bool IsOrderTypeLong(const ENUM_ORDER_TYPE); static bool IsOrderTypeShort(const ENUM_ORDER_TYPE); };
EAは、独自のトレードを記憶しているので、実行されているトレードプラットフォームから独立して動作させることができます。しかし、その欠点として、このクラスのインスタンスは、EA動作中のみ持続します。EAのトレードプラットフォームは情報を保存し、ロードするための手段がない限り、保存されたすべてのデータが損失となり、再起動する必要があります。
トレード識別子(チケット)
クロスプラットフォームの互換性のあるEAを作成するには、COrderのインスタンスを使用して、オーダー(またはポジション)のチケット番号が格納されるメソッドを使用します。この相違点は、以下の表にまとめられています:
オペレーション | MQL4 | MQL5(ネッティング) | MQL5(ヘッジ) |
---|---|---|---|
オーダーの送信 | ニュー・オーダーチケット | 新しいポジションチケット(ポジションなし)、または既存のポジションのチケット(既存のポジション) | 新しいポジションチケット |
部分的決済 | ニュー・オーダーチケット | 同じチケット(残りの場合)、そうでない場合はN/ A | 同じチケット |
オーダーを送信する場合、すべての3つのバージョンがエントリーしたトレードを表現するさまざまなメソッドがあります。トレードリクエストが成功したとき、MQL4では、新しいオーダーが開かれます。この新しいオーダーは、識別子(オーダーチケット)で表されます。MQL5ネッティングモードでは、トレードをエントリーするため、各トレードリクエストは、オーダーチケットによって表されます。ただし、オーダーチケットは、トレードを表現するメソッドでエントリーしたが、結果そのものではないかもしれません。その理由は、MQL4でオーダーチケットは相場に入った結果としてトレードに組まれます。(ただし、取得する際に直接使用することとは異なり、特定のオーダーから生じたポジションを取得しようとしたときにチケット番号が有用です)。同じタイプの既存のポジションがある場合にはさらに、チケットは(MQL4とは異なり)同じままになります。一方、MQL5ヘッジモードでは、それぞれの新たなトレードは、新しいポジション(MQL4オーダーチケット)を生成します。しかし、MQL5(ヘッジモード)で、複数の情報を持っている単一のオーダーが可能であるのに対し、単一トレードのリクエストは常に、単一のオーダーにつながる、ということになります。(SYMBOL_FILLING_FOK)。
部分的オーダー(MQL4)またはポジション(MQL5)を閉じる別の問題もあります。前述したように、特定のチケットで閉じられているMQL4では、( OrderLots)残りのボリュームは新しいチケットが割り当てられますが、トレードチケットは、部分的に閉じました。これは、MQL5では少し異なります。ネッティングでは、(部分的または完全に)ポジションを閉じるために、反対方向にトレードが必要です。ヘッジでは、プロセスはMQL4に類似しています(OrderClose 対 CTradeの PositionClose)が、MQL4とは異なり、部分的にポジションを閉じると、それを表す識別子上の任意の変更をトリガしません。
この問題を解決するには、2つのプラットフォーム上の特定のトレードの識別子を表現するメソッドを実装することです。オーダーチケットはMT5で変更されませんので、単に典型的な数値変数に割り当てることができます。一方、MT4のために、チケット番号を格納する CArrayIntのインスタンスを使用します。COrderBaseについて(MQL5COrderのバージョン用)、チケットメソッドの次のコードが使用されます。
COrderBase::Ticket(const ulong value) { m_ticket=value; }
このメソッドは、次のコードで、MQL4のバージョンに上書きされます。
COrder::Ticket(const ulong ticket) { m_ticket_current.InsertSort((int)ticket); }
ステート
クロスプラットフォームEAのオーダーの内部状態の中で、少なくとも2つの状態が存在することになります。
決済
サスペンド
2つの状態は、非常に類似しているが、根本的な違いがあります。オーダーがすでに閉じられ、EAが内部データでオーダーをアーカイブする必要があるのが閉じた状態です。これはMQL4でヒストリーにオーダーを移動するとほぼ同等でしょう。オーダーまたはそれにリンクされているストップの決済に失敗したときに、サスペンド状態が起こるでしょう。この場合、EAは、それが完全に閉じている状態になるまで再びオーダー(およびそのストップ)を閉じることができます。
ボリューム
MQL4では、ボリュームの計算は簡単です。トレードリクエストを送信するたびに、リクエストのボリュームも含まれています。トレードオブジェクト(CTradeとCExpertTrade)のデフォルト設定でMQL5と等価です。共通の特徴を取得するため、FOKマージンポリシーを与えるでしょう。MQL4とMQL5間で一貫した量の処理を行うために、一つのメソッドは、トレードリクエスト自体のボリュームに基づいて、オーダーのインスタンスのボリュームを導出することでしょう。しかし、これはMQL5のバージョンでは、FOKポリシーに固執する必要はないということを意味します。マージンポリシーを使用することは可能ですが、結果はわずかに異なるでしょう。(同じEAの所定のテストにMQL5版のCOrderインスタンスの数が大きくてもよい)
オーダーコンテナ
EAでCOrderの複数のインスタンスを処理する場合、いくつかのメソッドが必要とされます。これを容易にするクラスは、オーダーコンテナ、またはCOrdersです。クラスはCOrderの CArrayObjとストアのインスタンスを拡張します。これは、ストレージやEAがエントリーしたトレードの検索を可能にするでしょう。クラスの基本テンプレートを以下に示します。
#include <Arrays\ArrayObj.mqh> #include "OrderBase.mqh" class CExpertAdvisor; +------------------------------------------------------------------+ //|| +------------------------------------------------------------------+ class COrdersBase : public CArrayObj { public: COrdersBase(void); ~COrdersBase(void); virtual bool NewOrder(const ulong,const string,const int,const ENUM_ORDER_TYPE,const double,const double); }; +------------------------------------------------------------------+ //|| +------------------------------------------------------------------+ COrdersBase:: COrdersBase(ボイド) { if(!IsSorted()) Sort(); } +------------------------------------------------------------------+ //|| +------------------------------------------------------------------+ COrdersBase::~COrdersBase(void) { } +------------------------------------------------------------------+ //|| +------------------------------------------------------------------+ bool COrdersBase::NewOrder(const ulong ticket,const string symbol,const int magic,const ENUM_ORDER_TYPE type,const double volume,const double price) { COrder *order=new COrder(ticket,symbol,type,volume,price); if(CheckPointer(order)==POINTER_DYNAMIC) if(InsertSort(GetPointer(order))) order.Magic(magic); return false; } +------------------------------------------------------------------+ #ifdef __MQL5__ #include "..\..\MQL5\Order\Orders.mqh" #else #include "..\..\MQL4\Order\Orders.mqh" #endif +------------------------------------------------------------------+
主な関数は、COrderのインスタンスを格納することにあるので、前クラスのインスタンスを追加する手段を有していなければなりません。COrderのインスタンスがインスタンス化されている必要として、CArrayObjのデフォルトのメソッドは、理想的ではないかもしれません。このために、オーダーの新しいインスタンスを作成して、自動的に配列のメンバーとして追加する新規オーダーメソッドがあります。
bool COrdersBase::NewOrder(const ulong ticket,const string symbol,const int magic,const ENUM_ORDER_TYPE type,const double volume,const double price) { COrder *order=new COrder(ticket,symbol,type,volume,price); if(CheckPointer(order)==POINTER_DYNAMIC) if(InsertSort(GetPointer(order))) order.Magic(magic); return false; }
他のメソッドは、このクラスに追加することができます。例は、OnTickメソッドです。このメソッドは、単にそれが格納されているアイテム(COrder)を反復処理します。別の可能性は、COrderも同様にOnTickメソッドを持っていることです。そして次に、COrderのこのメソッドは全ティックにすることができます。
例
コード例では、ロングポジションをエントリーします。ポジションがエントリーした後、トレードの詳細がCOrderのインスタンスに格納されます。これは、(COrderのインスタンスの作成に対処)COrderの新オーダーメソッドを呼び出すことによって達成されます。
どちらのバージョンも、トレードオブジェクト(CExpertTradeX)、オーダーオブジェクト(COrders)、およびシンボルオブジェクト( CSymbolInfo)のインスタンスを使用します。EAのonclickのハンドラでは、トレードのオブジェクトは、Buyメソッドを用いて、ロングポジションをエントリーします。二つのバージョン(MQL4とMQL5)の間の唯一の違いは、トレードの詳細を検索するメソッドにあります。MQL5のバージョンについては、HistoryOrderSelectおよびその他の関連関数によって取得されます。オーダーチケットは、ResultOrderを使用して検索されます。このバージョンの実装を以下に示します。
ulong retcode=trade.ResultRetcode(); ulong order = trade.ResultOrder(); if(retcode==TRADE_RETCODE_DONE) { if(HistoryOrderSelect(order)) { ulong ticket=HistoryOrderGetInteger(order,ORDER_TICKET); ulong magic=HistoryOrderGetInteger(order,ORDER_MAGIC); string symbol = HistoryOrderGetString(order,ORDER_SYMBOL); double volume = HistoryOrderGetDouble(order,ORDER_VOLUME_INITIAL); double price=HistoryOrderGetDouble(order,ORDER_PRICE_OPEN); ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)HistoryOrderGetInteger(order,ORDER_TYPE); orders.NewOrder((int)ticket,symbol,(int)magic,order_type,volume,price); } }
MQL4のトレードオブジェクトは、MQL5のバージョンよりも少ないです。直前にエントリーしたトレードを得るためには、単にアカウント上のすべてのアクティブなオーダーを反復処理し、トレードオブジェクトを拡張することがあります。
for(int i=0;i<OrdersTotal();i++) { if(!OrderSelect(i,SELECT_BY_POS)) continue; if(OrderMagicNumber()==12345) orders.NewOrder(OrderTicket(),OrderSymbol(),OrderMagicNumber(),(ENUM_ORDER_TYPE)OrderType(),OrderLots(),OrderOpenPrice()); }
メインヘッダファイルの完全なコードを以下に示します。
(test_orders.mqh)
#include <MQLx-Orders\Base\Trade\ExpertTradeXBase.mqh> #include <MQLx-Orders\Base\Order\OrdersBase.mqh> CExpertTradeX trade; COrders orders; CSymbolInfo symbolinfo; +------------------------------------------------------------------+ //|エキスパート初期化関数| +------------------------------------------------------------------+ int OnInit() { //--- if(!symbolinfo.Name(Symbol())) { Print("failed to initialize symbol"); return INIT_FAILED; } trade.SetSymbol(GetPointer(symbolinfo)); trade.SetExpertMagicNumber(12345); //--- return(INIT_SUCCEEDED); } +------------------------------------------------------------------+ //|エキスパート初期化解除関数| +------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } +------------------------------------------------------------------+ //|EAティック関数| +------------------------------------------------------------------+ void OnTick() { //--- if(!symbolinfo.RefreshRates()) { Print("cannot refresh symbol"); return; } if(trade.Buy(1.0,symbolinfo.Ask(),0,0)) { #ifdef __MQL5__ int retcode=trade.ResultRetCode(); ulong order = trade.ResultOrder(); if(retcode==TRADE_RETCODE_DONE) { if(HistoryOrderSelect(order)) { ulong ticket=HistoryOrderGetInteger(order,ORDER_TICKET);; ulong magic=HistoryOrderGetInteger(order,ORDER_MAGIC); string symbol = HistoryOrderGetString(order,ORDER_SYMBOL); double volume = HistoryOrderGetDouble(order,ORDER_VOLUME_INITIAL); double price=HistoryOrderGetDouble(order,ORDER_PRICE_OPEN); ENUM_ORDER_TYPE order_type=order_type; m_orders.NewOrder((int)ticket,symbol,(int)magic,order_type,volume,price); } } #else for(int i=0;i<OrdersTotal();i++) { if(!OrderSelect(i,SELECT_BY_POS)) continue; if(OrderMagicNumber()==12345) orders.NewOrder(OrderTicket(),OrderSymbol(),OrderMagicNumber(),(ENUM_ORDER_TYPE)OrderType(),OrderLots(),OrderOpenPrice()); } #endif } Sleep(5000); ExpertRemove(); } +------------------------------------------------------------------+
ヘッダファイルには、必要なすべてのコードがあります。このように、メインソースファイルは、少なくともtest_orders.mqhを含むようにプリプロセッサディレクティブを持っている必要があります:
(test_orders.mq4 及び test_orders.mq5)
#include "test_orders.mqh"
プラットフォーム上でEAアドバイザーを実行すると、次のログエントリを与えます:
MT4では、以下のログファイルが生成されます。
Expert test_orders EURUSD,H1: loaded successfully
test_orders EURUSD,H1: initialized
test_orders EURUSD,H1: open #358063536 buy 1.00 EURUSD at 1.12470 ok
test_orders EURUSD,H1: ExpertRemove function called
test_orders EURUSD,H1: uninit reason 0
Expert test_orders EURUSD,H1: removed
以下は、EAを実行する際の、プラットフォームのスクリーンショットを示しています。なお、EAは ExpertRemove関数の呼び出し以来、(OnTickハンドラの単一の実行)、そのコードを実行するように、チャートから削除されます。
MT5では、生成されたログファイルはほぼ同じになります。
Experts expert test_orders (EURUSD,M1) loaded successfully
Trades '3681006': instant buy 1.00 EURUSD at 1.10669 (deviation: 10)
Trades '3681006': accepted instant buy 1.00 EURUSD at 1.10669 (deviation: 10)
Trades '3681006': deal #75334196 buy 1.00 EURUSD at 1.10669 done (based on order #90114599)
Trades '3681006': order #90114599 buy 1.00 / 1.00 EURUSD at 1.10669 done in 275 ms
Experts expert test_orders (EURUSD,M1) removed
MT4とは異なり、上記の表示されたログメッセージは、ターミナルウィンドウ(ないEAタブ上)のジャーナルタブで確認できます。
EAはまた、EAタブにメッセージを出力します。ただし、ExpertRemove関数の呼び出しがEAのタブにメッセージが表示されるMT4と比較して、行われたことを示すメッセージのみではありません。
拡張
現在の実装では、多くの場合、実世界のEAで使用されている特定の関数が欠けています:
1。トレードの初期stoplossとtakeprofit値とエントリー
2。SLとTPレベル(例えば、損益分岐、トレーリングストップ、または任意のカスタムメソッド)の変更
3。データの永続性 - 2つのプラットフォームは、トレードとそのストップレベルを保存するメソッドの違いがあります。クラスオブジェクトには、2つのメソッドを両立するが、専用メモリに保存されます。したがって、データを永続化するメソッドが必要になります。このようなターミナルで再起動されたEAは、保存やイベントでのオーダー情報をロードするためのへのメソッドを必要とし、クロスプラットフォームのEAとメタトレーダーでグラフを切り替えたとき、そのグラフ上にロードされます。
これらは、今後の記事で説明します。
結論
この記事では、クロスプラットフォームのEAが正常にクラスオブジェクトのインスタンスとしてトレードサーバで処理され、トレードリクエストの詳細を保存することができるようにメソッドについて議論してきました。このオブジェクトのインスタンスは、その後さらに、特定の戦略に基づいて、トレード上で動作するようにEAで使用することができます。基本テンプレートは、より洗練されたトレード戦略において有用であるため、このクラスのオブジェクトに提供されています。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/2590





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索