MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第4部)MQL4との互換性 - ポジション決済イベント
内容
未使用プロパティの削除
イベントの定義中に、MQL5ではすべての時間パラメータがミリ秒単位で設定されていることに気付きました。MQL4にはそのような注文プロパティとポジションプロパティはありませんが、MQL4で秒単位の時間をミリ秒で表して使用することを妨げるものはありません。つまり、秒単位の時間はミリ秒単位の時間で単に重複されていて、どこでも使用されていません。秒単位で設定された時間の受信と表示は、ミリ秒数の表示にある3桁の「尾」を除き、ミリ秒単位の場合とまったく同じです。
したがって、注文にミリ秒単位の同じプロパティがある場合、秒単位で設定されたすべての時間プロパティを注文プロパティから削除することにしました。
何かを削除することにしたので、何かを追加するのもいいでしょう。各注文に新しい「カスタムコメント」プロパティを追加することにします。これは、いつでも任意の注文またはポジション(開いているものおよび決済/削除されたものの両方)に対して設定できます。このプロパティが何故必要なのかと言えば、これは、たとえば、テキストラベルでマークされた注文がさまざまなグラフィカル構造で簡単に表示できるために、特定の条件を満たす注文のテキストラベル、または視覚表示(ライブラリは後で独自のグラフィカルシェルを備える予定です)に必要な場合があるからです。
Defines.mqhファイルを開いて、Ctrl+Fを押して秒単位の時間を含む注文プロパティと、それぞれに似た「_MSC」で終わる(ミリ秒で設定)プロパティを見つけます。「ミリ秒」注文プロパティを削除して「秒」プロパティを残し、整数プロパティの数を24から21に変えます。
//+------------------------------------------------------------------+ //| 注文、取引、ポジションの整数型プロパティ | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = 0, // 注文チケット ORDER_PROP_MAGIC, // 注文のマジックナンバー ORDER_PROP_TIME_OPEN, // Open time (MQL5 Deal time) ORDER_PROP_TIME_CLOSE, // Close time (MQL5 Execution or removal time - ORDER_TIME_DONE) ORDER_PROP_TIME_OPEN_MSC, // Open time in milliseconds (MQL5 Deal time in msc) ORDER_PROP_TIME_CLOSE_MSC, // Close time in milliseconds (MQL5 Execution or removal time - ORDER_TIME_DONE_MSC) ORDER_PROP_TIME_EXP, // 注文有効期限(未決注文用) ORDER_PROP_STATUS, // 注文ステータス(ENUM_ORDER_STATUS列挙体から) ORDER_PROP_TYPE, // Order/deal type ORDER_PROP_REASON, // 取引/注文/ポジション理由またはソース ORDER_PROP_STATE, // 注文ステータス(ENUM_ORDER_STATE列挙体から) ORDER_PROP_POSITION_ID, // ポジションID ORDER_PROP_POSITION_BY_ID, // 反対側のポジションID ORDER_PROP_DEAL_ORDER_TICKET, // Ticket of the order that triggered a deal ORDER_PROP_DEAL_ENTRY, // 取引の方向 – IN、OUT、IN/OUT ORDER_PROP_TIME_UPDATE, // Position change time in seconds ORDER_PROP_TIME_UPDATE_MSC, // Position change time in milliseconds ORDER_PROP_TICKET_FROM, // 親注文チケット ORDER_PROP_TICKET_TO, // 派生注文チケット ORDER_PROP_PROFIT_PT, // 利益(ポイント) ORDER_PROP_CLOSE_BY_SL, // ストップロスによる決済のフラグ ORDER_PROP_CLOSE_BY_TP, // テイクプロフィットによる決済のフラグ ORDER_PROP_GROUP_ID, // Order/position group ID ORDER_PROP_DIRECTION, // Direction type (Buy, Sell) }; #define ORDER_PROP_INTEGER_TOTAL (24) // Total number of integer properties #define ORDER_PROP_INTEGER_SKIP (0) // Number of order properties not used in sorting //+------------------------------------------------------------------+
変更後、注文の整数プロパティのリストは次のようになります。
//+------------------------------------------------------------------+ //| 注文、取引、ポジションの整数型プロパティ | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = 0, // 注文チケット ORDER_PROP_MAGIC, // 注文のマジックナンバー ORDER_PROP_TIME_OPEN, // Open time in milliseconds (MQL5 Deal time) ORDER_PROP_TIME_CLOSE, // Close time in milliseconds (MQL5 Execution or removal time - ORDER_TIME_DONE) ORDER_PROP_TIME_EXP, // 注文有効期限(未決注文用) ORDER_PROP_STATUS, // 注文ステータス(ENUM_ORDER_STATUS列挙体から) ORDER_PROP_TYPE, // Order/deal type ORDER_PROP_REASON, // 取引/注文/ポジション理由またはソース ORDER_PROP_STATE, // 注文ステータス(ENUM_ORDER_STATE列挙体から) ORDER_PROP_POSITION_ID, // ポジションID ORDER_PROP_POSITION_BY_ID, // 反対側のポジションID ORDER_PROP_DEAL_ORDER_TICKET, // Ticket of the order that triggered a deal ORDER_PROP_DEAL_ENTRY, // 取引の方向 – IN、OUT、IN/OUT ORDER_PROP_TIME_UPDATE, // Position change time in milliseconds ORDER_PROP_TICKET_FROM, // 親注文チケット ORDER_PROP_TICKET_TO, // 派生注文チケット ORDER_PROP_PROFIT_PT, // 利益(ポイント) ORDER_PROP_CLOSE_BY_SL, // ストップロスによる決済のフラグ ORDER_PROP_CLOSE_BY_TP, // テイクプロフィットによる決済のフラグ ORDER_PROP_GROUP_ID, // Order/position group ID ORDER_PROP_DIRECTION, // Direction type (Buy, Sell) }; #define ORDER_PROP_INTEGER_TOTAL (21) // Total number of integer properties #define ORDER_PROP_INTEGER_SKIP (0) // Number of order properties not used in sorting //+------------------------------------------------------------------+
時間による選択オプションの列挙体を見つけて、ミリ秒単位の選択定数を削除します。
//+------------------------------------------------------------------+ //| Possible selection options by time | //+------------------------------------------------------------------+ enum ENUM_SELECT_BY_TIME { SELECT_BY_TIME_OPEN, // 開始時間 SELECT_BY_TIME_CLOSE, // 終了時間 SELECT_BY_TIME_OPEN_MSC, // By open time in milliseconds SELECT_BY_TIME_CLOSE_MSC, // By close time in milliseconds }; //+------------------------------------------------------------------+
列挙体は、2つの定数のみで構成されるようになります。
//+------------------------------------------------------------------+ //| Possible selection options by time | //+------------------------------------------------------------------+ enum ENUM_SELECT_BY_TIME { SELECT_BY_TIME_OPEN, // By open time (in milliseconds) SELECT_BY_TIME_CLOSE, // By close time (in milliseconds) }; //+------------------------------------------------------------------+
時間での選択には、MQL5ではミリ秒単位、MQL4では秒単位の時間が使用されるようになります。
新しい「カスタムコメント」プロパティを注文文字列プロパティに追加し、文字列プロパティの総数を4に増やします。
//+------------------------------------------------------------------+ //| 注文、取引、ポジションの文字列型プロパティ | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_STRING { ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // 注文銘柄 ORDER_PROP_COMMENT, // 注文コメント ORDER_PROP_COMMENT_EXT, // Order custom comment ORDER_PROP_EXT_ID // Order ID in the external trading system }; #define ORDER_PROP_STRING_TOTAL (4) // Total number of string properties //+------------------------------------------------------------------+
可能な並び替え基準の列挙体内のミリ秒への参照をすべて削除して(時間で並び替えるときにデフォルトで使用されるようになっています)、カスタムコメントによる並び替え基準を追加しましょう。
//+------------------------------------------------------------------+ //| 注文と取引の並べ替えの可能な基準 | //+------------------------------------------------------------------+ #define FIRST_ORD_DBL_PROP (ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_INTEGER_SKIP) #define FIRST_ORD_STR_PROP (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL-ORDER_PROP_INTEGER_SKIP) enum ENUM_SORT_ORDERS_MODE { //--- 整数型プロパティによって並び替える SORT_BY_ORDER_TICKET = 0, // 注文チケットによって並び替える SORT_BY_ORDER_MAGIC = 1, // 注文マジックナンバーによって並び替える SORT_BY_ORDER_TIME_OPEN = 2, // Sort by order open time in milliseconds SORT_BY_ORDER_TIME_CLOSE = 3, // Sort by order close time in milliseconds SORT_BY_ORDER_TIME_EXP = 4, // Sort by order expiration date SORT_BY_ORDER_STATUS = 5, // Sort by order status (market order/pending order/deal/balance, credit operation) SORT_BY_ORDER_TYPE = 6, // Sort by order type SORT_BY_ORDER_REASON = 7, // Sort by order/position reason/source SORT_BY_ORDER_STATE = 8, // Sort by order status SORT_BY_ORDER_POSITION_ID = 9, // Sort by position ID SORT_BY_ORDER_POSITION_BY_ID = 10, // Sort by opposite position ID SORT_BY_ORDER_DEAL_ORDER = 11, // Sort by order a deal is based on SORT_BY_ORDER_DEAL_ENTRY = 12, // Sort by deal direction – IN, OUT or IN/OUT SORT_BY_ORDER_TIME_UPDATE = 13, // Sort by position change time in seconds SORT_BY_ORDER_TICKET_FROM = 14, // Sort by parent order ticket SORT_BY_ORDER_TICKET_TO = 15, // Sort by derived order ticket SORT_BY_ORDER_PROFIT_PT = 16, // Sort by order profit in points SORT_BY_ORDER_CLOSE_BY_SL = 17, // Sort by order closing by StopLoss flag SORT_BY_ORDER_CLOSE_BY_TP = 18, // Sort by order closing by TakeProfit flag SORT_BY_ORDER_GROUP_ID = 19, // Sort by order/position group ID SORT_BY_ORDER_DIRECTION = 20, // Sort by direction (Buy, Sell) //--- 実数型プロパティによって並び替える SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP, // Sort by open price SORT_BY_ORDER_PRICE_CLOSE = FIRST_ORD_DBL_PROP+1, // Sort by close price SORT_BY_ORDER_SL = FIRST_ORD_DBL_PROP+2, // Sort by StopLoss price SORT_BY_ORDER_TP = FIRST_ORD_DBL_PROP+3, // Sort by TakeProfit price SORT_BY_ORDER_PROFIT = FIRST_ORD_DBL_PROP+4, // Sort by profit SORT_BY_ORDER_COMMISSION = FIRST_ORD_DBL_PROP+5, // Sort by commission SORT_BY_ORDER_SWAP = FIRST_ORD_DBL_PROP+6, // Sort by swap SORT_BY_ORDER_VOLUME = FIRST_ORD_DBL_PROP+7, // Sort by volume SORT_BY_ORDER_VOLUME_CURRENT = FIRST_ORD_DBL_PROP+8, // Sort by unexecuted volume SORT_BY_ORDER_PROFIT_FULL = FIRST_ORD_DBL_PROP+9, // Sort by profit+commission+swap criterion SORT_BY_ORDER_PRICE_STOP_LIMIT= FIRST_ORD_DBL_PROP+10, // Sort by Limit order when StopLimit order is activated //--- 文字列型プロパティによって並び替える SORT_BY_ORDER_SYMBOL = FIRST_ORD_STR_PROP, // Sort by symbol SORT_BY_ORDER_COMMENT = FIRST_ORD_STR_PROP+1, // Sort by comment SORT_BY_ORDER_COMMENT_EXT = FIRST_ORD_STR_PROP+2, // Sort by custom comment SORT_BY_ORDER_EXT_ID = FIRST_ORD_STR_PROP+3 // Sort by order ID in an external trading system }; //+------------------------------------------------------------------+
これでDefines.mqh内の変更は完成です。ここで、削除された注文プロパティへの参照をライブラリファイルからすべて削除する必要があります。
すべてのライブラリファイルですべての並び変えモードのインスタンスを置き換えます
SORT_BY_ORDER_TIME_OPEN_MSC
と
SORT_BY_ORDER_TIME_CLOSE_MSC
を
SORT_BY_ORDER_TIME_OPEN
と
SORT_BY_ORDER_TIME_CLOSE
抽象注文の子孫クラスのHistoryDeal.mqh、HistoryOrder.mqh、HistoryPending.mqh、MarketOrder.mqh、MarketPending.mqh、MarketPosition.mqhファイルで、ミリ秒注文プロパティへのすべての参照を削除します(デフォルトではミリ秒です)。
ORDER_PROP_TIME_CLOSE_MSC
と
ORDER_PROP_TIME_UPDATE_MSC
COrder抽象注文クラスのOrder.mqhファイルで、privateセクションから秒単位で時間を返すメソッドを削除します。
datetime OrderOpenTime(void) const; datetime OrderCloseTime(void) const; datetime OrderExpiration(void) const; datetime PositionTimeUpdate(void) const; datetime PositionTimeUpdateMSC(void) const;
クラスのpublicセクションから時間をミリ秒単位で返す簡易アクセスメソッドを削除します。これは、時間を秒単位で返すメソッドによって返されます。
//+------------------------------------------------------------------+ //| Methods of a simplified access to the order object properties | //+------------------------------------------------------------------+ //--- Return (1) ticket, (2) parent order ticket, (3) derived order ticket, (4) magic number, (5) order reason, //--- (6) position ID, (7) opposite position ID, (8) group ID, (9) type, (10) flag of closing by StopLoss, //--- (11) flag of closing by TakeProfit (12) open time, (13) close time, (14) open time in milliseconds, //--- (15) close time in milliseconds, (16) expiration date, (17) state, (18) status, (19) order type by direction long Ticket(void) const { return this.GetProperty(ORDER_PROP_TICKET); } long TicketFrom(void) const { return this.GetProperty(ORDER_PROP_TICKET_FROM); } long TicketTo(void) const { return this.GetProperty(ORDER_PROP_TICKET_TO); } long Magic(void) const { return this.GetProperty(ORDER_PROP_MAGIC); } long Reason(void) const { return this.GetProperty(ORDER_PROP_REASON); } long PositionID(void) const { return this.GetProperty(ORDER_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID); } long GroupID(void) const { return this.GetProperty(ORDER_PROP_GROUP_ID); } long TypeOrder(void) const { return this.GetProperty(ORDER_PROP_TYPE); } bool IsCloseByStopLoss(void) const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL); } bool IsCloseByTakeProfit(void) const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP); } datetime TimeOpen(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN); } datetime TimeClose(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE); } datetime TimeOpenMSC(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC); } datetime TimeCloseMSC(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC); } datetime TimeExpiration(void) const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP); } ENUM_ORDER_STATE State(void) const { return (ENUM_ORDER_STATE)this.GetProperty(ORDER_PROP_STATE); } ENUM_ORDER_STATUS Status(void) const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS); } ENUM_ORDER_TYPE TypeByDirection(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); } //--- (1)始値、(2)終値、(3)利益、(4)手数料、(5)スワップ、(6)ボリューム、
また、注文カスタムコメントを返すメソッドと設定するメソッドを追加します。
//--- (1)銘柄、(2)コメント、(3)取引所でのIDを返す string Symbol(void) const { return this.GetProperty(ORDER_PROP_SYMBOL); } string Comment(void) const { return this.GetProperty(ORDER_PROP_COMMENT); } string CommentExt(void) const { return this.GetProperty(ORDER_PROP_COMMENT_EXT); } string ExternalID(void) const { return this.GetProperty(ORDER_PROP_EXT_ID); } //--- 完全な注文利益を取得する double ProfitFull(void) const { return this.Profit()+this.Comission()+this.Swap(); } //--- ポイント単位の注文利益を取得する int ProfitInPoints(void) const; //--- Set (1) group ID and (2) custom comment void SetGroupID(const long group_id) { this.SetProperty(ORDER_PROP_GROUP_ID,group_id); } void SetCommentExt(const string comment_ext) { this.SetProperty(ORDER_PROP_COMMENT_EXT,comment_ext); }
COrderクラスのクローズドコンストラクタで秒単位の時間プロパティの保存を削除し、ミリ秒単位のプロパティを秒単位のプロパティに置き換えて、ミリ秒単位の時間を保存します。カスタムコメントの空の文字列としての保存を追加します。
//+------------------------------------------------------------------+ //| Closedパラメトリックコンストラクタ | //+------------------------------------------------------------------+ COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket) { //--- 整数型プロパティを保存する this.m_ticket=ticket; this.m_long_prop[ORDER_PROP_STATUS] = order_status; this.m_long_prop[ORDER_PROP_MAGIC] = this.OrderMagicNumber(); this.m_long_prop[ORDER_PROP_TICKET] = this.OrderTicket(); this.m_long_prop[ORDER_PROP_TIME_EXP] = this.OrderExpiration(); this.m_long_prop[ORDER_PROP_TYPE] = this.OrderType(); this.m_long_prop[ORDER_PROP_STATE] = this.OrderState(); this.m_long_prop[ORDER_PROP_DIRECTION] = this.OrderTypeByDirection(); this.m_long_prop[ORDER_PROP_POSITION_ID] = this.OrderPositionID(); this.m_long_prop[ORDER_PROP_REASON] = this.OrderReason(); this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET] = this.DealOrderTicket(); this.m_long_prop[ORDER_PROP_DEAL_ENTRY] = this.DealEntry(); this.m_long_prop[ORDER_PROP_POSITION_BY_ID] = this.OrderPositionByID(); this.m_long_prop[ORDER_PROP_TIME_OPEN] = this.OrderOpenTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_CLOSE] = this.OrderCloseTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_UPDATE] = this.PositionTimeUpdateMSC(); //--- 実数型プロパティを保存する this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)] = this.OrderOpenPrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)] = this.OrderClosePrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)] = this.OrderProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)] = this.OrderCommission(); this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)] = this.OrderSwap(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)] = this.OrderVolume(); this.m_double_prop[this.IndexProp(ORDER_PROP_SL)] = this.OrderStopLoss(); this.m_double_prop[this.IndexProp(ORDER_PROP_TP)] = this.OrderTakeProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)] = this.OrderVolumeCurrent(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)] = this.OrderPriceStopLimit(); //--- 文字列プロパティを保存する this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)] = this.OrderSymbol(); this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)] = this.OrderComment(); this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)] = this.OrderExternalID(); //--- 追加の整数型プロパティを保存する this.m_long_prop[ORDER_PROP_PROFIT_PT] = this.ProfitInPoints(); this.m_long_prop[ORDER_PROP_TICKET_FROM] = this.OrderTicketFrom(); this.m_long_prop[ORDER_PROP_TICKET_TO] = this.OrderTicketTo(); this.m_long_prop[ORDER_PROP_CLOSE_BY_SL] = this.OrderCloseByStopLoss(); this.m_long_prop[ORDER_PROP_CLOSE_BY_TP] = this.OrderCloseByTakeProfit(); this.m_long_prop[ORDER_PROP_GROUP_ID] = 0; //--- 追加の実数型プロパティを保存する this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)] = this.ProfitFull(); //--- Save additional string properties this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT_EXT)] = ""; } //+------------------------------------------------------------------+
MQL4でポジションIDを返すメソッドで、これが市場のポジションの場合はチケットを返し、その他の場合はゼロを返します。MQL5では、ポジションを開いたチケットがポジションIDになります。これは、ポジションのライフタイム全体を通して変更されません。
よって、MQL4でポジションIDとなりえるのはポジションチケットのみです。MQL4では未決注文にはそのようなIDはありません。注文が削除された場合、対応するポジションは開かれていません。注文が発動した場合、MQL4注文履歴にはそのような注文はありませんが、ポジションはチケットを受け取るため、チケットがポジションIDとして機能します。//+------------------------------------------------------------------+ //| Return the position ID | //+------------------------------------------------------------------+ long COrder::OrderPositionID(void) const { #ifdef __MQL4__ return(this.Status()==ORDER_STATUS_MARKET_POSITION ? this.Ticket() : 0); #else long id=0; switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS)) { case ORDER_STATUS_MARKET_POSITION : id=::PositionGetInteger(POSITION_IDENTIFIER); break; case ORDER_STATUS_MARKET_ORDER : case ORDER_STATUS_MARKET_PENDING : id=::OrderGetInteger(ORDER_POSITION_ID); break; case ORDER_STATUS_HISTORY_PENDING : case ORDER_STATUS_HISTORY_ORDER : id=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break; case ORDER_STATUS_DEAL : id=::HistoryDealGetInteger(m_ticket,DEAL_POSITION_ID); break; default : id=0; break; } return id; #endif } //+------------------------------------------------------------------+
MQL4で反対側のポジションIDを返すメソッドを補足します。
//+------------------------------------------------------------------+ //| 反対側のポジションIDを返す | //+------------------------------------------------------------------+ long COrder::OrderPositionByID(void) const { long ticket=0; #ifdef __MQL4__ string order_comment=::OrderComment(); if(::StringFind(order_comment,"close hedge by #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,16)); #else switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS)) { case ORDER_STATUS_MARKET_ORDER : case ORDER_STATUS_MARKET_PENDING : ticket=::OrderGetInteger(ORDER_POSITION_BY_ID); break; case ORDER_STATUS_HISTORY_PENDING : case ORDER_STATUS_HISTORY_ORDER : ticket=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_BY_ID); break; default : ticket=0; break; } #endif return ticket; } //+------------------------------------------------------------------+
ここで、MQL4および注文コメントに「close hedge by #」行が含まれている場合、コメント文字列での反対注文チケット番号開始インデックスを計算して、メソッドによって返される値に割り当てます。
秒単位の時間の取得はもはや望まれないので、クラスから不要になった2つのメソッドの実装を削除します。
//+------------------------------------------------------------------+ //| Return open time | //+------------------------------------------------------------------+ datetime COrder::OrderOpenTime(void) const { #ifdef __MQL4__ return ::OrderOpenTime(); #else datetime res=0; switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS)) { case ORDER_STATUS_MARKET_POSITION : res=(datetime)::PositionGetInteger(POSITION_TIME); break; case ORDER_STATUS_MARKET_ORDER : case ORDER_STATUS_MARKET_PENDING : res=(datetime)::OrderGetInteger(ORDER_TIME_SETUP); break; case ORDER_STATUS_HISTORY_PENDING : case ORDER_STATUS_HISTORY_ORDER : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP); break; case ORDER_STATUS_DEAL : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME); break; default : res=0; break; } return res; #endif } //+------------------------------------------------------------------+ //| Return close time | //+------------------------------------------------------------------+ datetime COrder::OrderCloseTime(void) const { #ifdef __MQL4__ return ::OrderCloseTime(); #else datetime res=0; switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS)) { case ORDER_STATUS_HISTORY_PENDING : case ORDER_STATUS_HISTORY_ORDER : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE); break; case ORDER_STATUS_DEAL : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME); break; default : res=0; break; } return res; #endif } //+------------------------------------------------------------------+
MQL4 での注文ステータスの表示をより意味のあるものにするのために、注文ステータスの説明を返すメソッドを少し変更します。
//+------------------------------------------------------------------+ //| 注文状態名を返す | //+------------------------------------------------------------------+ string COrder::StatusDescription(void) const { ENUM_ORDER_STATUS status=this.Status(); ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)this.TypeOrder(); return ( status==ORDER_STATUS_BALANCE ? TextByLanguage("Балансовая операция","Balance operation") : #ifdef __MQL5__ status==ORDER_STATUS_MARKET_ORDER || status==ORDER_STATUS_HISTORY_ORDER ? ( type==ORDER_TYPE_CLOSE_BY ? TextByLanguage("Закрывающий ордер","Order for closing by") : TextByLanguage("Ордер на ","The order to ")+(type==ORDER_TYPE_BUY ? TextByLanguage("покупку","buy") : TextByLanguage("продажу","sell")) ) : #else status==ORDER_STATUS_HISTORY_ORDER ? TextByLanguage("Исторический ордер","History order") : #endif status==ORDER_STATUS_DEAL ? TextByLanguage("Сделка","Deal") : status==ORDER_STATUS_MARKET_POSITION ? TextByLanguage("Позиция","Active position") : status==ORDER_STATUS_MARKET_PENDING ? TextByLanguage("Установленный отложенный ордер","Active pending order") : status==ORDER_STATUS_HISTORY_PENDING ? TextByLanguage("Отложенный ордер","Pending order") : EnumToString(status) ); } //+------------------------------------------------------------------+
ここでは、MQL4のリモート未決注文と決済済みポジションについては、ステータスの説明をHistorical
orderとして返します。
注文の整数プロパティの説明を返すメソッドで、ORDER_PROP_TIME_OPEN、ORDER_PROP_TIME_CLOSE、ORDER_PROP_TIME_UPDATEプロパティの説明を含む文字列を変更して、ミリ秒プロパティが返されるようにします。
//+------------------------------------------------------------------+ //| 注文の整数型プロパティの説明を返す | //+------------------------------------------------------------------+ string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property) { return ( //--- 一般的なプロパティ property==ORDER_PROP_MAGIC ? TextByLanguage("Магик","Magic number")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET ? TextByLanguage("Тикет","Ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET_FROM ? TextByLanguage("Тикет родительского ордера","Ticket of parent order")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TICKET_TO ? TextByLanguage("Тикет наследуемого ордера","Inherited order ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : " #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_TIME_EXP ? TextByLanguage("Дата экспирации","Date of expiration")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : (this.GetProperty(property)==0 ? TextByLanguage(": Не задана",": Not set") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)) ) : property==ORDER_PROP_TYPE ? TextByLanguage("Тип","Type")+": "+this.TypeDescription() : property==ORDER_PROP_DIRECTION ? TextByLanguage("Тип по направлению","Type by direction")+": "+this.DirectionDescription() : property==ORDER_PROP_REASON ? TextByLanguage("Причина","Reason")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+this.GetReasonDescription(this.GetProperty(property)) ) : property==ORDER_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position identifier")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_DEAL_ORDER_TICKET ? TextByLanguage("Сделка на основании ордера с тикетом","Deal by order ticket")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": #"+(string)this.GetProperty(property) ) : property==ORDER_PROP_DEAL_ENTRY ? TextByLanguage("Направление сделки","Deal entry")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+this.GetEntryDescription(this.GetProperty(property)) ) : property==ORDER_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position identifier")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_TIME_OPEN ? TextByLanguage("Время открытия в милисекундах","Opening time in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" ) : property==ORDER_PROP_TIME_CLOSE ? TextByLanguage("Время закрытия в милисекундах","Closing time in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" ) : property==ORDER_PROP_TIME_UPDATE ? TextByLanguage("Время изменения позиции в милисекундах","Time to change the position in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property)!=0 ?TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" : "0") ) : property==ORDER_PROP_STATE ? TextByLanguage("Состояние","Statе")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": \""+this.StateDescription()+"\"" ) : //--- 追加のプロパティ property==ORDER_PROP_STATUS ? TextByLanguage("Статус","Status")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": \""+this.StatusDescription()+"\"" ) : property==ORDER_PROP_PROFIT_PT ? ( this.Status()==ORDER_STATUS_MARKET_PENDING ? TextByLanguage("Дистанция от цены в пунктах","Distance from price in points") : TextByLanguage("Прибыль в пунктах","Profit in points") )+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_CLOSE_BY_SL ? TextByLanguage("Закрытие по StopLoss","Close by StopLoss")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No")) ) : property==ORDER_PROP_CLOSE_BY_TP ? TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No")) ) : property==ORDER_PROP_GROUP_ID ? TextByLanguage("Идентификатор группы","Group identifier")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
そして文字列プロパティの説明を返すメソッドにカスタムコメントの説明を返すコードを追加します。
//+------------------------------------------------------------------+ //| 注文の文字列プロパティの説明を返す | //+------------------------------------------------------------------+ string COrder::GetPropertyDescription(ENUM_ORDER_PROP_STRING property) { return ( property==ORDER_PROP_SYMBOL ? TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\"" : property==ORDER_PROP_COMMENT ? TextByLanguage("Комментарий","Comment")+ (this.GetProperty(property)=="" ? TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"") : property==ORDER_PROP_COMMENT_EXT ? TextByLanguage("Пользовательский комментарий","Custom comment")+ (this.GetProperty(property)=="" ? TextByLanguage(": Не задан",": Not set"):": \""+this.GetProperty(property)+"\"") : property==ORDER_PROP_EXT_ID ? TextByLanguage("Идентификатор на бирже","Exchange identifier")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : (this.GetProperty(property)=="" ? TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"")): "" ); } //+------------------------------------------------------------------+
これで、COrder抽象注文クラスの変更は完成です。
DELib.mqhサービス関数ファイルで、注文タイプごとに注文/ポジション名を返す関数を少々改善します。
//+------------------------------------------------------------------+ //| Return order name | //+------------------------------------------------------------------+ string OrderTypeDescription(const ENUM_ORDER_TYPE type,bool as_order=true) { string pref=(#ifdef __MQL5__ "Market order" #else (as_order ? "Market order" : "Position") #endif ); return ( type==ORDER_TYPE_BUY_LIMIT ? "Buy Limit" : type==ORDER_TYPE_BUY_STOP ? "Buy Stop" : type==ORDER_TYPE_SELL_LIMIT ? "Sell Limit" : type==ORDER_TYPE_SELL_STOP ? "Sell Stop" : #ifdef __MQL5__ type==ORDER_TYPE_BUY_STOP_LIMIT ? "Buy Stop Limit" : type==ORDER_TYPE_SELL_STOP_LIMIT ? "Sell Stop Limit" : type==ORDER_TYPE_CLOSE_BY ? TextByLanguage("Закрывающий ордер","Order for closing by") : #else type==ORDER_TYPE_BALANCE ? TextByLanguage("Балансовая операция","Balance operation") : type==ORDER_TYPE_CREDIT ? TextByLanguage("Кредитная операция","Credit operation") : #endif type==ORDER_TYPE_BUY ? pref+" Buy" : type==ORDER_TYPE_SELL ? pref+" Sell" : TextByLanguage("Неизвестный тип ордера","Unknown order type") ); } //+------------------------------------------------------------------+
ここではMQL4でas
an order(注文として)またはas a position(ポジションとして)の注文名の表示を管理するフラグを追加しました。デフォルトは[as
an
order](注文として)です。これがなされた理由は何でしょうか。ポジションを開くイベントが操作ログに送信されるときに、ポジションを開く原因となった注文が角かっこで示されているとします。この場合、成行注文(指値注文ではない)によってチケット123で、ポジションを開くことになった注文として開かれた売りポジションの[Position
Sell #123]メッセージは、もっと意味のある[Market order Sell #123]エントリで置き換えられます。
成行注文とポジションコレクションクラスのAddToListMarket()メソッドを改善しましょう。ミリ秒単位でのORDER_PROP_TIME_UPDATE_MSCポジション更新時間の代わりに、ORDER_PROP_TIME_UPDATEポジション更新時間を使用します(デフォルトではミリ秒に設定)。
//+--------------------------------------------------------------------------------+ //| Add an order or a position to the list of orders and positions on the account | //+--------------------------------------------------------------------------------+ bool CMarketCollection::AddToListMarket(COrder *order) { if(order==NULL) return false; ENUM_ORDER_STATUS status=order.Status(); if(this.m_list_all_orders.InsertSort(order)) { if(status==ORDER_STATUS_MARKET_POSITION) { this.m_struct_curr_market.hash_sum_acc+=order.GetProperty(ORDER_PROP_TIME_UPDATE)+this.ConvertToHS(order); this.m_struct_curr_market.total_volumes+=order.Volume(); this.m_struct_curr_market.total_positions++; return true; } if(status==ORDER_STATUS_MARKET_PENDING) { this.m_struct_curr_market.hash_sum_acc+=this.ConvertToHS(order); this.m_struct_curr_market.total_volumes+=order.Volume(); this.m_struct_curr_market.total_pending++; return true; } } else { ::Print(DFUN,order.TypeDescription()," #",order.Ticket()," ",TextByLanguage("не удалось добавить в список","failed to add to the list")); delete order; } return false; } //+------------------------------------------------------------------+
管理注文を作成してリストに追加するメソッドで、ミリ秒単位の注文時間を注文時間に置き換えます(同じ理由で)。
//+------------------------------------------------------------------+ //| Create and add an order to the list of control orders | //+------------------------------------------------------------------+ bool CMarketCollection::AddToListControl(COrder *order) { if(order==NULL) return false; COrderControl* order_control=new COrderControl(order.PositionID(),order.Ticket(),order.Magic(),order.Symbol()); if(order_control==NULL) return false; order_control.SetTime(order.TimeOpen()); order_control.SetTimePrev(order.TimeOpen()); order_control.SetVolume(order.Volume()); order_control.SetTime(order.TimeOpen()); order_control.SetTypeOrder(order.TypeOrder()); order_control.SetTypeOrderPrev(order.TypeOrder()); order_control.SetPrice(order.PriceOpen()); order_control.SetPricePrev(order.PriceOpen()); order_control.SetStopLoss(order.StopLoss()); order_control.SetStopLossPrev(order.StopLoss()); order_control.SetTakeProfit(order.TakeProfit()); order_control.SetTakeProfitPrev(order.TakeProfit()); if(!this.m_list_control.Add(order_control)) { delete order_control; return false; } return true; } //+------------------------------------------------------------------+
過去の注文および取引のCHistoryCollectionコレクションクラスの時間によって注文を選択するメソッドのHistoryCollection.mqhファイルで、比較されたプロパティの選択を向上します。
すでに4つのプロパティ(ミリ秒単位の開始時間、ミリ秒単位の決済時間、秒単位の開始時間、秒単位の決済時間)の選択があり、そのうち2つを削除したため、選択が簡略化されました。
//+------------------------------------------------------------------+ //| Select orders from the collection with time | //| コレクションから選択する | //+------------------------------------------------------------------+ CArrayObj *CHistoryCollection::GetListByTime(const datetime begin_time=0,const datetime end_time=0, const ENUM_SELECT_BY_TIME select_time_mode=SELECT_BY_TIME_CLOSE) { ENUM_ORDER_PROP_INTEGER property=(select_time_mode==SELECT_BY_TIME_CLOSE ? ORDER_PROP_TIME_CLOSE : ORDER_PROP_TIME_OPEN); CArrayObj *list=new CArrayObj(); if(list==NULL) { ::Print(DFUN+TextByLanguage("Ошибка создания временного списка","Error creating temporary list")); return NULL; } datetime begin=begin_time,end=(end_time==0 ?END_TIME : end_time); if(begin_time>end_time) begin=0; list.FreeMode(false); ListStorage.Add(list); //--- this.m_order_instance.SetProperty(property,begin); int index_begin=this.m_list_all_orders.SearchGreatOrEqual(&m_order_instance); if(index_begin==WRONG_VALUE) return list; this.m_order_instance.SetProperty(property,end); int index_end=this.m_list_all_orders.SearchLessOrEqual(&m_order_instance); if(index_end==WRONG_VALUE) return list; for(int i=index_begin; i<=index_end; i++) list.Add(this.m_list_all_orders.At(i)); return list; } //+------------------------------------------------------------------+
CEngine.mqhファイルで、ミリ秒単位でのSORT_BY_ORDER_TIME_OPEN_MSC時間定数のすべてのインスタンスをSORT_BY_ORDER_TIME_OPEN時間定数で置き換えます。現在、この定数ではデフォルトでミリ秒単位の時間が使用されています。
これで、秒単位の時間の削除に関連するライブラリファイルの改善が完了しました。
MQL4でのポジション決済イベントの実装
MQL4でのポジション決済と注文の削除イベントの発生を特定するための可能なオプションを検索する際に行われたさまざまな実験とテストの結果は、あまりよいものではありませんでした。MQL5と異なり、MQL4ではイベントの決定的な識別に使用できるデータがはるかに少ないです。
MQL5では、特定のポジションに属する注文のデータを簡単に使用してイベントを定義できますが、MQL4ではポジションと未決注文の両方が注文と見なされます。MetaTrader 4で未決注文があった場合、その削除後、以下に直面します。
- 過去の注文数が増加する
- 合計ボリュームが減少する
- 残された市場の注文数は変化しない
イベントが未決注文の削除に属していることを確認するには(MQL4ではポジションも注文です)、ポジション数を確認します。変更されていない場合、アクションは未決注文で実行されています。MetaTrader 4のポジション(注文)のいずれかを部分決済するまで、すべてが正常で論理的なようです。ポジションを部分決済するとき、未決注文を削除するときと同じことが起こります。
- 過去の注文数が増加する(部分決済されたポジション(その注文)が履歴に追加された)
- 部分決済によって口座のボリュームが減少する
- ポジションの数は部分決済をしても減らず、変化しない
これは、未決注文を削除するときと同じ状態で、テスターで前の記事のテストEAを起動すると観察できます。ポジションを開き、部分決済し、指値注文を設定して削除します。最後のアクション中に、ポジションの部分決済と未決注文の削除という2つのエントリが操作ログに同時に表示されます。上記のこれら2つのイベントを定義するための基準の適合性については既に述べました。プログラムは、2つのイベントを一度に定義するだけで、そのうちの1つは正しくありません。
ここでは、未決注文数の変更のチェックを使用できます。ポジションの部分決済を定義するとき、市場の未決注文数の変更も確認します。変更がなければ、イベントはポジションの部分決済です。
すべてが論理的なように見えますが、このアプローチはMetaTrader
4の取引操作の許可された順序に制限を課します。つまり、上記のイベント定義ロジックに違反するため、単一のループで未決注文を削除してポジションを部分決済することはできません。この制限を回避するソリューションは実装できますが、市場のクラスと注文とポジションの過去のコレクションのクラスを作り直す必要があります。市場環境の変化を定義するには、発生した変化の量を管理するのではなく、口座で変更された注文とポジションの一時的なリストを使用する必要があります。イベントはこれらのリストのデータに従って処理されなければなりません。その場合、各イベントタイプには独自のリストがあり、イベントをプログラムに送信するためのイベントの作成は、変更された注文とポジションのリストに従って実行されます。
部分決済を定義するには、口座の合計ボリュームを管理する必要があります。これは、その変更値をCEventsCollectionクラスのRefresh()メソッドに渡す必要があることを意味します。いつものように、すべてはライブラリの基本オブジェクトから始まります。イベントコレクションクラスを更新するメソッドの呼び出しに必要な追加を加えましょう。
CEngine::TradeEventsControl()クラスメソッドのCEventsCollectionクラスのRefreshメソッドに追加の転送可能なパラメータ
を追加します。
//+------------------------------------------------------------------+ //| Check trading events | //+------------------------------------------------------------------+ void CEngine::TradeEventsControl(void) { //--- Initialize the trading events code and flags this.m_is_market_trade_event=false; this.m_is_history_trade_event=false; //--- リストを更新する this.m_market.Refresh(); this.m_history.Refresh(); //--- 初回実行 if(this.IsFirstStart()) { this.m_acc_trade_event=TRADE_EVENT_NO_EVENT; return; } //--- Check the changes in the market status and account history this.m_is_market_trade_event=this.m_market.IsTradeEvent(); this.m_is_history_trade_event=this.m_history.IsTradeEvent(); //--- If there is any event, send the lists, the flags and the number of new orders and deals to the event collection, and update it int change_total=0; CArrayObj* list_changes=this.m_market.GetListChanges(); if(list_changes!=NULL) change_total=list_changes.Total(); if(this.m_is_history_trade_event || this.m_is_market_trade_event || change_total>0) { this.m_events.Refresh(this.m_history.GetList(),this.m_market.GetList(),list_changes,this.m_market.GetListControl(), this.m_is_history_trade_event,this.m_is_market_trade_event, this.m_history.NewOrders(),this.m_market.NewPendingOrders(), this.m_market.NewPositions(),this.m_history.NewDeals(), this.m_market.ChangedVolumeValue()); //--- Get the account's last trading event this.m_acc_trade_event=this.m_events.GetLastTradeEvent(); } } //+------------------------------------------------------------------+
ここで、さらに別のパラメータ(ボリューム変更値)を実装して、CEventsCollectionクラス自体のRefresh()メソッドを変更する必要があります。
EventsCollection.mqhファイルのRefresh()メソッドの定義に新しいパラメータを追加します。
public: //--- Select events from the collection with time within the range from begin_time to end_time CArrayObj *GetListByTime(const datetime begin_time=0,const datetime end_time=0); //--- Return the full event collection list "as is" CArrayObj *GetList(void) { return &this.m_list_events; } //--- 比較された基準を満たす選択された(1)整数、(2)実数、(3)文字列プロパティののリストを返す CArrayObj *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode); } //--- Update the list of events void Refresh(CArrayObj* list_history, CArrayObj* list_market, CArrayObj* list_changes, CArrayObj* list_control, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals, const double changed_volume); //--- Set the control program chart ID void SetChartID(const long id) { this.m_chart_id=id; } //--- Return the last trading event on the account ENUM_TRADE_EVENT GetLastTradeEvent(void) const { return this.m_trade_event; } //--- 直近の取引イベントをリセットする void ResetLastTradeEvent(void) { this.m_trade_event=TRADE_EVENT_NO_EVENT; } //--- コンストラクタ CEventsCollection(void); };
Refresh()メソッドの実装には追加が必要です。
//+------------------------------------------------------------------+ //| Update the event list | //+------------------------------------------------------------------+ void CEventsCollection::Refresh(CArrayObj* list_history, CArrayObj* list_market, CArrayObj* list_changes, CArrayObj* list_control, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals, const double changed_volume) {
ここで、ポジション決済および部分決済のイベントハンドラを作成し、部分決済中に指値注文削除イベントが誤って定義される問題を解決する必要があります。
privateクラスセクションで、管理注文のリストをメソッドに渡すことを追加して、新しいイベント作成メソッドを呼び出す最初の形式を変更します。これは、ポジションを反対のポジションで決済することに関与している注文を識別するために必要になります。また、過去の(決済済み)ポジションのリストを返すメソッドと、ポジションチケットによる管理注文へのポインタを返すメソッドを追加します。
class CEventsCollection : public CListObj { private: CListObj m_list_events; // List of events bool m_is_hedge; // Hedge account flag long m_chart_id; // Control program chart ID int m_trade_event_code; // Trading event code ENUM_TRADE_EVENT m_trade_event; // Account trading event CEvent m_event_instance; // Event object for searching by property MqlTick m_tick; // Last tick structure ulong m_position_id; // Position ID (MQL4) ENUM_ORDER_TYPE m_type_first; // Opening order type (MQL4) //--- Create a trading event depending on the order (1) status and (2) change type void CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market,CArrayObj* list_control); void CreateNewEvent(COrderControl* order); //--- Create an event for a (1) hedging account, (2) netting account void NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market); void NewDealEventNetto(COrder* deal,CArrayObj* list_history,CArrayObj* list_market); //--- Select from the list and return the list of (1) market pending orders, (2) open positions CArrayObj* GetListMarketPendings(CArrayObj* list); CArrayObj* GetListPositions(CArrayObj* list); //--- Select from the list and return the list of historical (1) closed orders, //--- (2) removed pending orders, (3) deals, (4) all closing orders CArrayObj* GetListHistoryPositions(CArrayObj* list); CArrayObj* GetListHistoryPendings(CArrayObj* list); CArrayObj* GetListDeals(CArrayObj* list); CArrayObj* GetListCloseByOrders(CArrayObj* list); //--- Return the list of (1) all position orders by its ID, (2) all position deals by its ID //--- (3) all market entry deals by position ID, (4) all market exit deals by position ID, //--- (5) all position reversal deals by position ID CArrayObj* GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id); CArrayObj* GetListAllDealsInOutByPosID(CArrayObj* list,const ulong position_id); //--- Return the total volume of all deals (1) IN, (2) OUT of the position by its ID double SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id); double SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id); //--- Return the (1) first, (2) last and (3) closing order from the list of all position orders, //--- (4) an order by ticket, (5) market position by ID, //--- (6) the last and (7) penultimate InOut deal by position ID COrder* GetFirstOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetLastOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetCloseByOrderFromList(CArrayObj* list,const ulong position_id); COrder* GetHistoryOrderByTicket(CArrayObj* list,const ulong order_ticket); COrder* GetPositionByID(CArrayObj* list,const ulong position_id); //--- Return the (1) control order by ticket, (2) opening order type by position ticket (MQL4) COrderControl* GetOrderControlByTicket(CArrayObj* list,const ulong ticket); ENUM_ORDER_TYPE GetTypeFirst(CArrayObj* list,const ulong ticket); //--- Return the flag of the event object presence in the event list bool IsPresentEventInList(CEvent* compared_event); //--- Existing order/position change event handler void OnChangeEvent(CArrayObj* list_changes,const int index); public:
クラス本体の外に決済済みポジションのリストを返すメソッドを実装します。
//+------------------------------------------------------------------+ //| Select only closed positions from the list | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListHistoryPositions(CArrayObj *list) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection")); return NULL; } CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_ORDER,EQUAL); return list_orders; } //+------------------------------------------------------------------+
メソッドには、新しいものは何も含まれていません。連載のこれまでの部分で、同様のメソッドをすでに説明しました。
チケットによって管理注文を返すメソッドを実装し、 チケットによって管理注文のタイプを返すメソッドを変更してみましょう。
//+------------------------------------------------------------------+ //| Return a control order by a position ticket (MQL4) | //+------------------------------------------------------------------+ COrderControl* CEventsCollection::GetOrderControlByTicket(CArrayObj *list,const ulong ticket) { if(list==NULL) return NULL; int total=list.Total(); for(int i=0;i<total;i++) { COrderControl* ctrl=list.At(i); if(ctrl==NULL) continue; if(ctrl.Ticket()==ticket) return ctrl; } return NULL; } //+------------------------------------------------------------------+ //| Return an opening order type by a position ticket (MQL4) | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE CEventsCollection::GetTypeFirst(CArrayObj* list,const ulong ticket) { if(list==NULL) return WRONG_VALUE; COrderControl* ctrl=this.GetOrderControlByTicket(list,ticket); if(ctrl==NULL) return WRONG_VALUE; return (ENUM_ORDER_TYPE)ctrl.TypeOrder(); } //+------------------------------------------------------------------+
以前に検討された同様のメソッドと同様に、チケットで管理注文を返すメソッドは簡単で、管理注文のリストと、管理注文を取得したいポジションのチケットを受け取ります。
リストから注文を取得し、リストサイズのループで、メソッドに渡されたチケットと比較します。チケットが等しい場合は、管理注文へのポインタを返し、そうでない場合はNULLを返します。
管理注文タイプを返すGetTypeFirst()メソッドは、以前には、管理注文のリストによるループで構成されていて、メソッドに渡されたチケットと等しいチケットを持つ注文を検索しました。そのような注文が検出されると、そのタイプが返されました。
ポジションチケットによって管理注文を返すメソッドができたので、GetTypeFirst()メソッドから検索ループを削除できます。これはまさに私がやったことです。このメソッドでは、GetOrderControlByTicket()メソッドを使用して、ポジションチケットによって管理注文を取得します。正常に取得された場合(NULL以外)、取得された注文のタイプを返し、そうでない場合は-1を返します
。
MQL4でのポジション決済の処理をイベントコレクション更新メソッドに追加します。
//+------------------------------------------------------------------+ //| Update the event list | //+------------------------------------------------------------------+ void CEventsCollection::Refresh(CArrayObj* list_history, CArrayObj* list_market, CArrayObj* list_changes, CArrayObj* list_control, const bool is_history_event, const bool is_market_event, const int new_history_orders, const int new_market_pendings, const int new_market_positions, const int new_deals, const double changed_volume) { //--- Exit if the lists are empty if(list_history==NULL || list_market==NULL) return; //--- If the event is in the market environment if(is_market_event) { //--- if the order properties were changed int total_changes=list_changes.Total(); if(total_changes>0) { for(int i=total_changes-1;i>=0;i--) { this.OnChangeEvent(list_changes,i); } } //--- if the number of placed pending orders increased (MQL5, MQL4) if(new_market_pendings>0) { //--- Receive the list of the newly placed pending orders CArrayObj* list=this.GetListMarketPendings(list_market); if(list!=NULL) { //--- Sort the new list by order placement time list.Sort(SORT_BY_ORDER_TIME_OPEN); //--- Take the number of orders equal to the number of newly placed ones from the end of the list in a loop (the last N events) int total=list.Total(), n=new_market_pendings; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Receive an order from the list, if this is a pending order, set a trading event COrder* order=list.At(i); if(order!=NULL && order.Status()==ORDER_STATUS_MARKET_PENDING) this.CreateNewEvent(order,list_history,list_market,list_control); } } } #ifdef __MQL4__ //--- If the number of positions increased (MQL4) if(new_market_positions>0) { //--- Get the list of open positions CArrayObj* list=this.GetListPositions(list_market); if(list!=NULL) { //--- Sort the new list by a position open time list.Sort(SORT_BY_ORDER_TIME_OPEN); //--- Take the number of positions equal to the number of newly placed open positions from the end of the list in a loop (the last N events) int total=list.Total(), n=new_market_positions; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Receive a position from the list. If this is a position, search for opening order data and set a trading event COrder* position=list.At(i); if(position!=NULL && position.Status()==ORDER_STATUS_MARKET_POSITION) { //--- Find an order and set (1) a type of an order that led to opening a position and a (2) position ID this.m_type_first=this.GetTypeFirst(list_control,position.Ticket()); this.m_position_id=position.Ticket(); this.CreateNewEvent(position,list_history,list_market,list_control); } } } } //--- If the number of positions decreased or a position is closed partially (MQL4) else if(new_market_positions<0 || (new_market_positions==0 && changed_volume<0 && new_history_orders>0 && new_market_pendings>WRONG_VALUE)) { //--- Get the list of closed positions CArrayObj* list=this.GetListHistoryPositions(list_history); if(list!=NULL) { //--- Sort the new list by position close time list.Sort(SORT_BY_ORDER_TIME_CLOSE); //--- Take the number of positions equal to the number of newly closed positions from the end of the list in a loop (the last N events) int total=list.Total(), n=new_history_orders; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Receive an order from the list. If this is a position, look for data of an opening order and set a trading event COrder* position=list.At(i); if(position!=NULL && position.Status()==ORDER_STATUS_HISTORY_ORDER) { //--- If there is a control order of a closed position COrderControl* ctrl=this.GetOrderControlByTicket(list_control,position.Ticket()); if(ctrl!=NULL) { //--- Set an (1) order type that led to a position opening, (2) position ID and create a position closure event this.m_type_first=(ENUM_ORDER_TYPE)ctrl.TypeOrder(); this.m_position_id=position.Ticket(); this.CreateNewEvent(position,list_history,list_market,list_control); } } } } } #endif } //--- If an event in an account history if(is_history_event) { //--- If the number of historical orders increased (MQL5, MQL4) if(new_history_orders>0) { //--- Get the list of newly removed pending orders CArrayObj* list=this.GetListHistoryPendings(list_history); if(list!=NULL) { //--- Sort the new list by order removal time list.Sort(SORT_BY_ORDER_TIME_CLOSE); //--- Take the number of orders equal to the number of newly removed ones from the end of the list in a loop (the last N events) int total=list.Total(), n=new_history_orders; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Receive an order from the list. これがポジションIDなしの削除された未決注文の場合、 //--- this is an order removal - set a trading event COrder* order=list.At(i); if(order!=NULL && order.Status()==ORDER_STATUS_HISTORY_PENDING && order.PositionID()==0) this.CreateNewEvent(order,list_history,list_market,list_control); } } } //--- If the number of deals increased (MQL5) #ifdef __MQL5__ if(new_deals>0) { //--- Receive the list of deals only CArrayObj* list=this.GetListDeals(list_history); if(list!=NULL) { //--- Sort the new list by deal time list.Sort(SORT_BY_ORDER_TIME_OPEN); //--- Take the number of deals equal to the number of new ones from the end of the list in a loop (the last N events) int total=list.Total(), n=new_deals; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Receive a deal from the list and set a trading event COrder* order=list.At(i); if(order!=NULL) this.CreateNewEvent(order,list_history,list_market); } } } #endif } } //+------------------------------------------------------------------+
ポジション決済の処理は非常に明確で透明です。その説明はコメントされており、コードのコメントは十分に詳細に記述されているため、そのロジックについて説明する意味はありません。
新しいイベントを作成するためのメソッドを呼び出す最初の形式にもう1つのリストが渡されるようになったので、CEventsCollectionイベントコレクションクラスのRefresh()メソッドで、MQL5のメソッドの呼び出しに管理注文のリストの受け渡しを追加します。
//--- If the number of deals increased (MQL5) #ifdef __MQL5__ if(new_deals>0) { //--- Receive the list of deals only CArrayObj* list=this.GetListDeals(list_history); if(list!=NULL) { //--- Sort the new list by deal time list.Sort(SORT_BY_ORDER_TIME_OPEN); //--- Take the number of deals equal to the number of new ones from the end of the list in a loop (the last N events) int total=list.Total(), n=new_deals; for(int i=total-1; i>=0 && n>0; i--,n--) { //--- Receive a deal from the list and set a trading event COrder* order=list.At(i); if(order!=NULL) this.CreateNewEvent(order,list_history,list_market,list_control); } } } #endif
次に、イベント作成メソッドで、イベントコールの最初の形式にポジション決済イベントを作成するコードを追加しましょう。
CEventsCollection::CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market,CArrayObj* list_control).
メソッドは非常にかさばるので、MQL4でのポジション決済イベントを作成するためのコードのみを見ていきます。
//--- Position closed (__MQL4__) if(status==ORDER_STATUS_HISTORY_ORDER) { //--- Set the "position closed" trading event code this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED; //--- Set the "request executed in full" reason ENUM_EVENT_REASON reason=EVENT_REASON_DONE; //--- If the closure by StopLoss flag is set for an order, a position is closed by StopLoss if(order.IsCloseByStopLoss()) { //--- set the "closure by StopLoss" reason reason=EVENT_REASON_DONE_SL; //--- add the StopLoss closure flag to the event code this.m_trade_event_code+=TRADE_EVENT_FLAG_SL; } //--- If the closure by TakeProfit flag is set for an order, a position is closed by TakeProfit if(order.IsCloseByTakeProfit()) { //--- set the "closure by TakeProfit" reason reason=EVENT_REASON_DONE_TP; //--- add the TakeProfit closure flag to the event code this.m_trade_event_code+=TRADE_EVENT_FLAG_TP; } //--- If an order has the property with an inherited order filled, a position is closed partially if(order.TicketTo()>0) { //--- set the "partial closure" reason reason=EVENT_REASON_DONE_PARTIALLY; //--- add the partial closure flag to the event code this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; } //--- Check closure by an opposite position COrder* order_close_by=this.GetCloseByOrderFromList(list_history,order.Ticket()); //--- Declare the variables of the opposite order properties and initialize them with the values of the current closed position ENUM_ORDER_TYPE close_by_type=this.m_type_first; double close_by_volume=order.Volume(); ulong close_by_ticket=order.Ticket(); long close_by_magic=order.Magic(); string close_by_symbol=order.Symbol(); //--- If the list of historical orders features an order with a closed position ID, the position is closed by an opposite one if(order_close_by!=NULL) { //--- Fill in the properties of an opposite closing order using data on the opposite position properties close_by_type=(ENUM_ORDER_TYPE)order_close_by.TypeOrder(); close_by_ticket=order_close_by.Ticket(); close_by_magic=order_close_by.Magic(); close_by_symbol=order_close_by.Symbol(); close_by_volume=order_close_by.Volume(); //--- set the "close by" reason reason=EVENT_REASON_DONE_BY_POS; //--- add the close by flag to the event code this.m_trade_event_code+=TRADE_EVENT_FLAG_BY_POS; //--- Take data on (1) closed and (2) opposite positions from the list of control orders //--- (in this list, the properties of two opposite positions remain the same as before the closure) COrderControl* ctrl_closed=this.GetOrderControlByTicket(list_control,order.Ticket()); COrderControl* ctrl_close_by=this.GetOrderControlByTicket(list_control,close_by_ticket); double vol_closed=0; double vol_close_by=0; //--- If no errors detected when receiving these two opposite orders if(ctrl_closed!=NULL && ctrl_close_by!=NULL) { //--- Calculate closed volumes of a (1) closed and (2) an opposite positions vol_closed=ctrl_closed.Volume()-order.Volume(); vol_close_by=vol_closed-close_by_volume; //--- If a position is closed partially (the previous volume exceeds the currently closed one) if(ctrl_closed.Volume()>order.Volume()) { //--- add the partial closure flag to an event code this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL; //--- set the "partial closure" reason reason=EVENT_REASON_DONE_PARTIALLY_BY_POS; } } } //--- Create the position closure event CEvent* event=new CEventPositionClose(this.m_trade_event_code,order.Ticket()); if(event!=NULL && order.PositionByID()==0) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeClose()); // Event time event.SetProperty(EVENT_PROP_REASON_EVENT,reason); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,close_by_type); // Event deal type event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Event deal ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,close_by_type); // Type of the order that triggered an event deal (the last position order) event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,this.m_type_first); // Type of an order that triggered a position deal (the first position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,close_by_ticket); // Ticket of an order, based on which an event deal is opened (the last position order) event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // Ticket of an order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_POSITION_ID,this.m_position_id); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,close_by_ticket); // Opposite position ID event.SetProperty(EVENT_PROP_MAGIC_BY_ID,close_by_magic); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder()); // Position order type before direction changed event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket()); // Position order ticket before direction changed event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder()); // Current position order type event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket()); // Current position order ticket event.SetProperty(EVENT_PROP_PRICE_OPEN_BEFORE,order.PriceOpen()); // Order price before modification< event.SetProperty(EVENT_PROP_PRICE_SL_BEFORE,order.StopLoss()); // StopLoss before modification event.SetProperty(EVENT_PROP_PRICE_TP_BEFORE,order.TakeProfit()); // TakeProfit before modification event.SetProperty(EVENT_PROP_PRICE_EVENT_ASK,this.m_tick.ask); // Ask price during an event event.SetProperty(EVENT_PROP_PRICE_EVENT_BID,this.m_tick.bid); // Bid price during an event event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic()); // Order/deal/position magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpen()); // Time of an order, based on which a position deal is opened (the first position order) event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen()); // Event price event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen()); // Order/deal/position open price event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose()); // Order/deal/position close price event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // StopLoss position price event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // TakeProfit position price event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()-order.VolumeCurrent()); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent()); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,order.Volume()); // Executed position volume event.SetProperty(EVENT_PROP_PROFIT,order.Profit()); // Profit event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,close_by_symbol); // Opposite position symbol //--- Set control program chart ID, decode the event code and set the event type event.SetChartID(this.m_chart_id); event.SetTypeEvent(); //--- Add the event object if it is not in the list if(!this.IsPresentEventInList(event)) { this.m_list_events.InsertSort(event); //--- Send a message about the event and set the value of the last trading event event.SendEvent(); this.m_trade_event=event.TradeEvent(); } //--- If the event is already present in the list, remove a new event object and display a debugging message else { ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event is already in the list.")); delete event; } } } #endif
前の記事でイベントを作成するロジックについてはすでに説明したので、簡単に説明しましょう。最初に、イベントコードを「ポジション決済」、イベント理由を「リクエストが完全に実行されました」として設定します。次に、決済された注文のさまざまなプロパティを表示し、これらのプロパティに基づいて必要なフラグをイベントコードに追加し、必要に応じてイベントの理由を変更します。反対のポジション決済を定義するには、決済済みポジションの管理注文のデータを使用します。このデータは、複合決済の前に反対の注文に関するすべての情報を提供し、注文タイプとそのボリュームを定義し、どの注文が決済に至ったかを見つけることができます。
次に、収集されたすべてのデータがイベントプロパティに記録され、新しいポジション決済イベントが作成されます。
また、MQL4ですべてのポジションの成約注文のリストを 返すメソッドを改善しました。
//+------------------------------------------------------------------+ //| Return the list of all closing CloseBy orders from the list | //+------------------------------------------------------------------+ CArrayObj* CEventsCollection::GetListCloseByOrders(CArrayObj *list) { if(list.Type()!=COLLECTION_HISTORY_ID) { Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of history collection")); return NULL; } #ifdef __MQL5__ CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,ORDER_TYPE_CLOSE_BY,EQUAL); #else CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_BY_ID,0,NO_EQUAL); #endif return list_orders; } //+------------------------------------------------------------------+
MQL5では過去の注文のリストからORDER_TYPE_CLOSE_BYタイプの注文のみを選択します。MQL4ではこのような注文は存在しないため、opposite
position IDプロパティが記入されている注文のみ(すなわち、このプロパティがゼロでない注文)を選択します。
MQL4で最後の決済されたポジションの注文を返すメソッドも改善されています。
//+------------------------------------------------------------------+ //| Return the last closing order | //| 最初の過去の成行注文を返す | //+------------------------------------------------------------------+ COrder* CEventsCollection::GetCloseByOrderFromList(CArrayObj *list,const ulong position_id) { #ifdef __MQL5__ CArrayObj* list_orders=this.GetListAllOrdersByPosID(list,position_id); list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_TYPE,ORDER_TYPE_CLOSE_BY,EQUAL); if(list_orders==NULL || list_orders.Total()==0) return NULL; list_orders.Sort(SORT_BY_ORDER_TIME_OPEN); #else CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_POSITION_BY_ID,position_id,EQUAL); if(list_orders==NULL || list_orders.Total()==0) return NULL; list_orders.Sort(SORT_BY_ORDER_TIME_CLOSE); #endif COrder* order=list_orders.At(list_orders.Total()-1); return(order!=NULL ?order : NULL); } //+------------------------------------------------------------------+
MQL5では、いつでもポジションに属する最後の注文を取得できますが、MQL4ではそうではありません。したがって、 MQL4の場合、存在する場合は反対ポジションの注文を返し、反対ポジションで決済されていない場合はNULLを返すことにしました 。これにより、ポジションが反対のポジションによって決済されたときに、決済された注文の取得を部分的に実装できます。
これらはすべて、MQL4のポジション決済を定義するために必要な変更と改善です。
すべてのクラスの完全なコードは、下記に添付されているファイルでご覧ください。
テスト
テストを実行するには、\MQL4\Experts\TestDoEasy\Part10にある前の記事のテストEAであるTestDoEasyPart10.mq4を、新しいフォルダー\MQL4\Experts\TestDoEasy\Part11にTestDoEasyPart11.mq4として保存します。
可能な注文と取引の並び替え基準の列挙であるENUM_SORT_ORDERS_MODEからミリ秒単位の時定数を削除したため、削除された定数が設定される場所での配置時間による並び替え
list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
をEAの注文削除ボタン押下ハンドラでの時間による並び替えで置き換える必要があります。
//--- If the BUTT_DELETE_PENDING button is pressed: Remove the first pending order else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Get the list of all orders CArrayObj* list=engine.GetListMarketPendings(); if(list!=NULL) { //--- Sort the list by placement time list.Sort(SORT_BY_ORDER_TIME_OPEN); int total=list.Total(); //--- In the loop from the position with the most amount of time for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- delete the order by its ticket #ifdef __MQL5__ trade.OrderDelete(order.Ticket()); #else PendingOrderDelete(order.Ticket()); #endif } } }
EAをコンパイルし、テスターのStopLoss in pointsおよびTakeProfit in points入力をゼロに設定して、ストップ注文なしでポジションが決済されるようにします。次に、テスターでEAを起動し、ポジションを開いて部分的に決済します。
次に指値注文を出して削除します。
部分決済イベントと未決注文削除イベントは、個別のイベントとして定義されています。
EAをもう一度起動し、イベントの定義を確認するボタンをクリックします。
ご覧のとおり、イベントは正しく定義されています。「close by」イベントが定義され、ストップレベルの変更と未決注文価格も追跡されています。
次の段階
本稿では、MQL4との互換性のための既存のライブラリ関数の変換を終了しました。今後の記事では、新しい「口座」オブジェクトと「銘柄」オブジェクト、それらのコレクションとイベントを作成します。
現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問、コメント、提案はコメント欄にお願いします。
シリーズのこれまでの記事:
第1部: 概念、データ管理
第2部:
過去の注文と取引のコレクション
第3部:注文と取引のコレクション、検索と並び替え
第4部:
取引イベント概念
第5部: 取引イベントのクラスとコレクション取引イベントのプログラムへの送信
第6部:
ネッティング勘定イベント
第7部: StopLimit注文発動イベント、注文およびポジション変更イベントの機能の準備
第8部:
注文とポジションの変更イベント。第9部:MQL4との互換性 - データの準備
第10部:MQL4との互換性
- ポジションオープンイベントと指値注文発動イベント
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/6921
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索