MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第8部): 注文およびポジション変更イベント
内容
前の記事で、StopLimit注文発動イベントの追跡を実装しました。イベントを定義するための機能全体が拡張可能になり、他の必要なイベントの検索を簡単に追加できるようになりました。
StopLimit注文の発動を新しい未決注文の発注として「限定」しました。これは、新しい注文タイプには新しい発注イベントが必要なため、合理的です。本稿では、異なる種類のイベント、つまり既存の注文とポジションの変更を追跡します(プログラムで適切なイベントを取得したことによって、発注とポジションの開始についてはすでにわかっています)。これは、CEvent抽象イベントクラスから派生したさらに別の(変更イベント)クラスが必要であることを意味します。
変更イベントクラス
いつものように、必要な列挙型定数を用意することから始めます。
Defines.mqhライブラリファイルを開いて、並べ替え時の未使用の注文の整数プロパティの数を含むマクロ置換内でスキップされたプロパティの数をゼロに設定します。注文の整数プロパティがすべて必要になります。以前は、検索と並べ替えの際に1つのプロパティ(列挙定数のリストの最後であるORDER_PROP_DIRECTION)をスキップしていました。
本稿以降では、市場コレクションリスト内のすべての一方向の未決注文を検索するためにこのプロパティが必要になります。
//+------------------------------------------------------------------+ //| 注文、取引、ポジションの整数型プロパティ | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = 0, // 注文チケット ORDER_PROP_MAGIC, // 注文のマジックナンバー ORDER_PROP_TIME_OPEN, // 開始時間(MQL5取引時間) ORDER_PROP_TIME_CLOSE, // 終了時間(MQL5実行または削除時間 - ORDER_TIME_DONE) ORDER_PROP_TIME_OPEN_MSC, // ミリ秒での開始時間(ミリ秒でのMQL5取引時間) ORDER_PROP_TIME_CLOSE_MSC, // ミリ秒での終了時間(ミリ秒でのMQL5実行または削除時間 - 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, // ポジション変更時間(秒) ORDER_PROP_TIME_UPDATE_MSC, // ポジション変更時間(ミリ秒) 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 //+------------------------------------------------------------------+
プロパティのスキップを削除したので、それによって並び替える関数も追加する必要があります。注文と取引の列挙の並べ替えの基準に注文の方向による並び替えを追加しましょう。
//+------------------------------------------------------------------+ //| 注文と取引の並べ替えの可能な基準 | //+------------------------------------------------------------------+ #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_TIME_CLOSE = 3, // 注文終了時間によって並び替える SORT_BY_ORDER_TIME_OPEN_MSC = 4, // ミリ秒での注文開始時間によって並び替える SORT_BY_ORDER_TIME_CLOSE_MSC = 5, // ミリ秒での注文終了時間によって並び替える SORT_BY_ORDER_TIME_EXP = 6, // 注文有効期限によって並び替える SORT_BY_ORDER_STATUS = 7, // Sort by order status (market order/pending order/deal/balance, credit operation) SORT_BY_ORDER_TYPE = 8, // 注文タイプによって並び替える SORT_BY_ORDER_REASON = 9, // Sort by order/position reason/source SORT_BY_ORDER_STATE = 10, // Sort by order status SORT_BY_ORDER_POSITION_ID = 11, // ポジションIDによって並び替える SORT_BY_ORDER_POSITION_BY_ID = 12, // 反対側のポジションIDによって並び替える SORT_BY_ORDER_DEAL_ORDER = 13, // 取引が基づく注文によって並び替える SORT_BY_ORDER_DEAL_ENTRY = 14, // 取引方向(IN、OUT、IN/OUT)によって並び替える SORT_BY_ORDER_TIME_UPDATE = 15, // ポジション変更時間(秒)によって並び替える SORT_BY_ORDER_TIME_UPDATE_MSC = 16, // ポジション変更時間(ミリ秒)によって並び替える SORT_BY_ORDER_TICKET_FROM = 17, // 親の注文チケットによって並び替える SORT_BY_ORDER_TICKET_TO = 18, // 派生した注文チケットによって並び替える SORT_BY_ORDER_PROFIT_PT = 19, // Sort by order profit in points SORT_BY_ORDER_CLOSE_BY_SL = 20, // 注文の「ストップロスによる決済」フラグによって並び替える SORT_BY_ORDER_CLOSE_BY_TP = 21, // 注文の「テイクプロフィットによる決済」フラグによって並び替える SORT_BY_ORDER_GROUP_ID = 22, // Sort by order/position group ID SORT_BY_ORDER_DIRECTION = 23, // 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_EXT_ID = FIRST_ORD_STR_PROP+2 // Sort by order ID in an external trading system }; //+------------------------------------------------------------------+
前述しましたが、イベントIDを作成するには、発生したイベントのタイプを示す一連のフラグで構成されるイベントコードを使用します。変更イベントを追跡するので、取引イベントフラグの列挙に必要なフラグを追加する必要があります。
//+------------------------------------------------------------------+ //| 口座の取引イベントリスト | //+------------------------------------------------------------------+ enum ENUM_TRADE_EVENT_FLAGS { TRADE_EVENT_FLAG_NO_EVENT = 0, // イベントなし TRADE_EVENT_FLAG_ORDER_PLASED = 1, // 未決注文が出された TRADE_EVENT_FLAG_ORDER_REMOVED = 2, // 未決注文の削除 TRADE_EVENT_FLAG_ORDER_ACTIVATED = 4, // 未決注文の発動 TRADE_EVENT_FLAG_POSITION_OPENED = 8, // ポジションが開いた TRADE_EVENT_FLAG_POSITION_CHANGED= 16, // Position changed TRADE_EVENT_FLAG_POSITION_REVERSE= 32, // Position reversed TRADE_EVENT_FLAG_POSITION_CLOSED = 64, // Position closed TRADE_EVENT_FLAG_ACCOUNT_BALANCE = 128, // Balance operation (clarified by a deal type) TRADE_EVENT_FLAG_PARTIAL = 256, // Partial execution TRADE_EVENT_FLAG_BY_POS = 512, // Executed by opposite position TRADE_EVENT_FLAG_PRICE = 1024, // Placement price modification TRADE_EVENT_FLAG_SL = 2048, // Executed by StopLoss TRADE_EVENT_FLAG_TP = 4096, // Executed by TakeProfit TRADE_EVENT_FLAG_ORDER_MODIFY = 8192, // Order modification TRADE_EVENT_FLAG_POSITION_MODIFY = 16384, // Position modification }; //+------------------------------------------------------------------+
可能な取引イベントのリストに注文とポジション変更イベントを追加しましょう(以前は、リストにいくつかのイベントを追加しましたが、これは定数の予備的な宣言でした)。
//+------------------------------------------------------------------+ //| 口座で可能な取引イベントのリスト | //+------------------------------------------------------------------+ enum ENUM_TRADE_EVENT { TRADE_EVENT_NO_EVENT = 0, // No trading event TRADE_EVENT_PENDING_ORDER_PLASED, // 未決注文が出された TRADE_EVENT_PENDING_ORDER_REMOVED, // 未決注文の削除 //--- ENUM_DEAL_TYPE列挙体メンバに一致する列挙体メンバ //--- (constant order below should not be changed, no constants should be added/deleted) TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT, // Accruing credit (3) TRADE_EVENT_ACCOUNT_CHARGE, // 追加の課金 TRADE_EVENT_ACCOUNT_CORRECTION, // エントリの修正 TRADE_EVENT_ACCOUNT_BONUS, // Accruing bonuses TRADE_EVENT_ACCOUNT_COMISSION, // 追加手数料 TRADE_EVENT_ACCOUNT_COMISSION_DAILY, // Commission charged at the end of a trading day TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY, // Commission charged at the end of a trading month TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY, // 取引日の終わりに請求されるエージェント手数料 TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY, // 月末に請求される代理人手数料 TRADE_EVENT_ACCOUNT_INTEREST, // Accrued interest on free funds TRADE_EVENT_BUY_CANCELLED, // キャンセルされた買い取引 TRADE_EVENT_SELL_CANCELLED, // キャンセルされた売り取引 TRADE_EVENT_DIVIDENT, // Accruing dividends TRADE_EVENT_DIVIDENT_FRANKED, // Accruing franked dividends TRADE_EVENT_TAX = DEAL_TAX, // Tax //--- constants related to the DEAL_TYPE_BALANCE deal type from the DEAL_TYPE_BALANCE enumeration TRADE_EVENT_ACCOUNT_BALANCE_REFILL = DEAL_TAX+1, // Replenishing account balance TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = DEAL_TAX+2, // Withdrawing funds from an account //--- Remaining possible trading events //--- (constant order below can be changed, constants can be added/deleted) TRADE_EVENT_PENDING_ORDER_ACTIVATED = DEAL_TAX+3, // Pending order activated by price TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL, // 格による未決注文の部分的な発動 TRADE_EVENT_POSITION_OPENED, // ポジションが開いた TRADE_EVENT_POSITION_OPENED_PARTIAL, // ポジションが部分的に開いた TRADE_EVENT_POSITION_CLOSED, // ポジションが閉じた TRADE_EVENT_POSITION_CLOSED_BY_POS, // Position closed partially TRADE_EVENT_POSITION_CLOSED_BY_SL, // ストップロスによってポジションが閉じた TRADE_EVENT_POSITION_CLOSED_BY_TP, // テイクプロフィットによってポジションが閉じた TRADE_EVENT_POSITION_REVERSED_BY_MARKET, // Position reversal by a new deal (netting) TRADE_EVENT_POSITION_REVERSED_BY_PENDING, // Position reversal by activating a pending order (netting) TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL, // Position reversal by partial market order execution (netting) TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL, // Position reversal by partial pending order activation (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET, // Added volume to a position by a new deal (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL, // Added volume to a position by partial activation of a market order (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING, // Added volume to a position by activating a pending order (netting) TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL, // Added volume to a position by partial activation of a pending order (netting) TRADE_EVENT_POSITION_CLOSED_PARTIAL, // ポジションが部分的に閉じた TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS, // Position closed partially by an opposite one TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL, // ストップロスによってポジションが部分的に閉じた TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP, // テイクプロフィットによってポジションが部分的に閉じた TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER, // StopLimit order activation TRADE_EVENT_MODIFY_ORDER_PRICE, // Changing order price TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS, // Changing order and StopLoss price TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT, // Changing order and TakeProfit price TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT, // Changing order, StopLoss and TakeProfit price TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT, // Changing order's StopLoss and TakeProfit price TRADE_EVENT_MODIFY_ORDER_STOP_LOSS, // Changing order's StopLoss TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT, // Changing order's TakeProfit TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT, // Changing position's StopLoss and TakeProfit TRADE_EVENT_MODIFY_POSITION_STOP_LOSS, // Changing position's StopLoss TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT, // Changing position's TakeProfit }; //+------------------------------------------------------------------+
ここではCEvent抽象イベントクラスから派生した新しいクラスを開発しているので、新しく派生したクラスに対してさらに別のイベントステータス「変更」を設定する必要があります。イベントステータスの列挙リストに追加しましょう。
//+------------------------------------------------------------------+ //| Event status | //+------------------------------------------------------------------+ enum ENUM_EVENT_STATUS { EVENT_STATUS_MARKET_POSITION, // Market position event (opening, partial opening, partial closing, adding volume, reversal) EVENT_STATUS_MARKET_PENDING, // Market pending order event (placing) EVENT_STATUS_HISTORY_PENDING, // Historical pending order event (removal) EVENT_STATUS_HISTORY_POSITION, // Historical position event (closing EVENT_STATUS_BALANCE, // Balance operation event (accruing balance, withdrawing funds and events from the ENUM_DEAL_TYPE enumeration) EVENT_STATUS_MODIFY // Order/position modification event }; //+------------------------------------------------------------------+
イベントの理由の列挙リストに「変更」を追加しましょう。
//+------------------------------------------------------------------+ //| Event reason | //+------------------------------------------------------------------+ enum ENUM_EVENT_REASON { EVENT_REASON_REVERSE, // Position reversal (netting) EVENT_REASON_REVERSE_PARTIALLY, // Position reversal by partial request execution (netting) EVENT_REASON_REVERSE_BY_PENDING, // Position reversal by pending order activation (netting) EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY, // Position reversal in case of a pending order partial execution (netting) //--- All constants related to a position reversal should be located in the above list EVENT_REASON_ACTIVATED_PENDING, // Pending order activation EVENT_REASON_ACTIVATED_PENDING_PARTIALLY, // Pending order partial activation EVENT_REASON_STOPLIMIT_TRIGGERED, // StopLimit order activation EVENT_REASON_MODIFY, // Modification EVENT_REASON_CANCEL, // Cancelation EVENT_REASON_EXPIRED, // Order expiration EVENT_REASON_DONE, // Request executed in full EVENT_REASON_DONE_PARTIALLY, // Request executed partially EVENT_REASON_VOLUME_ADD, // Add volume to a position (netting) EVENT_REASON_VOLUME_ADD_PARTIALLY, // Add volume to a position by a partial request execution (netting) EVENT_REASON_VOLUME_ADD_BY_PENDING, // Add volume to a position when a pending order is activated (netting) EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY, // Add volume to a position when a pending order is partially executed (netting) EVENT_REASON_DONE_SL, // Closing by StopLoss EVENT_REASON_DONE_SL_PARTIALLY, // Partial closing by StopLoss EVENT_REASON_DONE_TP, // Closing by TakeProfit EVENT_REASON_DONE_TP_PARTIALLY, // Partial closing by TakeProfit EVENT_REASON_DONE_BY_POS, // Closing by an opposite position EVENT_REASON_DONE_PARTIALLY_BY_POS, // Partial closing by an opposite position EVENT_REASON_DONE_BY_POS_PARTIALLY, // Partial closing by an opposite position EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY, // Closing an opposite position by a partial volume //--- Constants related to DEAL_TYPE_BALANCE deal type from the ENUM_DEAL_TYPE enumeration EVENT_REASON_BALANCE_REFILL, // Refilling the balance EVENT_REASON_BALANCE_WITHDRAWAL, // Withdrawing funds from the account //--- List of constants is relevant to TRADE_EVENT_ACCOUNT_CREDIT from the ENUM_TRADE_EVENT enumeration and shifted to +13 relative to ENUM_DEAL_TYPE (EVENT_REASON_ACCOUNT_CREDIT-3) EVENT_REASON_ACCOUNT_CREDIT, // Accruing credit EVENT_REASON_ACCOUNT_CHARGE, // Additional charges EVENT_REASON_ACCOUNT_CORRECTION, // Correcting entry EVENT_REASON_ACCOUNT_BONUS, // Accruing bonuses EVENT_REASON_ACCOUNT_COMISSION, // Additional commissions EVENT_REASON_ACCOUNT_COMISSION_DAILY, // Commission charged at the end of a trading day EVENT_REASON_ACCOUNT_COMISSION_MONTHLY, // Commission charged at the end of a trading month EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY, // Agent commission charged at the end of a trading day EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY, // Agent commission charged at the end of a month EVENT_REASON_ACCOUNT_INTEREST, // Accruing interest on free funds EVENT_REASON_BUY_CANCELLED, // Canceled buy deal EVENT_REASON_SELL_CANCELLED, // Canceled sell deal EVENT_REASON_DIVIDENT, // Accruing dividends EVENT_REASON_DIVIDENT_FRANKED, // Accruing franked dividends EVENT_REASON_TAX // Tax }; #define REASON_EVENT_SHIFT (EVENT_REASON_ACCOUNT_CREDIT-3) //+------------------------------------------------------------------+
注文/ポジションのプロパティの変更内容を常に知りたい場合は、変更前の注文/ポジションのプロパティ価格(変更後の価格は既存のプロパティから取得します)とイベント中の現在の価格をイベントの整数プロパティに書き込むためのプロパティをイベントの整数プロパティに追加し、イベントの実数プロパティ数を10から15に変更し、検索と並べ替えの間に検索と並び替えで未使用のプロパティの数を追加します(変更イベントの間の変更データと価格は検索と並べ替えには使用されません)。
//+------------------------------------------------------------------+ //| Event's real properties | //+------------------------------------------------------------------+ enum ENUM_EVENT_PROP_DOUBLE { EVENT_PROP_PRICE_EVENT = EVENT_PROP_INTEGER_TOTAL, // Price an event occurred at EVENT_PROP_PRICE_OPEN, // Order/deal/position open price EVENT_PROP_PRICE_CLOSE, // Order/deal/position close price EVENT_PROP_PRICE_SL, // StopLoss order/deal/position price EVENT_PROP_PRICE_TP, // TakeProfit Order/deal/position EVENT_PROP_VOLUME_ORDER_INITIAL, // Requested order volume EVENT_PROP_VOLUME_ORDER_EXECUTED, // Executed order volume EVENT_PROP_VOLUME_ORDER_CURRENT, // Remaining order volume EVENT_PROP_VOLUME_POSITION_EXECUTED, // Current executed position volume after a deal EVENT_PROP_PROFIT, // Profit //--- EVENT_PROP_PRICE_OPEN_BEFORE, // Order price before modification EVENT_PROP_PRICE_SL_BEFORE, // StopLoss price before modification EVENT_PROP_PRICE_TP_BEFORE, // TakeProfit price before modification EVENT_PROP_PRICE_EVENT_ASK, // Ask price during an event EVENT_PROP_PRICE_EVENT_BID, // Bid price during an event }; #define EVENT_PROP_DOUBLE_TOTAL (15) // Total number of event's real properties #define EVENT_PROP_DOUBLE_SKIP (5) // Number of order properties not used in sorting //+------------------------------------------------------------------+
適切なマクロ置換の計算を変更して、イベントの並べ替え基準のリストで、イベントの最初の文字列プロパティのインデックスを正しく検索します。
//+------------------------------------------------------------------+ //| Possible event sorting criteria | //+------------------------------------------------------------------+ #define FIRST_EVN_DBL_PROP (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP) #define FIRST_EVN_STR_PROP (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP+EVENT_PROP_DOUBLE_TOTAL-EVENT_PROP_DOUBLE_SKIP) enum ENUM_SORT_EVENTS_MODE { //--- 整数型プロパティによって並び替える SORT_BY_EVENT_TYPE_EVENT = 0, // Sort by event type SORT_BY_EVENT_TIME_EVENT = 1, // Sort by event time SORT_BY_EVENT_STATUS_EVENT = 2, // Sort by event status (from the ENUM_EVENT_STATUS enumeration) SORT_BY_EVENT_REASON_EVENT = 3, // Sort by event reason (from the ENUM_EVENT_REASON enumeration) SORT_BY_EVENT_TYPE_DEAL_EVENT = 4, // Sort by deal event type SORT_BY_EVENT_TICKET_DEAL_EVENT = 5, // Sort by deal event ticket SORT_BY_EVENT_TYPE_ORDER_EVENT = 6, // Sort by type of an order, based on which a deal event is opened (the last position order) SORT_BY_EVENT_TICKET_ORDER_EVENT = 7, // Sort by type of an order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_TIME_ORDER_POSITION = 8, // Sort by time of an order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_TYPE_ORDER_POSITION = 9, // Sort by type of an order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_TICKET_ORDER_POSITION = 10, // Sort by a ticket of an order, based on which a position deal is opened (the first position order) SORT_BY_EVENT_POSITION_ID = 11, // Sort by position ID SORT_BY_EVENT_POSITION_BY_ID = 12, // Sort by opposite position ID SORT_BY_EVENT_MAGIC_ORDER = 13, // Sort by order/deal/position magic number SORT_BY_EVENT_MAGIC_BY_ID = 14, // Sort by opposite position magic number //--- 実数型プロパティによって並び替える SORT_BY_EVENT_PRICE_EVENT = FIRST_EVN_DBL_PROP, // Sort by a price an event occurred at SORT_BY_EVENT_PRICE_OPEN = FIRST_EVN_DBL_PROP+1, // Sort by position open price SORT_BY_EVENT_PRICE_CLOSE = FIRST_EVN_DBL_PROP+2, // Sort by position close price SORT_BY_EVENT_PRICE_SL = FIRST_EVN_DBL_PROP+3, // Sort by position's StopLoss price SORT_BY_EVENT_PRICE_TP = FIRST_EVN_DBL_PROP+4, // Sort by position's TakeProfit price SORT_BY_EVENT_VOLUME_ORDER_INITIAL = FIRST_EVN_DBL_PROP+5, // Sort by initial volume SORT_BY_EVENT_VOLUME_ORDER_EXECUTED = FIRST_EVN_DBL_PROP+6, // Sort by the current volume SORT_BY_EVENT_VOLUME_ORDER_CURRENT = FIRST_EVN_DBL_PROP+7, // Sort by remaining volume SORT_BY_EVENT_VOLUME_POSITION_EXECUTED = FIRST_EVN_DBL_PROP+8, // Sort by remaining volume SORT_BY_EVENT_PROFIT = FIRST_EVN_DBL_PROP+9, // Sort by profit //--- 文字列型プロパティによって並び替える SORT_BY_EVENT_SYMBOL = FIRST_EVN_STR_PROP, // Sort by order/position/deal symbol SORT_BY_EVENT_SYMBOL_BY_ID // Sort by an opposite position symbol }; //+------------------------------------------------------------------+
CEvent抽象イベントクラスを改善しましょう。
CEvent抽象イベントから派生したクラスで操作ログのデータを表示するので、イベントが発生した銘柄の相場の小数点以下の桁数(Digits())を知る必要があります。派生クラスのそれぞれで毎回受け取らないですむために、親クラスでそれを一度取得します。
クラスのprivateセクションで、イベント銘柄のDigits()値を格納するためのクラスメンバ変数を宣言して クラスコンストラクタの初期化リストでその変数を初期化します。
//+------------------------------------------------------------------+ //| Abstract event class | //+------------------------------------------------------------------+ class CEvent : public CObject { private: int m_event_code; // Event code //--- Return the index of the array the event's (1) double and (2) string properties are located at int IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; } protected: ENUM_TRADE_EVENT m_trade_event; // Trading event bool m_is_hedge; // Hedging account flag long m_chart_id; // Control program chart ID int m_digits; // Symbol Digits() int m_digits_acc; // Number of decimal places for the account currency long m_long_prop[EVENT_PROP_INTEGER_TOTAL]; // Event integer properties double m_double_prop[EVENT_PROP_DOUBLE_TOTAL]; // Event real properties string m_string_prop[EVENT_PROP_STRING_TOTAL]; // Event string properties //--- Return the flag presence in the trading event bool IsPresentEventFlag(const int event_code) const { return (this.m_event_code & event_code)==event_code; } //--- 保護されたパラメトリックコンストラクタ CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket); public: //--- デフォルトのコンストラクタ CEvent(void){;} //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code),m_digits(0) { this.m_long_prop[EVENT_PROP_STATUS_EVENT] = event_status; this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] = (long)ticket; this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS); this.m_chart_id=::ChartID(); } //+------------------------------------------------------------------+
整数および実数プロパティの説明を返すメソッドに、新しいイベントプロパティの説明を追加します。
//+------------------------------------------------------------------+ //| Return the description of the event's integer property | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property) { return ( property==EVENT_PROP_TYPE_EVENT ? TextByLanguage("Тип события","Event's type")+": "+this.TypeEventDescription() : property==EVENT_PROP_TIME_EVENT ? TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_STATUS_EVENT ? TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\"" : property==EVENT_PROP_REASON_EVENT ? TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription() : property==EVENT_PROP_TYPE_DEAL_EVENT ? TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_DEAL_EVENT ? TextByLanguage("Тикет сделки","Deal's ticket")+": #"+(string)this.GetProperty(property) : property==EVENT_PROP_TYPE_ORDER_EVENT ? TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORDER_POSITION ? TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORDER_POSITION ? TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+": #"+(string)this.GetProperty(property) : property==EVENT_PROP_TICKET_ORDER_EVENT ? TextByLanguage("Тикет ордера события","Event's order ticket")+": #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_ID ? TextByLanguage("Идентификатор позиции","Position ID")+": #"+(string)this.GetProperty(property) : property==EVENT_PROP_POSITION_BY_ID ? TextByLanguage("Идентификатор встречной позиции","Opposite position's ID")+": #"+(string)this.GetProperty(property) : property==EVENT_PROP_MAGIC_ORDER ? TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_MAGIC_BY_ID ? TextByLanguage("Магический номер встречной позиции","Magic number of opposite position")+": "+(string)this.GetProperty(property) : property==EVENT_PROP_TIME_ORDER_POSITION ? TextByLanguage("Время открытия позиции","Position's opened time")+": "+TimeMSCtoString(this.GetProperty(property)) : property==EVENT_PROP_TYPE_ORD_POS_BEFORE ? TextByLanguage("Тип ордера позиции до смены направления","Type order of position before changing direction")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORD_POS_BEFORE ? TextByLanguage("Тикет ордера позиции до смены направления","Ticket order of position before changing direction")+": #"+(string)this.GetProperty(property) : property==EVENT_PROP_TYPE_ORD_POS_CURRENT ? TextByLanguage("Тип ордера текущей позиции","Type order of current position")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) : property==EVENT_PROP_TICKET_ORD_POS_CURRENT ? TextByLanguage("Тикет ордера текущей позиции","Ticket order of current position")+": #"+(string)this.GetProperty(property) : EnumToString(property) ); } //+------------------------------------------------------------------+ //| Return the description of the event's real property | //+------------------------------------------------------------------+ string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property) { int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS); int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL)); return ( property==EVENT_PROP_PRICE_EVENT ? TextByLanguage("Цена на момент события","Price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_OPEN ? TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_CLOSE ? TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_SL ? TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_TP ? TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_VOLUME_ORDER_INITIAL ? TextByLanguage("Начальный объём ордера","Order initial volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_ORDER_EXECUTED ? TextByLanguage("Исполненный объём ордера","Order executed volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_ORDER_CURRENT ? TextByLanguage("Оставшийся объём ордера","Order remaining volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_VOLUME_POSITION_EXECUTED ? TextByLanguage("Текущий объём позиции","Position current volume")+": "+::DoubleToString(this.GetProperty(property),dgl) : property==EVENT_PROP_PROFIT ? TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc) : property==EVENT_PROP_PRICE_OPEN_BEFORE ? TextByLanguage("Цена открытия до модификации","Price open before modification")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_SL_BEFORE ? TextByLanguage("Цена StopLoss до модификации","StopLoss price before modification")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_TP_BEFORE ? TextByLanguage("Цена TakeProfit до модификации","TakeProfit price before modification")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_EVENT_ASK ? TextByLanguage("Цена Ask в момент события","Ask price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : property==EVENT_PROP_PRICE_EVENT_BID ? TextByLanguage("Цена Bid в момент события","Bid price at the time of event")+": "+::DoubleToString(this.GetProperty(property),dg) : EnumToString(property) ); } //+------------------------------------------------------------------+
イベントがないという説明、新しく追加されたイベントの説明、未知のイベントの説明を取引イベント名を返すメソッドに追加しましょう。
//+------------------------------------------------------------------+ //| Return the trading event name | //+------------------------------------------------------------------+ string CEvent::TypeEventDescription(void) const { ENUM_TRADE_EVENT event=this.TypeEvent(); return ( event==TRADE_EVENT_NO_EVENT ? TextByLanguage("Нет торгового события","No trading event") : event==TRADE_EVENT_PENDING_ORDER_PLASED ? TextByLanguage("Отложенный ордер установлен","Pending order placed") : event==TRADE_EVENT_PENDING_ORDER_REMOVED ? TextByLanguage("Отложенный ордер удалён","Pending order removed") : event==TRADE_EVENT_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : event==TRADE_EVENT_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : event==TRADE_EVENT_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : event==TRADE_EVENT_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : event==TRADE_EVENT_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : event==TRADE_EVENT_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : event==TRADE_EVENT_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : event==TRADE_EVENT_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : event==TRADE_EVENT_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : event==TRADE_EVENT_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : event==TRADE_EVENT_TAX ? TextByLanguage("Начисление налога","Tax charges") : event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL ? TextByLanguage("Пополнение средств на балансе","Balance refill") : event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawals") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED ? TextByLanguage("Отложенный ордер активирован ценой","Pending order activated") : event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL ? TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially") : event==TRADE_EVENT_POSITION_OPENED ? TextByLanguage("Позиция открыта","Position open") : event==TRADE_EVENT_POSITION_OPENED_PARTIAL ? TextByLanguage("Позиция открыта частично","Position open partially") : event==TRADE_EVENT_POSITION_CLOSED ? TextByLanguage("Позиция закрыта","Position closed") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL ? TextByLanguage("Позиция закрыта частично","Position closed partially") : event==TRADE_EVENT_POSITION_CLOSED_BY_POS ? TextByLanguage("Позиция закрыта встречной","Position closed by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS ? TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") : event==TRADE_EVENT_POSITION_CLOSED_BY_SL ? TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_BY_TP ? TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL ? TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss") : event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP ? TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit") : event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET ? TextByLanguage("Разворот позиции по рыночному запросу","Position reversal by market request") : event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING ? TextByLanguage("Разворот позиции срабатыванием отложенного ордера","Position reversal by triggering pending order") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET ? TextByLanguage("Добавлен объём к позиции по рыночному запросу","Added volume to position by market request") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by activation of pending order") : event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL ? TextByLanguage("Разворот позиции частичным исполнением запроса","Position reversal by partial completion of market request") : event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL ? TextByLanguage("Разворот позиции частичным срабатыванием отложенного ордера","Position reversal by partial activation of pending order") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ? TextByLanguage("Добавлен объём к позиции частичным исполнением запроса","Added volume to position by partial completion of market request") : event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by partial activation of pending order") : event==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER ? TextByLanguage("Сработал StopLimit-ордер","StopLimit order triggered.") : event==TRADE_EVENT_MODIFY_ORDER_PRICE ? TextByLanguage("Модифицирована цена установки ордера ","Order price modified") : event==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS ? TextByLanguage("Модифицированы цена установки и StopLoss ордера","Order price and StopLoss modified") : event==TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT ? TextByLanguage("Модифицированы цена установки и TakeProfit ордера","Order price and TakeProfit modified") : event==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT ? TextByLanguage("Модифицированы цена установки, StopLoss и TakeProfit ордера","Order price, StopLoss and TakeProfit modified") : event==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT ? TextByLanguage("Модифицированы цены StopLoss и TakeProfit ордера","Order StopLoss and TakeProfit modified") : event==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS ? TextByLanguage("Модифицирован StopLoss ордера","Order StopLoss modified") : event==TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT ? TextByLanguage("Модифицирован TakeProfit ордера","Order TakeProfit modified") : event==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT ? TextByLanguage("Модифицированы цены StopLoss и TakeProfit позиции","Position StopLoss and TakeProfit modified") : event==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS ? TextByLanguage("Модифицирован StopLoss позиции","Position StopLoss modified") : event==TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT ? TextByLanguage("Модифицирован TakeProfit позиции","Position TakeProfit modified") : EnumToString(event) ); } //+------------------------------------------------------------------+
イベントの理由の説明を返すメソッドに2つの新しい理由を追加します。
//+------------------------------------------------------------------+ //| Return the name of the deal/order/position reason | //+------------------------------------------------------------------+ string CEvent::ReasonDescription(void) const { ENUM_EVENT_REASON reason=this.Reason(); return ( reason==EVENT_REASON_ACTIVATED_PENDING ? TextByLanguage("Активирован отложенный ордер","Pending order activated") : reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY ? TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered") : reason==EVENT_REASON_STOPLIMIT_TRIGGERED ? TextByLanguage("Срабатывание StopLimit-ордера","StopLimit order triggered") : reason==EVENT_REASON_MODIFY ? TextByLanguage("Модификация","Modified") : reason==EVENT_REASON_CANCEL ? TextByLanguage("Отмена","Canceled") : reason==EVENT_REASON_EXPIRED ? TextByLanguage("Истёк срок действия","Expired") : reason==EVENT_REASON_DONE ? TextByLanguage("Рыночный запрос, выполненный в полном объёме","Fully completed market request") : reason==EVENT_REASON_DONE_PARTIALLY ? TextByLanguage("Выполненный частично рыночный запрос","Partially completed market request") : reason==EVENT_REASON_VOLUME_ADD ? TextByLanguage("Добавлен объём к позиции","Added volume to position") : reason==EVENT_REASON_VOLUME_ADD_PARTIALLY ? TextByLanguage("Добавлен объём к позиции частичным исполнением заявки","Volume added to the position by request partial completion") : reason==EVENT_REASON_VOLUME_ADD_BY_PENDING ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by activating pending order") : reason==EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY ? TextByLanguage("Добавлен объём к позиции частичной активацией отложенного ордера","Added volume to position by partial activation of pending order") : reason==EVENT_REASON_REVERSE ? TextByLanguage("Разворот позиции","Position reversal") : reason==EVENT_REASON_REVERSE_PARTIALLY ? TextByLanguage("Разворот позиции частичным исполнением заявки","Position reversal by partial completion of request") : reason==EVENT_REASON_REVERSE_BY_PENDING ? TextByLanguage("Разворот позиции при срабатывании отложенного ордера","Position reversal on triggered pending order") : reason==EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY ? TextByLanguage("Разворот позиции при при частичном срабатывании отложенного ордера","Position reversal on partially triggered pending order") : reason==EVENT_REASON_DONE_SL ? TextByLanguage("Закрытие по StopLoss","Close by StopLoss triggered") : reason==EVENT_REASON_DONE_SL_PARTIALLY ? TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered") : reason==EVENT_REASON_DONE_TP ? TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit triggered") : reason==EVENT_REASON_DONE_TP_PARTIALLY ? TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered") : reason==EVENT_REASON_DONE_BY_POS ? TextByLanguage("Закрытие встречной позицией","Closed by opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS ? TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position") : reason==EVENT_REASON_DONE_BY_POS_PARTIALLY ? TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") : reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY ? TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position") : reason==EVENT_REASON_BALANCE_REFILL ? TextByLanguage("Пополнение баланса","Balance refill") : reason==EVENT_REASON_BALANCE_WITHDRAWAL ? TextByLanguage("Снятие средств с баланса","Withdrawal from the balance") : reason==EVENT_REASON_ACCOUNT_CREDIT ? TextByLanguage("Начисление кредита","Credit") : reason==EVENT_REASON_ACCOUNT_CHARGE ? TextByLanguage("Дополнительные сборы","Additional charge") : reason==EVENT_REASON_ACCOUNT_CORRECTION ? TextByLanguage("Корректирующая запись","Correction") : reason==EVENT_REASON_ACCOUNT_BONUS ? TextByLanguage("Перечисление бонусов","Bonus") : reason==EVENT_REASON_ACCOUNT_COMISSION ? TextByLanguage("Дополнительные комиссии","Additional commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY ? TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY ? TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY ? TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission") : reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY ? TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission") : reason==EVENT_REASON_ACCOUNT_INTEREST ? TextByLanguage("Начисления процентов на свободные средства","Interest rate") : reason==EVENT_REASON_BUY_CANCELLED ? TextByLanguage("Отмененная сделка покупки","Canceled buy deal") : reason==EVENT_REASON_SELL_CANCELLED ? TextByLanguage("Отмененная сделка продажи","Canceled sell deal") : reason==EVENT_REASON_DIVIDENT ? TextByLanguage("Начисление дивиденда","Dividend operations") : reason==EVENT_REASON_DIVIDENT_FRANKED ? TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") : reason==EVENT_REASON_TAX ? TextByLanguage("Начисление налога","Tax charges") : EnumToString(reason) ); } //+------------------------------------------------------------------+
追加された新しいプロパティを返すメソッドをクラスのpublicセクションにあるイベントプロパティへの簡単なアクセスのセクションへのリンクに追加します。
//+------------------------------------------------------------------+ //| Methods of simplified access to event object properties | //+------------------------------------------------------------------+ //--- Return (1) event type, (2) event time in milliseconds, (3) event status, (4) event reason, (5) deal type, (6) deal ticket, //--- (7) order type, based on which a deal was executed, (8) position opening order type, (9) position last order ticket, //--- (10) position first order ticket, (11) position ID, (12) opposite position ID, (13) magic number, (14) opposite position magic number, (15) position open time ENUM_TRADE_EVENT TypeEvent(void) const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT); } long TimeEvent(void) const { return this.GetProperty(EVENT_PROP_TIME_EVENT); } ENUM_EVENT_STATUS Status(void) const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT); } ENUM_EVENT_REASON Reason(void) const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT); } ENUM_DEAL_TYPE TypeDeal(void) const { return (ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); } long TicketDeal(void) const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT); } ENUM_ORDER_TYPE TypeOrderEvent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT); } ENUM_ORDER_TYPE TypeFirstOrderPosition(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION); } long TicketOrderEvent(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT); } long TicketFirstOrderPosition(void) const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION); } long PositionID(void) const { return this.GetProperty(EVENT_PROP_POSITION_ID); } long PositionByID(void) const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID); } long Magic(void) const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER); } long MagicCloseBy(void) const { return this.GetProperty(EVENT_PROP_MAGIC_BY_ID); } long TimePosition(void) const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION); } //--- When changing position direction, return (1) previous position order type, (2) previous position order ticket //--- (3) current position order type, (4) current position order ticket //--- (5) position type and (6) ticket before changing direction, (7) position type and (8) ticket after changing direction ENUM_ORDER_TYPE TypeOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE); } long TicketOrderPosPrevious(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); } ENUM_ORDER_TYPE TypeOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT); } long TicketOrderPosCurrent(void) const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT);} ENUM_POSITION_TYPE TypePositionPrevious(void) const { return PositionTypeByOrderType(this.TypeOrderPosPrevious()); } ulong TicketPositionPrevious(void) const { return this.TicketOrderPosPrevious(); } ENUM_POSITION_TYPE TypePositionCurrent(void) const { return PositionTypeByOrderType(this.TypeOrderPosCurrent()); } ulong TicketPositionCurrent(void) const { return this.TicketOrderPosCurrent(); } //--- Return (1) the price the event occurred at, (2) open price, (3) close price, //--- (4) StopLoss price, (5) TakeProfit price, (6) profit, (7) requested order volume, //--- (8) executed order volume, (9) remaining order volume, (10) executed position volume double PriceEvent(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT); } double PriceOpen(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN); } double PriceClose(void) const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE); } double PriceStopLoss(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL); } double PriceTakeProfit(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP); } double Profit(void) const { return this.GetProperty(EVENT_PROP_PROFIT); } double VolumeOrderInitial(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL); } double VolumeOrderExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED); } double VolumeOrderCurrent(void) const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT); } double VolumePositionExecuted(void) const { return this.GetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED); } //--- When modifying prices, return (1) order price, (2) StopLoss and (3) TakeProfit before modification double PriceOpenBefore(void) const { return this.GetProperty(EVENT_PROP_PRICE_OPEN_BEFORE); } double PriceStopLossBefore(void) const { return this.GetProperty(EVENT_PROP_PRICE_SL_BEFORE); } double PriceTakeProfitBefore(void) const { return this.GetProperty(EVENT_PROP_PRICE_TP_BEFORE); } double PriceEventAsk(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT_ASK); } double PriceEventBid(void) const { return this.GetProperty(EVENT_PROP_PRICE_EVENT_BID); } //--- Return the (1) symbol and (2) opposite position symbol string Symbol(void) const { return this.GetProperty(EVENT_PROP_SYMBOL); } string SymbolCloseBy(void) const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID); } //+------------------------------------------------------------------+
ほとんどのクラスプロパティはCreateNewEvent()メソッドのイベントコレクションクラスで書き入れられ、CEventクラスのSetTypeEvent()メソッドを呼び出してイベントタイプを設定するので、CEventクラスのSetTypeEvent()メソッドでイベント銘柄のDigits()を設定して変更イベントを定義します。
//+------------------------------------------------------------------+ //| イベントコードをデコードして取引イベントを設定する | //+------------------------------------------------------------------+ void CEvent::SetTypeEvent(void) { //--- Set event symbol Digits() this.m_digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS); //--- Pending order placed (check for matching the event code since there can only be one flag here) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Pending order removed (check for matching the event code since there can only be one flag here) if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED) { this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Pending order is modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_MODIFY)) { //--- If the placement price is modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_PRICE)) { this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE; //--- If StopLoss and TakeProfit are modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT; //--- If StopLoss is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS; //--- If TakeProfit is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT; } //--- If the placement price is not modified else { //--- If StopLoss and TakeProfit are modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT; //--- If StopLoss is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_STOP_LOSS; //--- If TakeProfit is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If a position is modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_MODIFY)) { //--- If StopLoss and TakeProfit are modified if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL) && this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT; //--- If StopLoss is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_STOP_LOSS; //--- If TakeProfit is modified else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) this.m_trade_event=TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT; this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- Position opened (Check the presence of multiple flags in the event code) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED)) { //--- If an existing position is changed if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CHANGED)) { //--- If a pending order is activated by a price if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- If this is a position reversal if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE)) { //--- check the partial closure flag and set the //--- "position reversal by activation of a pending order" or "position reversal by partial activation of a pending order" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_REVERSED_BY_PENDING : TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If this is adding a volume to a position else { //--- check the partial opening flag and set the //--- "added volume to a position by activating a pending order" or "added volume to a position by partially activating a pending order" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING : TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- If a position was changed by a market deal else { //--- If this is a position reversal if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE)) { //--- check the partial opening flag and set the "position reversal" or "position reversal by partial execution" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_REVERSED_BY_MARKET : TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If this is adding a volume to a position else { //--- check the partial opening flag and set "added volume to a position" or "added volume to a position by partial execution" trading event this.m_trade_event= ( !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET : TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } } //--- If a new position is opened else { //--- If a pending order is activated by a price if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED)) { //--- check the partial opening flag and set "pending order activated" or "pending order partially activated" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ?TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- check the partial opening flag and set the "Position opened" or "Position partially opened" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ?TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Position closed (Check the presence of multiple flags in the event code) if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED)) { //--- if a position is closed by StopLoss if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL)) { //--- check the partial closing flag and set the "Position closed by StopLoss" or "Position partially closed by StopLoss" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ?TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- if a position is closed by TakeProfit else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP)) { //--- check the partial closure flag and set the "Position closed by TakeProfit" or "Position partially closed by TakeProfit" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ?TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- if a position is closed by an opposite one else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS)) { //--- check the partial closure flag and set the "Position closed by opposite one" or "Position partially closed by opposite one" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ?TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } //--- If a position is closed else { //--- check the partial closure flag and set the "Position closed" or "Position partially closed" trading event this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ?TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL); this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //--- Balance operation on the account (clarify the event by deal type) if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE) { //--- Initialize a trading event this.m_trade_event=TRADE_EVENT_NO_EVENT; //--- Take a deal type ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT); //--- if a deal is a balance operation if(deal_type==DEAL_TYPE_BALANCE) { //--- check the deal profit and set an event (funds deposit or withdrawal) this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ?TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL); } //--- The remaining balance operation types match the ENUM_DEAL_TYPE enumeration starting from DEAL_TYPE_CREDIT else if(deal_type>DEAL_TYPE_BALANCE) { //--- イベントを設定する this.m_trade_event=(ENUM_TRADE_EVENT)deal_type; } this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event); return; } } //+------------------------------------------------------------------+
メソッド内の必要なすべての確認とアクションはコメントされているので、ここでは説明を省略します。すべてがとてもシンプルでここで理解しやすいものだと思います。
これで抽象イベントクラスの改善は完了です。
もう少し先を見てみると、テストEAで未決注文を出すために価格変更の追跡を確認するとき、価格から最も遠い注文を見つけることが必要になったことに注意すべきです。注文のプロパティを調べてみると、ライブラリにはこれに対する迅速で用途の広い解決策がないことがわかりました。したがって、追加の整数型注文プロパティの1つであるポイント単位の利益を使用します。未決注文の場合、これはポイントからの価格からの注文の距離です。したがって、価格から最も遠い注文を見つけるには、単にポイントで「利益」(距離)が最も高い注文を探す必要があります。
この場合は、すべての未決注文をその方向で検索するのと似ています。価格から最も遠い未決注文を見つけるために、一方向のすべての注文を選択し、得られたリストを距離が大きい順に並び替えます。その結果、一方向(BuyLimit、BuyStopおよびBuyStopLimitはすべて買いです)の異なるタイプの注文のすべてから1つの注文を取得します。逆のことが売りにも当てはまります)。
Order.mqh抽象注文クラスのリストで、その方向によって注文のタイプを取得する方法を変更しましょう。
//+------------------------------------------------------------------+ //| ポイント単位の注文利益を返す | //+------------------------------------------------------------------+ int COrder::ProfitInPoints(void) const { MqlTick tick={0}; string symbol=this.Symbol(); if(!::SymbolInfoTick(symbol,tick)) return 0; ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)this.TypeOrder(); double point=::SymbolInfoDouble(symbol,SYMBOL_POINT); if(type==ORDER_TYPE_CLOSE_BY || point==0) return 0; if(this.Status()==ORDER_STATUS_HISTORY_ORDER) return int(type==ORDER_TYPE_BUY ? (this.PriceClose()-this.PriceOpen())/point : type==ORDER_TYPE_SELL ? (this.PriceOpen()-this.PriceClose())/point : 0); else if(this.Status()==ORDER_STATUS_MARKET_POSITION) { if(type==ORDER_TYPE_BUY) return int((tick.bid-this.PriceOpen())/point); else if(type==ORDER_TYPE_SELL) return int((this.PriceOpen()-tick.ask)/point); } else if(this.Status()==ORDER_STATUS_MARKET_PENDING) { if(type==ORDER_TYPE_BUY_LIMIT || type==ORDER_TYPE_BUY_STOP || type==ORDER_TYPE_BUY_STOP_LIMIT) return (int)fabs((tick.bid-this.PriceOpen())/point); else if(type==ORDER_TYPE_SELL_LIMIT || type==ORDER_TYPE_SELL_STOP || type==ORDER_TYPE_SELL_STOP_LIMIT) return (int)fabs((this.PriceOpen()-tick.ask)/point); } return 0; } //+------------------------------------------------------------------+
ここでは、本質的に未決注文の確認を追加し、注文価格から現在の価格までの距離をポイント単位で返します。
抽象注文クラスの整数プロパティを記述するメソッドに、価格から未決注文までの距離の表示を追加しましょう。
//+------------------------------------------------------------------+ //| 注文の整数型プロパティの説明を返す | //+------------------------------------------------------------------+ string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property) { return ( //--- 一般的なプロパティ property==ORDER_PROP_MAGIC ? TextByLanguage("Магик","Magic")+ (!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("Тикет родительского ордера","Parent order ticket")+ (!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_OPEN ? TextByLanguage("Время открытия","Time open")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==ORDER_PROP_TIME_CLOSE ? TextByLanguage("Время закрытия","Close time")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : property==ORDER_PROP_TIME_EXP ? TextByLanguage("Дата экспирации","Expiration date")+ (!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 ID")+ (!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 ID")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : property==ORDER_PROP_TIME_OPEN_MSC ? TextByLanguage("Время открытия в милисекундах","Open time in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" ) : property==ORDER_PROP_TIME_CLOSE_MSC ? TextByLanguage("Время закрытия в милисекундах","Close time in milliseconds")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")" ) : property==ORDER_PROP_TIME_UPDATE ? TextByLanguage("Время изменения позиции","Position change time")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(property)!=0 ?::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) : "0") ) : property==ORDER_PROP_TIME_UPDATE_MSC ? 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 ID")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+
ここで注文ステータスを確認し、これが既存の未決注文である場合は距離に関するメッセージを表示し、それ以外の場合は利益に関するメッセージを表示します。
これで抽象注文クラスの変更は完了です。
今度はCEvent抽象イベントクラスを継承する別のクラスを作成する必要があります。これは変更イベントクラスです。
第6部でネッティング勘定での作業をで実装した際に、ポジションを開くクラスのイベントを改善しました。CEventPositionOpenクラスは、イベントステータスといくつかのイベントオブジェクトプロパティの存在に応じて短いメッセージテキストを作成するメソッドを持つようになりました。
新しい変更イベントを作成するときも、変更イベントタイプを確認し、取得したタイプに応じてイベントテキストを作成します。また、コントロールプログラムのチャートにイベントを送信する際には、EventChartCustom()関数の
dparamパラメータで渡す価格を定義する必要があります。ポジション開始イベントクラスでは始値を渡すためにこのパラメータを使用しましたが、変更イベントクラスではいくつかの価格変更オプションが可能で、ユーザイベントのdparamパラメータで送信する価格を決める必要があります。
- 注文価格のみを変更できる — 新しい未決注文価格を送信します。
- 注文価格とStopLoss価格を変更できる — 新しい未決注文価格を送信します。
- 注文価格とTakeProfit価格を変更できる — 新しい未決注文価格を送信します。
- 注文価格、StopLoss価格、TakeProfit価格を変更できる — 新しい未決注文価格を送信します。
- StopLoss注文を変更できる — 新しいStopLoss価格を送信します。
- TakeProfit注文を変更できる — 新しいTakeProfit価格を送信します。
- StopLossポジションを変更できる — StopLossポジションを送信します。
- TakeProfitポジションを変更できる — TakeProfitポジションを送信します。
- StopLossポジションとTakeProfitポジションを変更できる — ポジションの始値を送信します。
ご覧のとおり、単一の価格を変更すると、変更された価格がイベントに渡されます。複数の価格を同時に変更する場合は、ポジション始値または注文価格のみを送信します(これもまた変更できます)。カスタムプログラムでは、発生した修正イベントの種類によって、それぞれの価格の変更を(同時に修正する間に)明確にすることができます。
ライブラリの\MQL5\Include\DoEasy\Objects\Eventsフォルダの新しいEventModify.mqhファイルで、新しいCEventModifyクラスを作成します。
CEvent抽象クラスを基本クラスにします。
抽象イベントクラスのファイルを変更クラスファイルにインクルードするのを忘れないでください。
クラスは比較的小さいので、研究のためにここでその全リストを提供します。第6部でCEventPositionOpenクラスの変更を実装する際に、すでに似たクラスを説明しました。
//+------------------------------------------------------------------+ //| EventModify.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| ファイルをインクルードする | //+------------------------------------------------------------------+ #include "Event.mqh" //+------------------------------------------------------------------+ //| Placing a pending order event | //+------------------------------------------------------------------+ class CEventModify : public CEvent { private: double m_price; // Price passed to an event //--- Create and return a brief event description string EventsMessage(void); public: //--- コンストラクタ CEventModify(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MODIFY,event_code,ticket),m_price(0) {} //--- サポートされる注文プロパティ: (1)実数、(2)整数 virtual bool SupportProperty(ENUM_EVENT_PROP_INTEGER property); virtual bool SupportProperty(ENUM_EVENT_PROP_DOUBLE property); //--- (1) Display a brief message about the event in the journal, (2) Send the event to the chart virtual void PrintShort(void); virtual void SendEvent(void); }; //+------------------------------------------------------------------+ //| Return 'true' if the event supports the passed | //| その他の場合は「false」を返す | //+------------------------------------------------------------------+ bool CEventModify::SupportProperty(ENUM_EVENT_PROP_INTEGER property) { if(property==EVENT_PROP_TYPE_DEAL_EVENT || property==EVENT_PROP_TICKET_DEAL_EVENT || property==EVENT_PROP_TYPE_ORDER_POSITION || property==EVENT_PROP_TICKET_ORDER_POSITION || property==EVENT_PROP_POSITION_ID || property==EVENT_PROP_POSITION_BY_ID || property==EVENT_PROP_TIME_ORDER_POSITION ) return false; return true; } //+------------------------------------------------------------------+ //| Return 'true' if the event supports the passed | //| その他の場合は「false」を返す | //+------------------------------------------------------------------+ bool CEventModify::SupportProperty(ENUM_EVENT_PROP_DOUBLE property) { if(property==EVENT_PROP_PRICE_CLOSE || property==EVENT_PROP_PROFIT ) return false; return true; } //+------------------------------------------------------------------+ //| Display a brief message about the event in the journal | //+------------------------------------------------------------------+ void CEventModify::PrintShort(void) { ::Print(this.EventsMessage()); } //+------------------------------------------------------------------+ //| Send the event to the chart | //+------------------------------------------------------------------+ void CEventModify::SendEvent(void) { this.PrintShort(); ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.TicketOrderEvent(),this.m_price,this.Symbol()); } //+------------------------------------------------------------------+ //| Create and return a brief event message | //+------------------------------------------------------------------+ string CEventModify::EventsMessage(void) { //--- (1) header, (2) magic number string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n"; string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : ""); string text=""; //--- Pending order price is modified if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+magic; this.m_price=this.PriceOpen(); } //--- Pending order price and StopLoss are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]"; string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+TextByLanguage(" и"," and")+" StopLoss: "+sl+magic; this.m_price=this.PriceOpen(); } //--- Pending order price and TakeProfit are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_TAKE_PROFIT) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]"; string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic; this.m_price=this.PriceOpen(); } //--- Pending order price, as well as its StopLoss and TakeProfit are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string price="["+::DoubleToString(this.PriceOpenBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceOpen(),this.m_digits)+"]"; string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирована цена: ",": modified price: ")+price+", StopLoss: "+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic; this.m_price=this.PriceOpen(); } //--- Pending order StopLoss is modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+magic; this.m_price=this.PriceStopLoss(); } //--- Pending order TakeProfit is modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован TakeProfit: ",": modified TakeProfit: ")+tp+magic; this.m_price=this.PriceTakeProfit(); } //--- Pending order StopLoss and TakeProfit are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_ORDER_STOP_LOSS_TAKE_PROFIT) { string order=OrderTypeDescription(this.TypeOrderPosCurrent())+" #"+(string)this.TicketOrderPosCurrent(); string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic; this.m_price=this.PriceOpen(); } //--- Position StopLoss is modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS) { string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent(); string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+magic; this.m_price=this.PriceStopLoss(); } //--- Position TakeProfit is modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_TAKE_PROFIT) { string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent(); string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован TakeProfit: ",": modified TakeProfit: ")+tp+magic; this.m_price=this.PriceTakeProfit(); } //--- Position StopLoss and TakeProfit are modified else if(this.TypeEvent()==TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT) { string order=PositionTypeDescription(this.TypePositionCurrent())+" #"+(string)this.TicketPositionCurrent(); string sl="["+::DoubleToString(this.PriceStopLossBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceStopLoss(),this.m_digits)+"]"; string tp="["+::DoubleToString(this.PriceTakeProfitBefore(),this.m_digits)+" --> "+::DoubleToString(this.PriceTakeProfit(),this.m_digits)+"]"; text=order+TextByLanguage(": модифицирован StopLoss: ",": modified StopLoss: ")+sl+TextByLanguage(" и"," and")+" TakeProfit: "+tp+magic; this.m_price=this.PriceOpen(); } return head+this.Symbol()+" "+text; } //+------------------------------------------------------------------+
今度は、既存の注文とポジションを変更するイベントを定義し、新しいイベントを作成し、それをイベントコレクションクラスのイベント収集リストに追加する必要があります。
ライブラリフォルダの\MQL5\Include\DoEasy\CollectionsフォルダのEventsCollection.mqhファイルのCEventsCollectionクラスを実装しましょう。
新しい変更イベントクラスのファイルをインクルードします。
クラスのprivateセクションで、クラスメンバ変数(ティックデータを保存する構造体)を宣言します。これは最後の変更イベント価格に関するデータを取得するために使用されます。
//+------------------------------------------------------------------+ //| EventsCollection.mqh | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| ファイルをインクルードする | //+------------------------------------------------------------------+ #include "ListObj.mqh" #include "..\Services\Select.mqh" #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\EventBalanceOperation.mqh" #include "..\Objects\Events\EventOrderPlaced.mqh" #include "..\Objects\Events\EventOrderRemoved.mqh" #include "..\Objects\Events\EventPositionOpen.mqh" #include "..\Objects\Events\EventPositionClose.mqh" #include "..\Objects\Events\EventModify.mqh" //+------------------------------------------------------------------+ //| Account event collection | //+------------------------------------------------------------------+ class CEventsCollection : public CListObj { private: CListObj m_list_events; // Event list bool m_is_hedge; // Hedging 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
クラスコンストラクでティック構造体を初期化します。
//+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT) { this.m_list_events.Clear(); this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT); this.m_list_events.Type(COLLECTION_EVENTS_ID); this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); this.m_chart_id=::ChartID(); ::ZeroMemory(this.m_tick); } //+------------------------------------------------------------------+
第7部では、新しいイベントを作成するためのオーバーロードメソッドを開発しましが、今は、口座の注文数とポジションを変更するときにイベントを作成するメソッドと、既存の注文またはポジションを変更するときに新しいイベントを作成するメソッドの2つがあります。
2番目のメソッドは、注文およびポジションの変更イベントを追跡できるように改善されるべきです
(第7部では、このメソッドはStopLimit注文の発動イベントのみを処理しました)。
注文/ポジション変更イベントを処理するコード文字列と変更前の注文/ポジションプロパティの保存を追加しましょう。
//+------------------------------------------------------------------+ //| Create a trading event depending on the order change type | //+------------------------------------------------------------------+ void CEventsCollection::CreateNewEvent(COrderControl* order) { if(!::SymbolInfoTick(order.Symbol(),this.m_tick)) { Print(DFUN,TextByLanguage("Не удалось получить текущие цены по символу события ","Failed to get current prices by event symbol "),order.Symbol()); return; } CEvent* event=NULL; //--- Pending StopLimit order activated if(order.GetChangeType()==CHANGE_TYPE_ORDER_TYPE) { this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED; event=new CEventOrderPlased(this.m_trade_event_code,order.Ticket()); } //--- Modification else { //--- Pending order price is modified if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE; //--- Pending order price and StopLoss are modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_STOP_LOSS) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_SL; //--- Pending order price and TakeProfit are modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_TP; //--- Pending order price, as well as its StopLoss and TakeProfit are modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_PRICE_STOP_LOSS_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_PRICE+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP; //--- Pending order StopLoss is modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_STOP_LOSS) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_SL; //--- Pending order TakeProfit is modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_TP; //--- Pending order StopLoss and TakeProfit are modified else if(order.GetChangeType()==CHANGE_TYPE_ORDER_STOP_LOSS_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_MODIFY+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP; //--- Position StopLoss is modified else if(order.GetChangeType()==CHANGE_TYPE_POSITION_STOP_LOSS) this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_SL; //--- Position TakeProfit is modified else if(order.GetChangeType()==CHANGE_TYPE_POSITION_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_TP; //--- Position StopLoss and TakeProfit are modified else if(order.GetChangeType()==CHANGE_TYPE_POSITION_STOP_LOSS_TAKE_PROFIT) this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_MODIFY+TRADE_EVENT_FLAG_SL+TRADE_EVENT_FLAG_TP; //--- Create a modification event event=new CEventModify(this.m_trade_event_code,order.Ticket()); } //--- Create an event if(event!=NULL) { event.SetProperty(EVENT_PROP_TIME_EVENT,order.Time()); // Event time event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_STOPLIMIT_TRIGGERED); // Event reason (from the ENUM_EVENT_REASON enumeration) event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,PositionTypeByOrderType((ENUM_ORDER_TYPE)order.TypeOrderPrev())); // Type of the order that triggered an event event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket()); // Ticket of the order that triggered an event event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder()); // Event order type event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket()); // Event order ticket event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder()); // First position order type event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket()); // First position order ticket event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID()); // Position ID event.SetProperty(EVENT_PROP_POSITION_BY_ID,0); // Opposite position ID event.SetProperty(EVENT_PROP_MAGIC_BY_ID,0); // Opposite position magic number event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrderPrev()); // Position order type before changing the direction event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket()); // Position order ticket before changing direction 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.PricePrev()); // Order price before modification event.SetProperty(EVENT_PROP_PRICE_SL_BEFORE,order.StopLossPrev()); // StopLoss price before modification event.SetProperty(EVENT_PROP_PRICE_TP_BEFORE,order.TakeProfitPrev()); // TakeProfit price 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 magic number event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimePrev()); // Position first order time event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PricePrev()); // Price the event occurred at event.SetProperty(EVENT_PROP_PRICE_OPEN,order.Price()); // Order placement price event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.Price()); // Order close price event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss()); // Order StopLoss price event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit()); // Order TakeProfit price event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume()); // Requested order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,0); // Executed order volume event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.Volume()); // Remaining (unexecuted) order volume event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0); // Executed position volume event.SetProperty(EVENT_PROP_PROFIT,0); // Profit event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol()); // Order symbol event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol()); // Opposite position symbol //--- Set the 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 already in the list.")); delete event; } } } //+------------------------------------------------------------------+
さまざまな変更タイプの処理条件は比較的簡単で、コードのコメントで説明されています。注文/ポジションの変更タイプに応じて、イベントコードは一連のフラグを使用して作成されます。新しい変更イベントを作成すると、コードがCEventModifyクラスコンストラクタに送信されます。
新しい注文/ポジションプロパティを保存するためのカラーマーク付きコードブロックは、クラスのポジション/注文プロパティを保存するためのすべてのメソッドに追加されます。コードは全く同じなのでここではお話しませんが、以下の添付ファイルでご覧になれます。
これで、既存の注文とポジションの変更イベントをテストする準備が整いました。
注文とポジションの変更順序のテスト
テストを実行するには、第7部から既存の一連のテスト用EAボタンを補足する必要があります。
押下ハンドラと一緒にStopLossの設定、
TakeProfitの設定、すべてのトレーリングの3つのボタンを追加しましょう。
最初の2つのボタンはストップロスとテイクプロフィットを持っていないすべての注文とポジションに設定します。3番目のボタンにはオン/オフの2つの状態があります。つまり押すとボタンは押されたままになって2つのトレーリング機能が作動します 。もう一度ボタンを押すと、両方のトレーリングが無効になります。もう一度ボタンを押すと、両方のトレーリングが無効になります。
TestDoEasyPart07.mq5 EAを\MQL5\Experts\TestDoEasy\Part07からコピーして \MQL5\Experts\TestDoEasy\Part08フォルダにTestDoEasyPart08.mq5として保存しましょう。
3つの新しい定数をボタン列挙に追加してボタンの総数をマクロ置き換えで17から20に変えます。
//--- 列挙体 enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_PROFIT_WITHDRAWAL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20)
価格からのストップロスレベルの距離、トレーリングステップ、トレーリングを開始するまでの利益ポイント数、ポイント単位でのストップロスとテイクプロフィットを指定するための変数(適切なボタンをクリックして設定)を追加します(InpStopLossとInpTakeProfitパラメータは、未決注文の発注直後のストップレベルの設定に使用されます)。
グローバル変数のリストに、
新しく追加された入力の値を格納するために必要な変数とトレーリング関数の動作を示すフラグ変数を追加します。
//--- 入力変数 input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 0; // Slippage in points input double InpWithdrawal = 10; // Withdrawal funds (in tester) input uint InpButtShiftX = 40; // Buttons X shift input uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) //--- グローバル変数 CEngine engine; CTrade trade; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ?0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; bool trailing_on; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; //+------------------------------------------------------------------+
これはテストEAなので、ライブラリのデバッグ時にプログラム操作が致命的なエラーで終了することがよくあります。このような場合、プロットされたすべてのグラフィカルオブジェクト(ボタン)はチャート上に残ります。エラーが修正され、EAが再起動された後、ボタンを再描画することはできません。次回の起動時にクリーンチャート上のすべてのボタンを再描画できるように、もう一度OnDeinit()ハンドラでチャートから既存のボタンを削除するには、再起動する必要があります。
チャート上のボタンの有無の確認をOnInit()ハンドラに追加します。トレーリング関数変数とストップレベルの値を設定します、トレーリングボタンのアクティビティフラグを確認し、フラグが設定されている場合はすべてのボタンをプロットした後でボタンを有効にします。
//+------------------------------------------------------------------+ //| エキスパート初期化関数 | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal, //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- check for undeleted objects if(IsPresentObects(prefix)) ObjectsDeleteAll(0,prefix); //--- グローバル変数を設定する prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; //--- ボタンを作成する if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- set button trailing ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- setting trade parameters trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
チャート上の指定された接頭辞を持つグラフィカルオブジェクトの存在を定義するための関数とボタンのステータスを追跡するための関数を書きましょう。コードを読みやすくするために、EAのOnTick()ハンドラから別の関数に追跡を移動します。
//+------------------------------------------------------------------+ //| Return the flag of a prefixed object presence | //+------------------------------------------------------------------+ bool IsPresentObects(const string object_prefix) { for(int i=ObjectsTotal(0)-1;i>=0;i--) if(StringFind(ObjectName(0,i,0),object_prefix)>WRONG_VALUE) return true; return false; } //+------------------------------------------------------------------+ //| Tracking the buttons' status | //+------------------------------------------------------------------+ void PressButtonsControl(void) { int total=ObjectsTotal(0); for(int i=0;i<total;i++) { string obj_name=ObjectName(0,i); if(StringFind(obj_name,prefix+"BUTT_")<0) continue; PressButtonEvents(obj_name); } } //+------------------------------------------------------------------+
ボタンオブジェクトの状態を設定する機能を変更しましょう。
//+------------------------------------------------------------------+ //| Set the button status | //+------------------------------------------------------------------+ void ButtonState(const string name,const bool state) { ObjectSetInteger(0,name,OBJPROP_STATE,state); if(name==butt_data[TOTAL_BUTT-1].name) { if(state) ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240'); else ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240'); } } //+------------------------------------------------------------------+
ここでは:
ボタンの状態を設定します (有効/無効)
これが最後のボタンで
「有効」な場合、ボタンオブジェクトの背景色を変更します
その他の場合、背景色を「無効」ステータスに返します。
新しいボタンが3つあるので、名前からボタンテキストを作成する関数に新しいボタンオブジェクトの名前からテキストへの返還を追加します。
//+------------------------------------------------------------------+ //| 列挙をボタンテキストに変換する | //+------------------------------------------------------------------+ string EnumToButtText(const ENUM_BUTTONS member) { string txt=StringSubstr(EnumToString(member),5); StringToLower(txt); StringReplace(txt,"set_take_profit","Set TakeProfit"); StringReplace(txt,"set_stop_loss","Set StopLoss"); StringReplace(txt,"trailing_all","Trailing All"); StringReplace(txt,"buy","Buy"); StringReplace(txt,"sell","Sell"); StringReplace(txt,"_limit"," Limit"); StringReplace(txt,"_stop"," Stop"); StringReplace(txt,"close_","Close "); StringReplace(txt,"2"," 1/2"); StringReplace(txt,"_by_"," by "); StringReplace(txt,"profit_","Profit "); StringReplace(txt,"delete_","Delete "); return txt; } //+------------------------------------------------------------------+
ここで、3つの新しいボタンの押下の処理が必要です。これを実現するには、 PressButtonEvents() ボタン押下処理関数の最後に次のコードを追加します(出勤ボタンの押下を処理するコードブロックの後)。
//--- BUTT_PROFIT_WITHDRAWALボタンが押下されたら、口座から出金する if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- プログラムがテスターで起動された場合 if(MQLInfoInteger(MQL_TESTER)) { //--- 資金出金のエミュレーション TesterWithdrawal(withdrawal); } } //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_STOP_LOSS)) { SetStopLoss(); } //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_TAKE_PROFIT)) { SetTakeProfit(); } //--- 0.1秒待つ Sleep(100); //--- "Unpress" the button (if this is not a trailing button) if(button!=EnumToString(BUTT_TRAILING_ALL)) ButtonState(button_name,false); //--- If the BUTT_TRAILING_ALL button is pressed else { //--- Set the color of the active button ButtonState(button_name,true); trailing_on=true; } //--- re-draw the chart ChartRedraw(); } //--- Return the inactive button color (if this is a trailing button) else if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; //--- re-draw the chart ChartRedraw(); } } //+------------------------------------------------------------------+
ご覧のように、SetStopLoss()とSetTakeProfit()の2つの新しい関数がここで呼び出され、適切な注文とポジションレベルを設定できます。
//+------------------------------------------------------------------+ //| Set StopLoss to all orders and positions | //+------------------------------------------------------------------+ void SetStopLoss(void) { if(stoploss_to_modify==0) return; //--- Set StopLoss to all positions where it is absent CArrayObj* list=engine.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify); trade.PositionModify(position.Ticket(),sl,position.TakeProfit()); } //--- Set StopLoss to all pending orders where it is absent list=engine.GetListMarketPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify); trade.OrderModify(order.Ticket(),order.PriceOpen(),sl,order.TakeProfit(),trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit()); } } //+------------------------------------------------------------------+ //| Set TakeProfit to all orders and positions | //+------------------------------------------------------------------+ void SetTakeProfit(void) { if(takeprofit_to_modify==0) return; //--- Set TakeProfit to all positions where it is absent CArrayObj* list=engine.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify); trade.PositionModify(position.Ticket(),position.StopLoss(),tp); } //--- Set TakeProfit to all pending orders where it is absent list=engine.GetListMarketPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify); trade.OrderModify(order.Ticket(),order.PriceOpen(),order.StopLoss(),tp,trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit()); } } //+------------------------------------------------------------------+
関数は非常に簡単です。テイクプロフィットをそれが存在しないすべての注文とポジションに配置してみましょう。
まず、設定するポイント単位のストップロスを確認します。値が0の場合は、変更できるものがないためすぐに終了します。
次に、アクティブなマーケットポジションのみのリストを受け取り、ポジションにテイクプロフィットがないためテイクプロフィットがゼロなもので並び替えます。
次に、最後のリストを反復処理してポジションを取得し、第4部で説明したサービス関数を使用してそれぞれに対して正しいテイクプロフィットを計算し、標準ライブラリのCTradeクラスのポジション変更メソッドに送信します。
テイクプロフィットを注文に設定するために、アクティブな未決注文のリストを取得して上記のアクションを実行します。
これで、最後のポジションストップと発注価格のための関数を書くだけです。
//+------------------------------------------------------------------+ //| Trailing stop of a position with the maximum profit | //+------------------------------------------------------------------+ void TrailingPositions(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- すべてのポジションのリストを取得する CArrayObj* list=engine.GetListMarketPosition(); //--- リストから買いポジションのみを選択する CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ買いポジションのインデックスを取得する int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- Calculate the new StopLoss double sl=NormalizeDouble(tick.bid-trailing_stop,Digits()); //--- If the price and the StopLevel based on it are higher than the new StopLoss (the distance by StopLevel is maintained) if(tick.bid-stop_level>sl) { //--- If the new StopLoss level exceeds the trailing step based on the current StopLoss if(buy.StopLoss()+trailing_step<sl) { //--- If we trail at any profit or position profit in points exceeds the trailing start, modify StopLoss if(trailing_start==0 || buy.ProfitInPoints()>(int)trailing_start) trade.PositionModify(buy.Ticket(),sl,buy.TakeProfit()); } } } } //--- リストから売りポジションのみを選択する CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get Sell position index with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- Calculate the new StopLoss double sl=NormalizeDouble(tick.ask+trailing_stop,Digits()); //--- If the price and StopLevel based on it are below the new StopLoss (the distance by StopLevel is maintained) if(tick.ask+stop_level<sl) { //--- If the new StopLoss level is below the trailing step based on the current StopLoss or a position has no StopLoss if(sell.StopLoss()-trailing_step>sl || sell.StopLoss()==0) { //--- If we trail at any profit or position profit in points exceeds the trailing start value, modify StopLoss if(trailing_start==0 || sell.ProfitInPoints()>(int)trailing_start) trade.PositionModify(sell.Ticket(),sl,sell.TakeProfit()); } } } } } //+------------------------------------------------------------------+ //| Trailing the farthest pending orders | //+------------------------------------------------------------------+ void TrailingOrders(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- Get the list of all placed orders CArrayObj* list=engine.GetListMarketPendings(); //--- Select only Buy orders from the list CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL); //--- Sort the list by distance from the price in points (by profit in points) list_buy.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the Buy order with the greatest distance int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT); if(index_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- If the order is below the price (BuyLimit) and it should be "elevated" following the price if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT) { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.ask-trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price is below the StopLevel distance based on Ask order price (the distance by StopLevel is maintained) if(price<tick.ask-stop_level) { //--- If the calculated price exceeds the trailing step based on the order placement price, modify the order price if(price>buy.PriceOpen()+trailing_step) { trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),buy.PriceStopLimit()); } } } //--- If the order exceeds the price (BuyStop and BuyStopLimit), and it should be "decreased" following the price else { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.ask+trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price exceeds the StopLevel based on Ask order price (the distance by StopLevel is maintained) if(price>tick.ask+stop_level) { //--- If the calculated price is lower than the trailing step based on order price, modify the order price if(price<buy.PriceOpen()-trailing_step) { trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(buy.PriceStopLimit()>0 ? price-distance_stoplimit*Point() : 0)); } } } } } //--- Select only Sell order from the list CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL); //--- Sort the list by the distance from the price in points (by profit in points) list_sell.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the Sell order having the greatest distance int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- If the order exceeds the price (SellLimit), and it needs to be "decreased" following the price if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT) { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.bid+trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price exceeds the StopLevel distance based on the Bid order price (the distance by StopLevel is maintained) if(price>tick.bid+stop_level) { //--- If the calculated price is below the trailing step based on the order price, modify the order price if(price<sell.PriceOpen()-trailing_step) { trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),sell.PriceStopLimit()); } } } //--- If the order is below the price (SellStop and SellStopLimit), and it should be "elevated" following the price else { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.bid-trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price is below the StopLevel distance based on the Bid order price (the distance by StopLevel is maintained) if(price<tick.bid-stop_level) { //--- If the calculated price exceeds the trailing step based on the order price, modify the order price if(price>sell.PriceOpen()+trailing_step) { trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(sell.PriceStopLimit()>0 ? price+distance_stoplimit*Point() : 0)); } } } } } } //+------------------------------------------------------------------+
関数には何も新しいものが含まれていません。必要なアクションはすべてコードのコメントで直接記述されています。それほど難なく自分でコードを研究することができるはずです。
ボタンが3つ増えたので、ボタンパネルの作成関数でボタンの座標の計算が調整されました(最終的なコードを参照)。
OnTick()ハンドラですべてのトレーリング関数を呼びます。
//+------------------------------------------------------------------+ //| エキスパートティック関数 | //+------------------------------------------------------------------+ void OnTick() { //--- Initialize the last trading event static ENUM_TRADE_EVENT last_event=WRONG_VALUE; //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); PressButtonsControl(); } //--- If the last trading event changed if(engine.LastTradeEvent()!=last_event) { last_event=engine.LastTradeEvent(); } //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); TrailingOrders(); } } //+------------------------------------------------------------------+
以下はテストEAのコード全部です。
//+------------------------------------------------------------------+ //| TestDoEasyPart08.mq5 | //| Copyright 2018, MetaQuotes Software Corp. | //| https://mql5.com/ja/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2018, MetaQuotes Software Corp." #property link "https://mql5.com/ja/users/artmedia70" #property version "1.00" //--- include #include <DoEasy\Engine.mqh> #include <Trade\Trade.mqh> //--- 列挙体 enum ENUM_BUTTONS { BUTT_BUY, BUTT_BUY_LIMIT, BUTT_BUY_STOP, BUTT_BUY_STOP_LIMIT, BUTT_CLOSE_BUY, BUTT_CLOSE_BUY2, BUTT_CLOSE_BUY_BY_SELL, BUTT_SELL, BUTT_SELL_LIMIT, BUTT_SELL_STOP, BUTT_SELL_STOP_LIMIT, BUTT_CLOSE_SELL, BUTT_CLOSE_SELL2, BUTT_CLOSE_SELL_BY_BUY, BUTT_DELETE_PENDING, BUTT_CLOSE_ALL, BUTT_PROFIT_WITHDRAWAL, BUTT_SET_STOP_LOSS, BUTT_SET_TAKE_PROFIT, BUTT_TRAILING_ALL }; #define TOTAL_BUTT (20) //--- 構造体 struct SDataButt { string name; string text; }; //--- 入力変数 input ulong InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 50; // StopLoss in points input uint InpTakeProfit = 50; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpSlippage = 0; // Slippage in points input double InpWithdrawal = 10; // Withdrawal funds (in tester) input uint InpButtShiftX = 40; // Buttons X shift input uint InpButtShiftY = 10; // Buttons Y shift input uint InpTrailingStop = 50; // Trailing Stop (points) input uint InpTrailingStep = 20; // Trailing Step (points) input uint InpTrailingStart = 0; // Trailing Start (points) input uint InpStopLossModify = 20; // StopLoss for modification (points) input uint InpTakeProfitModify = 60; // TakeProfit for modification (points) //--- グローバル変数 CEngine engine; CTrade trade; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ?0.1 : InpWithdrawal); ulong magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint slippage; bool trailing_on; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; //+------------------------------------------------------------------+ //| エキスパート初期化関数 | //+------------------------------------------------------------------+ int OnInit() { //--- Calling the function displays the list of enumeration constants in the journal, //--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity //EnumNumbersTest(); //--- check for undeleted objects if(IsPresentObects(prefix)) ObjectsDeleteAll(0,prefix); //--- グローバル変数を設定する prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; for(int i=0;i<TOTAL_BUTT;i++) { butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i); butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i); } lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0)); magic_number=InpMagic; stoploss=InpStopLoss; takeprofit=InpTakeProfit; distance_pending=InpDistance; distance_stoplimit=InpDistanceSL; slippage=InpSlippage; trailing_stop=InpTrailingStop*Point(); trailing_step=InpTrailingStep*Point(); trailing_start=InpTrailingStart; stoploss_to_modify=InpStopLossModify; takeprofit_to_modify=InpTakeProfitModify; //--- ボタンを作成する if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- set button trailing ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- setting trade parameters trade.SetDeviationInPoints(slippage); trade.SetExpertMagicNumber(magic_number); trade.SetTypeFillingBySymbol(Symbol()); trade.SetMarginMode(); trade.LogLevel(LOG_LEVEL_NO); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| エキスパート初期化解除関数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- オブジェクトを削除する ObjectsDeleteAll(0,prefix); Comment(""); } //+------------------------------------------------------------------+ //| エキスパートティック関数 | //+------------------------------------------------------------------+ void OnTick() { //--- Initialize the last trading event static ENUM_TRADE_EVENT last_event=WRONG_VALUE; //--- If working in the tester if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(); PressButtonsControl(); } //--- If the last trading event changed if(engine.LastTradeEvent()!=last_event) { last_event=engine.LastTradeEvent(); } //--- If the trailing flag is set if(trailing_on) { TrailingPositions(); TrailingOrders(); } } //+------------------------------------------------------------------+ //| タイマー関数 | //+------------------------------------------------------------------+ void OnTimer() { if(!MQLInfoInteger(MQL_TESTER)) engine.OnTimer(); } //+------------------------------------------------------------------+ //| ChartEvent関数 | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(MQLInfoInteger(MQL_TESTER)) return; if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0) { PressButtonEvents(sparam); } if(id>=CHARTEVENT_CUSTOM) { ushort event=ushort(id-CHARTEVENT_CUSTOM); Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam); } } //+------------------------------------------------------------------+ //| Return the flag of a prefixed object presence | //+------------------------------------------------------------------+ bool IsPresentObects(const string object_prefix) { for(int i=ObjectsTotal(0)-1;i>=0;i--) if(StringFind(ObjectName(0,i,0),object_prefix)>WRONG_VALUE) return true; return false; } //+------------------------------------------------------------------+ //| Tracking the buttons' status | //+------------------------------------------------------------------+ void PressButtonsControl(void) { int total=ObjectsTotal(0); for(int i=0;i<total;i++) { string obj_name=ObjectName(0,i); if(StringFind(obj_name,prefix+"BUTT_")<0) continue; PressButtonEvents(obj_name); } } //+------------------------------------------------------------------+ //| Create the button panel | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=30,const int shift_y=0) { int h=18,w=84,offset=2; int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1; int x=cx,y=cy; int shift=0; for(int i=0;i<TOTAL_BUTT;i++) { x=x+(i==7 ?w+2 : 0); if(i==TOTAL_BUTT-6) x=cx; y=(cy-(i-(i>6 ?7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-6 ?w : w*2+2),h,butt_data[i].text,(i<4 ?clrGreen : i>6 && i<11 ?clrRed : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+ //| ボタンを作成する | //+------------------------------------------------------------------+ bool ButtonCreate(const string name,const int x,const int y,const int w,const int h,const string text,const color clr,const string font="Calibri",const int font_size=8) { if(ObjectFind(0,name)<0) { if(!ObjectCreate(0,name,OBJ_BUTTON,0,0,0)) { Print(DFUN,TextByLanguage("не удалось создать кнопку!Код ошибки=","Could not create button!Error code="),GetLastError()); return false; } ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_HIDDEN,true); ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,name,OBJPROP_XSIZE,w); ObjectSetInteger(0,name,OBJPROP_YSIZE,h); ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER); ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size); ObjectSetString(0,name,OBJPROP_FONT,font); ObjectSetString(0,name,OBJPROP_TEXT,text); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); ObjectSetString(0,name,OBJPROP_TOOLTIP,"\n"); ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,clrGray); return true; } return false; } //+------------------------------------------------------------------+ //| ボタンの状態を返す | //+------------------------------------------------------------------+ bool ButtonState(const string name) { return (bool)ObjectGetInteger(0,name,OBJPROP_STATE); } //+------------------------------------------------------------------+ //| ボタンの状態を設定する | //+------------------------------------------------------------------+ void ButtonState(const string name,const bool state) { ObjectSetInteger(0,name,OBJPROP_STATE,state); if(name==butt_data[TOTAL_BUTT-1].name) { if(state) ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'220,255,240'); else ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240'); } } //+------------------------------------------------------------------+ //| 列挙をボタンテキストに変換する | //+------------------------------------------------------------------+ string EnumToButtText(const ENUM_BUTTONS member) { string txt=StringSubstr(EnumToString(member),5); StringToLower(txt); StringReplace(txt,"set_take_profit","Set TakeProfit"); StringReplace(txt,"set_stop_loss","Set StopLoss"); StringReplace(txt,"trailing_all","Trailing All"); StringReplace(txt,"buy","Buy"); StringReplace(txt,"sell","Sell"); StringReplace(txt,"_limit"," Limit"); StringReplace(txt,"_stop"," Stop"); StringReplace(txt,"close_","Close "); StringReplace(txt,"2"," 1/2"); StringReplace(txt,"_by_"," by "); StringReplace(txt,"profit_","Profit "); StringReplace(txt,"delete_","Delete "); return txt; } //+------------------------------------------------------------------+ //| ボタン押下を処理する | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { //--- ボタン名を文字列IDに変換する string button=StringSubstr(button_name,StringLen(prefix)); //--- ボタンが押下された場合 if(ButtonState(button_name)) { //--- BUTT_BUYボタンが押下されたら、買いポジションを開く if(button==EnumToString(BUTT_BUY)) { //--- StopLevelに相対した正しいストップロスとテイクプロフィット価格を取得する double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit); //--- 買いポジションを開く trade.Buy(NormalizeLot(Symbol(),lot),Symbol(),0,sl,tp); } //--- BUTT_BUY_LIMITボタンが押下されたら、BuyLimit注文を出す else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- StopLevelに相対した正しい注文配置を取得する double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending); //--- StopLevelを考慮して、発注レベルに対する正しいストップロスとテイクプロフィットの価格を取得する double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit); //--- BuyLimit注文を設定する trade.BuyLimit(lot,price_set,Symbol(),sl,tp); } //--- BUTT_BUY_STOPボタンが押下されたら、BuyStopを設定する else if(button==EnumToString(BUTT_BUY_STOP)) { //--- StopLevelに相対した正しい注文配置を取得する double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- StopLevelを考慮して、発注レベルに対する正しいストップロスとテイクプロフィットの価格を取得する double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit); //--- BuyStop注文を設定する trade.BuyStop(lot,price_set,Symbol(),sl,tp); } //--- BUTT_BUY_STOP_LIMITボタンが押下されたら、BuyStopLimitを設定する else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- StopLevelに相対した正しいBuyStop注文配置を取得する double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending); //--- StopLevelを考慮して、BuyStopレベルに相対したBuyLimit注文価格を計算する double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop); //--- StopLevelを考慮して、発注レベルに対する正しいストップロスとテイクプロフィットの価格を取得する double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit); //--- BuyStopLimit注文を設定する trade.OrderOpen(Symbol(),ORDER_TYPE_BUY_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp); } //--- BUTT_SELLボタンが押下されたら、売りポジションを開く else if(button==EnumToString(BUTT_SELL)) { //--- StopLevelに相対した正しいストップロスとテイクプロフィット価格を取得する double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit); //--- 売りポジションを開く trade.Sell(lot,Symbol(),0,sl,tp); } //--- BUTT_SELL_LIMITボタンが押下されたら、SellLimitを設定する else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- StopLevelに相対した正しい注文配置を取得する double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending); //--- StopLevelを考慮して、発注レベルに対する正しいストップロスとテイクプロフィットの価格を取得する double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit); //--- SellLimit注文を設定する trade.SellLimit(lot,price_set,Symbol(),sl,tp); } //--- BUTT_SELL_STOPボタンが押下されたら、SellStopを設定する else if(button==EnumToString(BUTT_SELL_STOP)) { //--- StopLevelに相対した正しい注文配置を取得する double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- StopLevelを考慮して、発注レベルに対する正しいストップロスとテイクプロフィットの価格を取得する double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit); //--- SellStop注文を設定する trade.SellStop(lot,price_set,Symbol(),sl,tp); } //--- BUTT_SELL_STOP_LIMITボタンが押下されたら、SellStopLimitを設定する else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- StopLevelに相対した正しいSellStop注文価格を取得する double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending); //--- StopLevelを考慮して、SellStopレベルに相対したSellLimit注文価格を計算する double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop); //--- StopLevelを考慮して、発注レベルに対する正しいストップロスとテイクプロフィットの価格を取得する double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss); double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit); //--- SellStopLimit注文を設定する trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp); } //--- BUTT_CLOSE_BUYボタンが押下されたら、最大利益を持つ買いポジションを決済する else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- すべてのポジションのリストを取得する CArrayObj* list=engine.GetListMarketPosition(); //--- リストから買いポジションのみを選択する list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ買いポジションのインデックスを取得する int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- 買いポジションチケットを取得し、チケットでポジションを決済する trade.PositionClose(position.Ticket()); } } } //--- BUTT_CLOSE_BUY2ボタンが押下されたら、最大利益を持つ買いポジションを半分決済する else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- すべてのポジションのリストを取得する CArrayObj* list=engine.GetListMarketPosition(); //--- リストから買いポジションのみを選択する list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ買いポジションのインデックスを取得する int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- 決済の出来高を計算し、チケットで買いポジションの半分を決済する if(engine.IsHedge()) trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); else trade.Sell(NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } } //--- BUTT_CLOSE_BUY_BY_SELLボタンが押下されたら、最大利益を持つ買いポジションを反対方向の売りで決済する else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- すべてのポジションのリストを取得する CArrayObj* list_buy=engine.GetListMarketPosition(); //--- リストから買いポジションのみを選択する list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ買いポジションのインデックスを取得する int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- すべてのポジションのリストを取得する CArrayObj* list_sell=engine.GetListMarketPosition(); //--- リストから売りポジションのみを選択する list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ売りポジションのインデックスを取得する int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- 最大の利益を持つ買いポジションを選択する COrder* position_buy=list_buy.At(index_buy); //--- 最大の利益を持つ売りポジションを選択する COrder* position_sell=list_sell.At(index_sell); if(position_buy!=NULL && position_sell!=NULL) { //--- 買いポジションを反対の売りポジションによって決済する trade.PositionCloseBy(position_buy.Ticket(),position_sell.Ticket()); } } } //--- BUTT_CLOSE_SELLボタンが押下されたら、最大利益を持つ売りポジションを決済する else if(button==EnumToString(BUTT_CLOSE_SELL)) { //--- すべてのポジションのリストを取得する CArrayObj* list=engine.GetListMarketPosition(); //--- リストから売りポジションのみを選択する list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ売りポジションのインデックスを取得する int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- 売りポジションチケットを取得し、チケットでポジションを決済する trade.PositionClose(position.Ticket()); } } } //--- BUTT_CLOSE_SELL2ボタンが押下されたら、最大利益を持つ売りポジションを半分決済する else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- すべてのポジションのリストを取得する CArrayObj* list=engine.GetListMarketPosition(); //--- リストから売りポジションのみを選択する list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ売りポジションのインデックスを取得する int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); if(position!=NULL) { //--- 決済の出来高を計算し、チケットで売りポジションの半分を決済する if(engine.IsHedge()) trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0)); else trade.Buy(NormalizeLot(position.Symbol(),position.Volume()/2.0)); } } } //--- BUTT_CLOSE_SELL_BY_BUYボタンが押下されたら、最大利益を持つ売りポジションを反対方向の買いで決済する else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- すべてのポジションのリストを取得する CArrayObj* list_sell=engine.GetListMarketPosition(); //--- リストから売りポジションのみを選択する list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ売りポジションのインデックスを取得する int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- すべてのポジションのリストを取得する CArrayObj* list_buy=engine.GetListMarketPosition(); //--- リストから買いポジションのみを選択する list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ買いポジションのインデックスを取得する int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- 最大の利益を持つ売りポジションを選択する COrder* position_sell=list_sell.At(index_sell); //--- 最大の利益を持つ買いポジションを選択する COrder* position_buy=list_buy.At(index_buy); if(position_sell!=NULL && position_buy!=NULL) { //--- 売りポジションを反対の買いポジションによって決済する trade.PositionCloseBy(position_sell.Ticket(),position_buy.Ticket()); } } } //--- BUTT_CLOSE_ALLボタンが押下されたら、最小利益を持つポジションから初めて、すべてのポジションを決済する else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- すべてのポジションのリストを取得する CArrayObj* list=engine.GetListMarketPosition(); if(list!=NULL) { //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- 最小の利益を持つポジションからの反復処理 for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- 個々のポジションをチケットで決済する trade.PositionClose(position.Ticket()); } } } //--- 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_MSC); 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 trade.OrderDelete(order.Ticket()); } } } //--- BUTT_PROFIT_WITHDRAWALボタンが押下されたら、口座から出金する if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- プログラムがテスターで起動された場合 if(MQLInfoInteger(MQL_TESTER)) { //--- 資金出金のエミュレーション TesterWithdrawal(withdrawal); } } //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_STOP_LOSS)) { SetStopLoss(); } //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present if(button==EnumToString(BUTT_SET_TAKE_PROFIT)) { SetTakeProfit(); } //--- 0.1秒待つ Sleep(100); //--- "Unpress" the button (if this is not a trailing button) if(button!=EnumToString(BUTT_TRAILING_ALL)) ButtonState(button_name,false); //--- If the BUTT_TRAILING_ALL button is pressed else { //--- Set the color of the active button ButtonState(button_name,true); trailing_on=true; } //--- re-draw the chart ChartRedraw(); } //--- Return the inactive button color (if this is a trailing button) else if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; //--- re-draw the chart ChartRedraw(); } } //+------------------------------------------------------------------+ //| Set StopLoss to all orders and positions | //+------------------------------------------------------------------+ void SetStopLoss(void) { if(stoploss_to_modify==0) return; //--- Set StopLoss to all positions where it is absent CArrayObj* list=engine.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify); trade.PositionModify(position.Ticket(),sl,position.TakeProfit()); } //--- Set StopLoss to all pending orders where it is absent list=engine.GetListMarketPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify); trade.OrderModify(order.Ticket(),order.PriceOpen(),sl,order.TakeProfit(),trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit()); } } //+------------------------------------------------------------------+ //| Set TakeProfit to all orders and positions | //+------------------------------------------------------------------+ void SetTakeProfit(void) { if(takeprofit_to_modify==0) return; //--- Set TakeProfit to all positions where it is absent CArrayObj* list=engine.GetListMarketPosition(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; int total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* position=list.At(i); if(position==NULL) continue; double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify); trade.PositionModify(position.Ticket(),position.StopLoss(),tp); } //--- Set TakeProfit to all pending orders where it is absent list=engine.GetListMarketPendings(); list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL); if(list==NULL) return; total=list.Total(); for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify); trade.OrderModify(order.Ticket(),order.PriceOpen(),order.StopLoss(),tp,trade.RequestTypeTime(),trade.RequestExpiration(),order.PriceStopLimit()); } } //+------------------------------------------------------------------+ //| Trailing stop of a position with the maximum profit | //+------------------------------------------------------------------+ void TrailingPositions(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- すべてのポジションのリストを取得する CArrayObj* list=engine.GetListMarketPosition(); //--- リストから買いポジションのみを選択する CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- 最大の利益を持つ買いポジションのインデックスを取得する int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- Calculate the new StopLoss double sl=NormalizeDouble(tick.bid-trailing_stop,Digits()); //--- If the price and the StopLevel based on it are higher than the new StopLoss (the distance by StopLevel is maintained) if(tick.bid-stop_level>sl) { //--- If the new StopLoss level exceeds the trailing step based on the current StopLoss if(buy.StopLoss()+trailing_step<sl) { //--- If we trail at any profit or position profit in points exceeds the trailing start, modify StopLoss if(trailing_start==0 || buy.ProfitInPoints()>(int)trailing_start) trade.PositionModify(buy.Ticket(),sl,buy.TakeProfit()); } } } } //--- リストから売りポジションのみを選択する CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- 手数料とスワップを考慮して、リストを利益順に並べ替える list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get Sell position index with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- Calculate the new StopLoss double sl=NormalizeDouble(tick.ask+trailing_stop,Digits()); //--- If the price and StopLevel based on it are below the new (the distance by StopLevel is maintained) if(tick.ask+stop_level<sl) { //--- If the new StopLoss level is below the trailing step based on the current StopLoss or a position has no StopLoss if(sell.StopLoss()-trailing_step>sl || sell.StopLoss()==0) { //--- If we trail at any profit or position profit in points exceeds the trailing start value, modify StopLoss if(trailing_start==0 || sell.ProfitInPoints()>(int)trailing_start) trade.PositionModify(sell.Ticket(),sl,sell.TakeProfit()); } } } } } //+------------------------------------------------------------------+ //| Trailing the farthest pending orders | //+------------------------------------------------------------------+ void TrailingOrders(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- Get the list of all placed orders CArrayObj* list=engine.GetListMarketPendings(); //--- Select only Buy orders from the list CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL); //--- Sort the list by distance from the price in points (by profit in points) list_buy.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the Buy order with the greatest distance int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT); if(index_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- If the order is below the price (BuyLimit) and it should be "elevated" following the price if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT) { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.ask-trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price is below the StopLevel distance based on Ask order price (the distance by StopLevel is maintained) if(price<tick.ask-stop_level) { //--- If the calculated price exceeds the trailing step based on the order placement price, modify the order price if(price>buy.PriceOpen()+trailing_step) { trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),buy.PriceStopLimit()); } } } //--- If the order exceeds the price (BuyStop and BuyStopLimit), and it should be "decreased" following the price else { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.ask+trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price exceeds the StopLevel based on Ask order price (the distance by StopLevel is maintained) if(price>tick.ask+stop_level) { //--- If the calculated price is lower than the trailing step based on order price, modify the order price if(price<buy.PriceOpen()-trailing_step) { trade.OrderModify(buy.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(buy.PriceStopLimit()>0 ? price-distance_stoplimit*Point() : 0)); } } } } } //--- Select only Sell order from the list CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL); //--- Sort the list by the distance from the price in points (by profit in points) list_sell.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the Sell order having the greatest distance int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- If the order exceeds the price (SellLimit), and it needs to be "decreased" following the price if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT) { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.bid+trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price exceeds the StopLevel distance based on the Bid order price (the distance by StopLevel is maintained) if(price>tick.bid+stop_level) { //--- If the calculated price is below the trailing step based on the order price, modify the order price if(price<sell.PriceOpen()-trailing_step) { trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),sell.PriceStopLimit()); } } } //--- If the order is below the price (SellStop and SellStopLimit), and it should be "elevated" following the price else { //--- Calculate the new order price and stop levels based on it double price=NormalizeDouble(tick.bid-trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price is below the StopLevel distance based on the Bid order price (the distance by StopLevel is maintained) if(price<tick.bid-stop_level) { //--- If the calculated price exceeds the trailing step based on the order price, modify the order price if(price>sell.PriceOpen()+trailing_step) { trade.OrderModify(sell.Ticket(),price,sl,tp,trade.RequestTypeTime(),trade.RequestExpiration(),(sell.PriceStopLimit()>0 ? price+distance_stoplimit*Point() : 0)); } } } } } } //+------------------------------------------------------------------+
EAをコンパイルしましょう。
ストップレベルなしでポジションをオープンし未決注文を出すには、StopLoss in pointsとTaleProfit in pointsの値を0に設定します。StopLoss
for modification (points)とTakeProfit for modification (points)をそれぞれ20 と60 に設定します(デフォルト値)。これらのレベルはボタンを押して設定します。
テスターでEAを起動して未決注文を設定します。次に、ストップロスとテイクプロフィットを設定するためのボタンを順番に押します。レベルが設定され、適切なエントリが操作ログに表示されます。次に、トレーリングを有効にして注文が価格に従っていることを確認し、適切なエントリが操作ログに表示されるようにします。注文によって発動されたポジションのストップロスレベルがトレールされ、適切なエントリーが操作ログに表示されます。
ネッティング口座
ヘッジ勘定
次の段階
今後の記事では、ライブラリを拡張し、MQL4との互換性を実装します。もっとエキサイティングなことはまだこれからです。
現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問、コメント、提案はコメント欄にお願いします。
シリーズのこれまでの記事:
第1部: 概念、データ管理
第2部:
過去の注文と取引のコレクション
第3部:注文と取引のコレクション、検索と並び替え
第4部:
取引イベント概念
第5部: 取引イベントのクラスとコレクション取引イベントのプログラムへの送信
第6部:
ネッティング勘定イベント
第7部: StopLimit注文アクティブ化イベント。注文イベントと位置変更イベントの機能を準備します。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/6595
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索