MetaTraderプログラムを簡単かつ迅速に開発するためのライブラリ(第26部): 未決取引リクエスト - 特定の条件下でのポジションのオープン
Artyom Trishkin | 2 5月, 2020
内容
概念
ライブラリ機能の開発中に、保留中リクエストを使用した取引の概念を紹介しました。この概念は、プログラムで設定された条件下での取引サーバエラーの処理と通常の取引注文の送信という2つの操作オプションを特徴としています。第26部に始まって、保留中リクエストを使用して取引サーバエラーの処理を徐々に実装し、エラーでパラメータを修正した後または待機後にサーバに注文を再送信する必要がある場合には、サーバへの取引注文の再送を処理できるようにしています。
この記事から始めて、特定の条件下で保留中リクエストを使用して取引できる機能を開発します。このライブラリ機能により、ユーザーはプログラムで条件を作成し、その条件で取引注文をサーバーに送信できます。
例として:
- 価格が指定された値を下回った場合または特定の時間を超えたときに買い注文を出します(銘柄プロパティ値に関連する2つの条件)。
- 指定された利益を超えた場合、ポジションを部分決済します(口座プロパティ値に関連する1つの条件)。
- ポジションがストップロスで決済されている場合は、反対のポジションを開きます(口座イベントプロパティに関連する1つの条件)。
これらの例は単純ですが、多くの条件とそれらの組み合わせがある可能性があります。この段階で、現在の口座で発生する口座、銘柄、イベントのプロパティの変更を制御できるようにします。これら3つのリストの条件は、任意の組み合わせで設定できます。
銘柄と口座プロパティの値の変更を制御するという簡単なことから始めます。これに続いて、口座イベントを制御し、それらに反応します。
保留中リクエストオブジェクトを取引ロジックの一部として機能させる(条件に基づいて取引注文を送信する)には、このオブジェクトに追加のデータを実装して、保留中リクエストのアクティブ化条件とその制御および処理のメソッドを保存する必要があります。データストレージは、2次元配列の形式で作成されます。最初の次元は条件番号(条件の数に上限はありません)を格納するためのもので、2番目の次元は最初の次元で指定された数の条件のすべてのデータ(条件ソースタイプ(銘柄、口座、イベント) 、条件自体(各ソースの列挙を作成)、比較メソッド(>、<、==、!=、> =、<=)、追跡されるプロパティの参照値とその現在の値)を含むものです。
保留中リクエストオブジェクトに設定された条件は、保留中リクエストを管理するクラスのタイマーで制御されます。アクティブ化された保留中リクエストは、保留中リクエストオブジェクトに設定されたすべての条件を満たした直後に、同じクラスからサーバに送信されます。
本稿では、保留中リクエストを使用して取引(特定の条件下でのポジションのオープン)を作成および確認します。テストEAでは、価格と時間の2つの条件のみを追跡します。条件は個別に(価格または時間)、または一緒に(価格および時間)設定できます。
データの準備
いつものように、新しいライブラリメッセージのインデックスと適切なテキストを追加することから始めます。
Datas.mqhファイルに必要なすべてのメッセージインデックスを書き込みます。
//--- CEvent MSG_EVN_EVENT, // Event MSG_EVN_TYPE, // Event type
...
//--- CAccount MSG_ACC_ACCOUNT, // Account MSG_ACC_PROP_LOGIN, // Account number
...
MSG_LIB_TEXT_REQUEST, // Pending request # MSG_LIB_TEXT_REQUEST_ACTIVATED, // Pending request activated: # MSG_LIB_TEXT_REQUEST_DATAS, // Trading request parameters MSG_LIB_TEXT_PEND_REQUEST_DATAS, // Pending trading request parameters MSG_LIB_TEXT_PEND_REQUEST_CREATED, // Pending request created MSG_LIB_TEXT_PEND_REQUEST_DELETED, // Removed due to expiration MSG_LIB_TEXT_PEND_REQUEST_EXECUTED, // Removed due to execution MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED, // Failed to obtain a pending request object from the list MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS, // Failed to add request activation parameters. Error: MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE, // Price at the moment of request generation
...
MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION, // Actual order lifetime MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS, // No free IDs to create a pending request MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS, // Activation conditions MSG_LIB_TEXT_PEND_REQUEST_CRITERION, // Criterion MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS, // Added pending request activation conditions }; //+------------------------------------------------------------------+
また、新しく追加したインデックスに対応するテキストも追加します。
//--- CEvent {"Событие","Event"}, {"Тип события","Event's type"},
...
//--- CAccount {"Аккаунт","Account"}, {"Номер счёта","Account number"},
...
{"Отложенный запрос #","Pending request #"}, {"Активирован отложенный запрос: #","Pending request activated: #"}, {"Параметры торгового запроса","Trade request parameters"}, {"Параметры отложенного торгового запроса","Pending trade request parameters"}, {"Создан отложенный запрос","Pending request created"}, {"Удалён в связи с окончанием времени его действия","Deleted due to expiration"}, {"Удалён в связи с его исполнением","Deleted due completed"}, {"Не удалось получить объект-отложенный запрос из списка","Failed to get pending request object from list"}, {"Не удалось добавить параметры активации запроса. Ошибка: ","Failed to add request activation parameters. Error: "}, {"Цена в момент создания запроса","Price at time of request create"},
...
{"Фактическое время жизни ордера","Actual of order lifetime"}, {"Нет свободных идентификаторов для создания отложенного запроса","No free IDs to create a pending request"}, {"Условия активации","Activation terms"}, {"Критерий","Criterion"}, {"Добавлены условия активации отложенного запроса","Pending request activation conditions added"}, }; //+---------------------------------------------------------------------+
単一の保留中リクエストオブジェクトが完全に異なるソース(この場合、これらは口座、銘柄、口座イベントであり、次に何かを追加できます)からの制御された条件を処理するため、 指定された保留中リクエストのアクティブ化条件のアクティブ化を追跡するために追跡するパラメータを持つデータソースが必要です。口座と銘柄のパラメータを追跡する場合、プロパティ自体は完全に異なりますが、一致するプロパティインデックスがある場合があります。混乱を避けるために、データソースを指定して、そこでプロパティ値を追跡します。
Defines.mqhファイルで、保留中リクエストのアクティブ化ソースの列挙を記述します。
//+------------------------------------------------------------------+ //| Pending request type | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_TYPE { PEND_REQ_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR, // Pending request created based on the return code or error PEND_REQ_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ, // Pending request created by request }; //+------------------------------------------------------------------+ //| Pending request activation source | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_ACTIVATION_SOURCE { PEND_REQ_ACTIVATION_SOURCE_ACCOUNT, // Pending request activated by account data PEND_REQ_ACTIVATION_SOURCE_SYMBOL, // Pending request activated by symbol data PEND_REQ_ACTIVATION_SOURCE_EVENT, // Pending request activated by trading event data }; //+------------------------------------------------------------------+ //| Integer properties of a pending trading request | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_PROP_INTEGER {
また、保留中リクエストのアクティブ化に使用される可能性のある基準の列挙を追加します。
口座、銘柄、イベントプロパティによるアクティブ化の基準については、個別の列挙を使用します。
//+------------------------------------------------------------------+ //| Possible criteria for activating requests by account properties | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_ACTIVATE_BY_ACCOUNT_PROP { //--- long PEND_REQ_ACTIVATE_BY_ACCOUNT_EMPTY = MSG_LIB_PROP_NOT_SET, // Value not set PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE = MSG_ACC_PROP_LEVERAGE, // Activate by a provided leverage PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS = MSG_ACC_PROP_LIMIT_ORDERS, // Activate by a maximum allowed number of active pending orders PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED = MSG_ACC_PROP_TRADE_ALLOWED, // Activate by the permission to trade for the current account from the server side PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT = MSG_ACC_PROP_TRADE_EXPERT, // Activate by the permission to trade for an EA from the server side //--- double PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE = MSG_ACC_PROP_BALANCE, // Activate by an account balance in the deposit currency PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT = MSG_ACC_PROP_CREDIT, // Activate by credit in a deposit currency PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT = MSG_ACC_PROP_PROFIT, // Activate by the current profit on the account in the deposit currency PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY = MSG_ACC_PROP_EQUITY, // Sort by an account equity in the deposit currency PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN = MSG_ACC_PROP_MARGIN, // Activate by an account reserved margin in the deposit currency PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE = MSG_ACC_PROP_MARGIN_FREE, // Activate by account free funds available for opening a position in the deposit currency PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL = MSG_ACC_PROP_MARGIN_LEVEL, // Activate by account margin level in % PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL = MSG_ACC_PROP_MARGIN_INITIAL, // Activate by funds reserved on an account to ensure a guarantee amount for all pending orders PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE = MSG_ACC_PROP_MARGIN_MAINTENANCE, // Activate by funds reserved on an account to ensure a minimum amount for all open positions PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS = MSG_ACC_PROP_ASSETS, // Activate by the current assets on the account PEND_REQ_ACTIVATE_BY_ACCOUNT_LIABILITIES = MSG_ACC_PROP_LIABILITIES, // Activate by the current liabilities on the account PEND_REQ_ACTIVATE_BY_ACCOUNT_COMMISSION_BLOCKED = MSG_ACC_PROP_COMMISSION_BLOCKED // Activate by the current amount of blocked commissions on the account }; //+------------------------------------------------------------------+ //| Possible criteria for activating requests by symbol properties | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP { PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY = MSG_LIB_PROP_NOT_SET, // Value not set //--- double PEND_REQ_ACTIVATE_BY_SYMBOL_BID = MSG_LIB_PROP_BID, // Activate by Bid - the best price at which a symbol can be sold PEND_REQ_ACTIVATE_BY_SYMBOL_ASK = MSG_LIB_PROP_ASK, // Activate by Ask - best price, at which an instrument can be bought PEND_REQ_ACTIVATE_BY_SYMBOL_LAST = MSG_LIB_PROP_LAST, // Activate by the last deal price //--- long PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS = MSG_SYM_PROP_SESSION_DEALS, // Activate by number of deals in the current session PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS = MSG_SYM_PROP_SESSION_BUY_ORDERS, // Activate by number of Buy orders at the moment PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS = MSG_SYM_PROP_SESSION_SELL_ORDERS, // Activate by number of Sell orders at the moment PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME = MSG_SYM_PROP_VOLUME, // Activate by the last deal volume PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH = MSG_SYM_PROP_VOLUMEHIGH, // Activate by maximum Volume per day PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW = MSG_SYM_PROP_VOLUMELOW, // Activate by minimum Volume per day PEND_REQ_ACTIVATE_BY_SYMBOL_TIME = MSG_SYM_PROP_TIME, // Activate by the last quote time PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD = MSG_SYM_PROP_SPREAD, // Activate by spread in points PEND_REQ_ACTIVATE_BY_SYMBOL_START_TIME = MSG_SYM_PROP_START_TIME, // Activate by an instrument trading start date (usually used for futures) PEND_REQ_ACTIVATE_BY_SYMBOL_EXPIRATION_TIME = MSG_SYM_PROP_EXPIRATION_TIME, // Activate by an instrument trading completion date (usually used for futures) PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_STOPS_LEVEL = MSG_SYM_PROP_TRADE_STOPS_LEVEL, // Activate by the minimum indent from the current close price (in points) for setting Stop orders PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL = MSG_SYM_PROP_TRADE_FREEZE_LEVEL, // Activate by trade operation freeze distance (in points) //--- double PEND_REQ_ACTIVATE_BY_SYMBOL_BIDHIGH = MSG_SYM_PROP_BIDHIGH, // Activate by a maximum Bid of the day PEND_REQ_ACTIVATE_BY_SYMBOL_BIDLOW = MSG_SYM_PROP_BIDLOW, // Activate by a minimum Bid of the day PEND_REQ_ACTIVATE_BY_SYMBOL_ASKHIGH = MSG_SYM_PROP_ASKHIGH, // Activate by a maximum Ask of the day PEND_REQ_ACTIVATE_BY_SYMBOL_ASKLOW = MSG_SYM_PROP_ASKLOW, // Activate by a minimum Ask of the day PEND_REQ_ACTIVATE_BY_SYMBOL_LASTHIGH = MSG_SYM_PROP_LASTHIGH, // Activate by the maximum Last of the day PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW = MSG_SYM_PROP_LASTLOW, // Activate by the minimum Last of the day PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL = MSG_SYM_PROP_VOLUME_REAL, // Activate by Volume of the day PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH_REAL = MSG_SYM_PROP_VOLUMEHIGH_REAL, // Activate by a maximum Volume of the day PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW_REAL = MSG_SYM_PROP_VOLUMELOW_REAL, // Activate by a minimum Volume of the day PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE = MSG_SYM_PROP_OPTION_STRIKE, // Activate by an option execution price PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_ACCRUED_INTEREST = MSG_SYM_PROP_TRADE_ACCRUED_INTEREST, // Activate by an accrued interest PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FACE_VALUE = MSG_SYM_PROP_TRADE_FACE_VALUE, // Activate by a face value – initial bond value set by an issuer PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_LIQUIDITY_RATE = MSG_SYM_PROP_TRADE_LIQUIDITY_RATE, // Activate by a liquidity rate – the share of an asset that can be used for a margin PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_LONG = MSG_SYM_PROP_SWAP_LONG, // Activate by a long swap value PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_SHORT = MSG_SYM_PROP_SWAP_SHORT, // Activate by a short swap value PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME = MSG_SYM_PROP_SESSION_VOLUME, // Activate by a summary volume of the current session deals PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER = MSG_SYM_PROP_SESSION_TURNOVER, // Activate by a summary turnover of the current session PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_INTEREST = MSG_SYM_PROP_SESSION_INTEREST, // Activate by a summary open interest PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME = MSG_SYM_PROP_SESSION_BUY_ORDERS_VOLUME, // Activate by the current volume of Buy orders PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME= MSG_SYM_PROP_SESSION_SELL_ORDERS_VOLUME, // Activate by the current volume of Sell orders PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_OPEN = MSG_SYM_PROP_SESSION_OPEN, // Activate by an open price of the current session PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_CLOSE = MSG_SYM_PROP_SESSION_CLOSE, // Activate by a close price of the current session PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_AW = MSG_SYM_PROP_SESSION_AW, // Activate by an average weighted session price PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_SETTLEMENT = MSG_SYM_PROP_SESSION_PRICE_SETTLEMENT, // Activate by a settlement price of the current session PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN = MSG_SYM_PROP_SESSION_PRICE_LIMIT_MIN, // Activate by a minimum session price PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX = MSG_SYM_PROP_SESSION_PRICE_LIMIT_MAX, // Activate by a maximum session price }; //+------------------------------------------------------------------+ //| Possible criteria for activating requests by events | //+------------------------------------------------------------------+ enum ENUM_PEND_REQ_ACTIVATE_BY_EVENT { PEND_REQ_ACTIVATE_BY_EVENT_EMPTY = MSG_LIB_PROP_NOT_SET, // Value not set PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED = MSG_EVN_STATUS_MARKET_POSITION, // Position opened PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED = MSG_EVN_STATUS_HISTORY_POSITION, // Position closed PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_PLASED = MSG_EVN_PENDING_ORDER_PLASED, // Pending order placed PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_REMOVED = MSG_EVN_PENDING_ORDER_REMOVED, // Pending order removed PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CREDIT = MSG_EVN_ACCOUNT_CREDIT, // Accruing credit (3) PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CHARGE = MSG_EVN_ACCOUNT_CHARGE, // Additional charges PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CORRECTION = MSG_EVN_ACCOUNT_CORRECTION, // Correcting entry PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BONUS = MSG_EVN_ACCOUNT_BONUS, // Charging bonuses PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION = MSG_EVN_ACCOUNT_COMISSION, // Additional commissions PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_DAILY = MSG_EVN_ACCOUNT_COMISSION_DAILY, // Commission charged at the end of a day PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_MONTHLY = MSG_EVN_ACCOUNT_COMISSION_MONTHLY, // Commission charged at the end of a trading month PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_DAILY = MSG_EVN_ACCOUNT_COMISSION_AGENT_DAILY, // Agent commission charged at the end of a trading day PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY = MSG_EVN_ACCOUNT_COMISSION_AGENT_MONTHLY, // Agent commission charged at the end of a month PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_INTEREST = MSG_EVN_ACCOUNT_INTEREST, // Accruing interest on free funds PEND_REQ_ACTIVATE_BY_EVENT_BUY_CANCELLED = MSG_EVN_BUY_CANCELLED, // Canceled buy deal PEND_REQ_ACTIVATE_BY_EVENT_SELL_CANCELLED = MSG_EVN_SELL_CANCELLED, // Canceled sell deal PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT = MSG_EVN_DIVIDENT, // Accruing dividends PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT_FRANKED = MSG_EVN_DIVIDENT_FRANKED, // Accrual of franked dividend PEND_REQ_ACTIVATE_BY_EVENT_TAX = MSG_EVN_TAX, // Tax accrual PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_REFILL = MSG_EVN_BALANCE_REFILL, // Replenishing account balance PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = MSG_EVN_BALANCE_WITHDRAWAL, // Withdrawing funds from an account PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED = MSG_EVN_ACTIVATED_PENDING, // Pending order activated by price PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL = MSG_EVN_ACTIVATED_PENDING_PARTIALLY, // Pending order partially activated by price PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED_PARTIAL = MSG_EVN_POSITION_OPENED_PARTIALLY, // Position opened partially PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL = MSG_EVN_POSITION_CLOSED_PARTIALLY, // Position closed partially PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_POS = MSG_EVN_POSITION_CLOSED_BY_POS, // Position closed by an opposite one PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_POS = MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_POS, // Position partially closed by an opposite one PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_SL = MSG_EVN_POSITION_CLOSED_BY_SL, // Position closed by StopLoss PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_TP = MSG_EVN_POSITION_CLOSED_BY_TP, // Position closed by TakeProfit PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_SL = MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_SL, // Position closed partially by StopLoss PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_TP = MSG_EVN_POSITION_CLOSED_PARTIALLY_BY_TP, // Position closed partially by TakeProfit PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET = MSG_EVN_POSITION_REVERSED_BY_MARKET, // Position reversal by a new deal (netting) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING = MSG_EVN_POSITION_REVERSED_BY_PENDING, // Position reversal by activating a pending order (netting) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL = MSG_EVN_POSITION_REVERSE_PARTIALLY, // Position reversal by partial market order execution (netting) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_MARKET = MSG_EVN_POSITION_VOLUME_ADD_BY_MARKET, // Added volume to a position by a new deal (netting) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_PENDING = MSG_EVN_POSITION_VOLUME_ADD_BY_PENDING, // Added volume to a position by activating a pending order (netting) PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE = MSG_EVN_MODIFY_ORDER_PRICE, // Order price change PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL = MSG_EVN_MODIFY_ORDER_PRICE_SL, // Changing order and StopLoss price PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_TP = MSG_EVN_MODIFY_ORDER_PRICE_TP, // Order and TakeProfit price change PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL_TP = MSG_EVN_MODIFY_ORDER_PRICE_SL_TP, // Changing order, StopLoss and TakeProfit price PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL_TP = MSG_EVN_MODIFY_ORDER_SL_TP, // Changing order's StopLoss and TakeProfit price PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL = MSG_EVN_MODIFY_ORDER_SL, // Modify StopLoss order PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_TP = MSG_EVN_MODIFY_ORDER_TP, // Modify TakeProfit order PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL_TP = MSG_EVN_MODIFY_POSITION_SL_TP, // Change position's StopLoss and TakeProfit PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL = MSG_EVN_MODIFY_POSITION_SL, // Modify position's StopLoss PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_TP = MSG_EVN_MODIFY_POSITION_TP, // Modify position's TakeProfit PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_MARKET_PARTIAL = MSG_EVN_REASON_ADD_PARTIALLY, // Added volume to a position by partial execution of a market order (netting) PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_PENDING_PARTIAL = MSG_EVN_REASON_ADD_BY_PENDING_PARTIALLY, // Added volume to a position by partial activation of a pending order (netting) PEND_REQ_ACTIVATE_BY_EVENT_TRIGGERED_STOP_LIMIT_ORDER = MSG_EVN_REASON_STOPLIMIT_TRIGGERED, // StopLimit order activation PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL= MSG_EVN_REASON_REVERSE_BY_PENDING_PARTIALLY, // Position reversal by activating a pending order (netting) }; //+------------------------------------------------------------------+
列挙の定数値は、適切な銘柄、口座、イベントプロパティのテキストメッセージの定数値と同じなので、操作ログにメッセージを表示するときに、記述された定数を銘柄、口座、またはイベントに属するものとして追加で識別する必要がなくなります。代わりに、定数のインデックス自体を使用してメッセージを表示します。
アクティブ化条件の3つの異なる列挙を使用して、必要な保留中リクエストのアクティブ化基準をコンパイルするために、3つの列挙から定数の任意の組み合わせを最終的に設定できます。
比較タイプの説明を返す関数をDELib.mqhサービス関数ファイルに追加します。
//+------------------------------------------------------------------+ //| Return the comparison type description | //+------------------------------------------------------------------+ string ComparisonTypeDescription(const ENUM_COMPARER_TYPE type) { switch((int)type) { case EQUAL : return " == "; case MORE : return " > "; case LESS : return " < "; case EQUAL_OR_MORE : return " >= "; case EQUAL_OR_LESS : return " <= "; default : return " != "; } } //+------------------------------------------------------------------+
「STOP_LOSS」および「TAKE_PROFIT」文字列を含む列挙定数の名前は、多くのライブラリファイルでそれぞれ「SL」と「TP」に置き換えられています。
オンデマンドで作成された保留中リクエストオブジェクト
抽象保留リクエストの基本オブジェクトは、すべてのライブラリオブジェクトの基本オブジェクトから継承されるようになりました。
すべてのライブラリオブジェクトの基本オブジェクトファイルをCPendRequestクラスファイルに含めて、クラスに基本オブジェクトを継承させます。
//+------------------------------------------------------------------+ //| PendRequest.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include <Object.mqh> #include "..\..\Services\DELib.mqh" #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Abstract pending trading request class | //+------------------------------------------------------------------+ class CPendRequest : public CBaseObj {
クラスのprivateセクションで、追跡された保留中リクエストのアクティブ化基準を使用してデータを格納するための配列を宣言します。
//+------------------------------------------------------------------+ //| Abstract pending trading request class | //+------------------------------------------------------------------+ class CPendRequest : public CBaseObj { private: MqlTradeRequest m_request; // Trade request structure CPause m_pause; // Pause class object /* Data on a pending request activation in the array: The first dimension contains the activation criteria number The second one features: m_activated_control[criterion number][0] - controlled property source m_activated_control[criterion number][1] - controlled property m_activated_control[criterion number][2] - type of comparing a controlled property with an actual value (=,>,<,!=,>=,<=) m_activated_control[criterion number][3] - property reference value for activation m_activated_control[criterion number][4] - actual property value */ double m_activated_control[][5]; // Array of reference values of the pending request activation criterion //--- Copy trading request data void CopyRequest(const MqlTradeRequest &request);
同じprivateセクションで、EA設定で設定されたマジックナンバーとID、および1番目と2番目のグループのID を返すメソッド追加します。
また、制御されたプロパティの実際値に対するチェックが成功したことを示すフラグを返すメソッド、 2つの制御されたプロパティ値を比較するメソッド、操作ログに値を正しく表示するために、追跡されるプロパティの小数点以下の桁数を返すメソッドを宣言します。
//--- Return (1) the magic number, ID of the (2) magic number, (3) the first group, (4) the second group, //--- (5) hedging account flag, (6) flag indicating the real property is equal to the value ulong GetMagic(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC); } ushort GetMagicID(void) const { return CBaseObj::GetMagicID((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC)); } uchar GetGroupID1(void) const { return CBaseObj::GetGroupID1((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC));} uchar GetGroupID2(void) const { return CBaseObj::GetGroupID2((uint)this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC));} bool IsHedge(void) const { return this.m_is_hedge; } bool IsEqualByMode(const int mode,const double value) const; bool IsEqualByMode(const int mode,const long value) const; //--- Return the flags indicating the pending request has completed changing each of the order/position parameters bool IsCompletedVolume(void) const; bool IsCompletedPrice(void) const; bool IsCompletedStopLimit(void) const; bool IsCompletedStopLoss(void) const; bool IsCompletedTakeProfit(void) const; bool IsCompletedTypeFilling(void) const; bool IsCompletedTypeTime(void) const; bool IsCompletedExpiration(void) const; //--- Return the flag of a successful check of a controlled object property and the appropriate actual property bool IsComparisonCompleted(const uint index) const; //--- Compare two data source values by a comparison type bool IsCompared(const double actual_value,const double control_value,const ENUM_COMPARER_TYPE compare) const; //--- Return the number of decimal places of a controlled property int DigitsControlledValue(const uint index) const; public:
マジックナンバーおよびグループIDを返すメソッドは、基本抽象保留中リクエストオブジェクトを継承したCBaseObj親オブジェクトの同名のメソッドを使用します。
クラスのpublicセクションのリクエストオブジェクトプロパティに簡単にアクセスするためのメソッドのブロックで、必要なすべてのpublicメソッドの宣言を追加します。これらはさらに検討します。
//+------------------------------------------------------------------+ //| Methods of a simplified access to the request object properties | //+------------------------------------------------------------------+ //--- Return (1) request structure, (2) status, (3) type, (4) price at the moment of the request generation, //--- (5) request generation time, (6) next attempt activation time, //--- (7) waiting time between requests, (8) current attempt index, //--- (9) number of attempts, (10) request ID //--- (11) result a request is based on, //--- (12) order ticket, (13) position ticket, (14) trading operation type MqlTradeRequest MqlRequest(void) const { return this.m_request; } ENUM_PEND_REQ_STATUS Status(void) const { return (ENUM_PEND_REQ_STATUS)this.GetProperty(PEND_REQ_PROP_STATUS); } ENUM_PEND_REQ_TYPE TypeRequest(void) const { return (ENUM_PEND_REQ_TYPE)this.GetProperty(PEND_REQ_PROP_TYPE); } double PriceCreate(void) const { return this.GetProperty(PEND_REQ_PROP_PRICE_CREATE); } ulong TimeCreate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_CREATE); } ulong TimeActivate(void) const { return this.GetProperty(PEND_REQ_PROP_TIME_ACTIVATE); } ulong WaitingMSC(void) const { return this.GetProperty(PEND_REQ_PROP_WAITING); } uchar CurrentAttempt(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT); } uchar TotalAttempts(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_TOTAL); } uchar ID(void) const { return (uchar)this.GetProperty(PEND_REQ_PROP_ID); } int Retcode(void) const { return (int)this.GetProperty(PEND_REQ_PROP_RETCODE); } ulong Order(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER); } ulong Position(void) const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION); } ENUM_TRADE_REQUEST_ACTIONS Action(void) const { return (ENUM_TRADE_REQUEST_ACTIONS)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ACTION); } //--- Return the actual (1) volume, (2) order, (3) limit order, //--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type, //--- (7) order expiration type and (8) order lifetime double ActualVolume(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME); } double ActualPrice(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE); } double ActualStopLimit(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT); } double ActualSL(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_SL); } double ActualTP(void) const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_TP); } ENUM_ORDER_TYPE_FILLING ActualTypeFilling(void) const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING); } ENUM_ORDER_TYPE_TIME ActualTypeTime(void) const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME); } datetime ActualExpiration(void) const { return (datetime)this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION); } //--- Set (1) the price when creating a request, (2) request creation time, //--- (3) current attempt time, (4) waiting time between requests, //--- (5) current attempt index, (6) number of attempts, (7) ID, //--- (8) order ticket, (9) position ticket, (10) pending request type void SetPriceCreate(const double price) { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); } void SetTimeCreate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.m_pause.SetTimeBegin(time); } void SetTimeActivate(const ulong time) { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time); } void SetWaitingMSC(const ulong miliseconds) { this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds); this.m_pause.SetWaitingMSC(miliseconds); } void SetCurrentAttempt(const uchar number) { this.SetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT,number); } void SetTotalAttempts(const uchar number) { this.SetProperty(PEND_REQ_PROP_TOTAL,number); } void SetID(const uchar id) { this.SetProperty(PEND_REQ_PROP_ID,id); } void SetOrder(const ulong ticket) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_ORDER,ticket); } void SetPosition(const ulong ticket) { this.SetProperty(PEND_REQ_PROP_MQL_REQ_POSITION,ticket); } void SetTypeRequest(const ENUM_PEND_REQ_TYPE type) { this.SetProperty(PEND_REQ_PROP_TYPE,type); } //--- Set the actual (1) volume, (2) order, (3) limit order, //--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type, //--- (7) order expiration type and (8) order lifetime void SetActualVolume(const double volume) { this.SetProperty(PEND_REQ_PROP_ACTUAL_VOLUME,volume); } void SetActualPrice(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_PRICE,price); } void SetActualStopLimit(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT,price); } void SetActualSL(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_SL,price); } void SetActualTP(const double price) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TP,price); } void SetActualTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,type); } void SetActualTypeTime(const ENUM_ORDER_TYPE_TIME type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME,type); } void SetActualExpiration(const datetime expiration) { this.SetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION,expiration); } //--- Set a controlled property and a comparison method for a request activation criteria data by its index - both the actual one and the one in the object of //--- account, symbol or trading event property value (depends on 'source' value) for activating a pending request void SetNewActivationProperties(const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value); //--- Set a (1) controlled property, (2) comparison type, (3) object value and //--- (4) actual controlled property value for activating a pending request bool SetActivationProperty(const uint index,const ENUM_PEND_REQ_ACTIVATION_SOURCE source,const int property); bool SetActivationComparerType(const uint index,const ENUM_COMPARER_TYPE comparer_type); bool SetActivationControlValue(const uint index,const double value); bool SetActivationActualValue(const uint index,const double value); //--- Return (1) a pending request activation source, (2) controlled property, (3) comparison type, //--- (4) object value,(5) actual controlled property value for activating a pending request ENUM_PEND_REQ_ACTIVATION_SOURCE GetActivationSource(const uint index) const; int GetActivationProperty(const uint index) const; ENUM_COMPARER_TYPE GetActivationComparerType(const uint index) const; double GetActivationControlValue(const uint index) const; double GetActivationActualValue(const uint index) const; //--- Return the flag of a successful check of all controlled object properties and the appropriate actual properties bool IsAllComparisonCompleted(void) const;
SetTypeRequest()メソッドはタイプメソッドに渡される「保留中のリクエストタイプ」プロパティを設定します。「エラーコードに基づく保留中リクエスト」または「リクエストによって作成された保留中リクエスト」をタイプとして使用できます。保留中リクエストタイプは、「エラーコード」パラメータに応じて、クラスコンストラクタ内のオブジェクトに自動的に設定されます。コードがゼロの場合、保留中リクエストオブジェクトはプログラムリクエストによって作成されています。したがって、このメソッドは現在どこでも使用されていません。これは、保留中リクエストタイプを外部からすばやく変更する必要がある場合に備えて作成されます(個人的には、まだ必要がありません)。
適切なメソッドのブロックに制御されたプロパティの説明を返すメソッドの宣言を追加します。
//+------------------------------------------------------------------+ //| Descriptions of request object properties | //+------------------------------------------------------------------+ //--- Get description of a request (1) integer, (2) real and (3) string property string GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property); string GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property); string GetPropertyDescription(ENUM_PEND_REQ_PROP_STRING property); //--- Return the description of a (1) controlled property, (2) comparison type, (3) controlled property value in the object, //--- (4) actual controlled property value for activating a pending request, (5) total number of activation conditions string GetActivationPropertyDescription(const uint index) const; string GetActivationComparerTypeDescription(const uint index) const; string GetActivationControlValueDescription(const uint index) const; string GetActivationActualValueDescription(const uint index) const; uint GetActivationCriterionTotal(void) const { return ::ArrayRange(this.m_activated_control,0); } //--- Return the names of pending request object parameters string StatusDescription(void) const; string TypeRequestDescription(void) const; string IDDescription(void) const; string RetcodeDescription(void) const; string TimeCreateDescription(void) const; string TimeActivateDescription(void) const; string TimeWaitingDescription(void) const; string CurrentAttemptDescription(void) const; string TotalAttemptsDescription(void) const; string PriceCreateDescription(void) const; string TypeFillingActualDescription(void) const; string TypeTimeActualDescription(void) const; string ExpirationActualDescription(void) const; string VolumeActualDescription(void) const; string PriceActualDescription(void) const; string StopLimitActualDescription(void) const; string StopLossActualDescription(void) const; string TakeProfitActualDescription(void) const; //--- Return the names of trading request structures parameters in the request object string MqlReqActionDescription(void) const; string MqlReqMagicDescription(void) const; string MqlReqOrderDescription(void) const; string MqlReqSymbolDescription(void) const; string MqlReqVolumeDescription(void) const; string MqlReqPriceDescription(void) const; string MqlReqStopLimitDescription(void) const; string MqlReqStopLossDescription(void) const; string MqlReqTakeProfitDescription(void) const; string MqlReqDeviationDescription(void) const; string MqlReqTypeOrderDescription(void) const; string MqlReqTypeFillingDescription(void) const; string MqlReqTypeTimeDescription(void) const; string MqlReqExpirationDescription(void) const; string MqlReqCommentDescription(void) const; string MqlReqPositionDescription(void) const; string MqlReqPositionByDescription(void) const; //--- Display (1) description of request properties (full_prop=true - all properties, false - only supported ones), //--- (2) request activation parameters, (3) short message about the request, (4) short request name (3 and 4 - implementation in the class descendants) void Print(const bool full_prop=false); void PrintActivations(void); virtual void PrintShort(void){;} virtual string Header(void){return NULL;} }; //+------------------------------------------------------------------+
GetActivationCriterionTotal()メソッドは、アクティブ化条件データ配列の最初の次元のサイズ、つまり保留中リクエストオブジェクトのアクティブ化条件の数を返します。
クラスコンストラクタで、最初の次元のアクティブ化条件データ配列のサイズをゼロに設定します。
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPendRequest::CPendRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const double price, const ulong time, const MqlTradeRequest &request, const int retcode) { this.CopyRequest(request); this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif; this.m_digits=(int)::SymbolInfoInteger(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL),SYMBOL_DIGITS); int dg=(int)DigitsLots(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)); this.m_digits_lot=(dg==0 ? 1 : dg); this.SetProperty(PEND_REQ_PROP_STATUS,status); this.SetProperty(PEND_REQ_PROP_ID,id); this.SetProperty(PEND_REQ_PROP_RETCODE,retcode); this.SetProperty(PEND_REQ_PROP_TYPE,this.GetProperty(PEND_REQ_PROP_RETCODE)>0 ? PEND_REQ_TYPE_ERROR : PEND_REQ_TYPE_REQUEST); this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time); this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price); this.m_pause.SetTimeBegin(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)); this.m_pause.SetWaitingMSC(this.GetProperty(PEND_REQ_PROP_WAITING)); ::ArrayResize(this.m_activated_control,0,10); } //+------------------------------------------------------------------+
連続するアクティブ化条件を追加するたびに、アクティブ化条件データ配列のサイズが自動的に変更されます。
保留中リクエストオブジェクトデータの完全なリストを表示するメソッドで、すべてのプロパティを表示した後、アクティブ化条件リストの表示を追加します。
//+------------------------------------------------------------------+ //| Display the pending request properties in the journal | //+------------------------------------------------------------------+ void CPendRequest::Print(const bool full_prop=false) { int header_code= ( this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_OPEN ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_CLOSE ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_SLTP ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_PLACE ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_REMOVE ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE : this.GetProperty(PEND_REQ_PROP_STATUS)==PEND_REQ_STATUS_MODIFY ? MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY : WRONG_VALUE ); ::Print("============= \"",CMessage::Text(header_code),"\" ============="); int beg=0, end=PEND_REQ_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_PEND_REQ_PROP_INTEGER prop=(ENUM_PEND_REQ_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=PEND_REQ_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_PEND_REQ_PROP_DOUBLE prop=(ENUM_PEND_REQ_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=PEND_REQ_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_PEND_REQ_PROP_STRING prop=(ENUM_PEND_REQ_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } this.PrintActivations(); ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",CMessage::Text(header_code),"\" ==================\n"); } //+------------------------------------------------------------------+
保留中リクエストのアクティブ化条件を操作ログに表示するメソッドを実装します。
//+------------------------------------------------------------------+ //| Display request activation parameters in the journal | //+------------------------------------------------------------------+ void CPendRequest::PrintActivations(void) { //--- Get the size of the activation conditions data array's first dimension, //--- if it exceeds zero, send all data written in the data array to the journal int range=::ArrayRange(this.m_activated_control,0); if(range>0) { ::Print("--- ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS)," ---"); for(int i=0;i<range;i++) { ENUM_PEND_REQ_ACTIVATION_SOURCE source=(ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[i][0]; string type= ( source==PEND_REQ_ACTIVATION_SOURCE_ACCOUNT ? CMessage::Text(MSG_ACC_ACCOUNT) : source==PEND_REQ_ACTIVATION_SOURCE_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL) : source==PEND_REQ_ACTIVATION_SOURCE_EVENT ? CMessage::Text(MSG_EVN_EVENT) : "" ); ::Print(" - ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CRITERION)," #",string(i+1),". ",type,": ",this.GetActivationPropertyDescription(i)); } } ::Print(""); } //+------------------------------------------------------------------+
以下は、アクティブ化条件データ配列で保留中リクエストをアクティブにするための新しい条件を作成するメソッドです。
//+------------------------------------------------------------------+ //| Set a controlled property, values | //| and comparison method for activating a pending request | //+------------------------------------------------------------------+ void CPendRequest::SetNewActivationProperties(const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value) { int range=::ArrayRange(this.m_activated_control,0); if(::ArrayResize(this.m_activated_control,range+1,10)==WRONG_VALUE) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS)); return; } this.m_activated_control[range][0]=source; this.m_activated_control[range][1]=property; this.m_activated_control[range][2]=comparer_type; this.m_activated_control[range][3]=control_value; this.m_activated_control[range][4]=actual_value; } //+---------------------------------------------------------------------+
アクティブ化のデータソース、アクティブ化の条件、制御下および実際のアクティブ化条件値、並びに比較メソッドがメソッドに渡されます。
アクティブ化条件データ配列のサイズが1つ増加され、配列内の必要なすべてのデータは、メソッド入力で渡された値で書き入れられます。このメソッドは、新しいアクティブ化条件を追加する場合にのみ使用されるべきです。
次のメソッドは、リクエストオブジェクトに既に存在するアクティブ化条件を修正するために使用されます。
//+---------------------------------------------------------------------+ //| Set a controlled property to activate a request | //+---------------------------------------------------------------------+ bool CPendRequest::SetActivationProperty(const uint index,const ENUM_PEND_REQ_ACTIVATION_SOURCE source,const int property) { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return false; } this.m_activated_control[index][0]=source; this.m_activated_control[index][1]=property; return true; } //+------------------------------------------------------------------+ //| Set the object property comparison type | //| with the actual one for a pending request activation | //+------------------------------------------------------------------+ bool CPendRequest::SetActivationComparerType(const uint index,const ENUM_COMPARER_TYPE comparer_type) { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return false; } this.m_activated_control[index][2]=comparer_type; return true; } //+------------------------------------------------------------------+ //| Set the controlled property | //| value for activating a pending request | //+------------------------------------------------------------------+ bool CPendRequest::SetActivationControlValue(const uint index,const double value) { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return false; } this.m_activated_control[index][3]=value; return true; } //+------------------------------------------------------------------+ //| Set the actual value | //| of a controlled property in the request object | //+------------------------------------------------------------------+ bool CPendRequest::SetActivationActualValue(const uint index,const double value) { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return false; } this.m_activated_control[index][4]=value; return true; } //+------------------------------------------------------------------+
SetActivationProperty()で制御されるプロパティを設定するメソッドは、インデックスと2つの条件パラメータ(条件ソース(銘柄、口座、またはイベント)およびアクティブ化条件自体(上記で検討した適切な列挙から)を受け取ります。 条件は、プロパティ変更のソースとタイプの2つのパラメータで構成されます。アクティブ化の値を設定する他のメソッドは、インデックスと値のみを受け取ります。
アクティブ化の条件番号がインデックスとして使用されます。条件が1つしかない場合、インデックスはゼロに等しくなければなりません。2つの条件の場合、変更する条件などに応じて、インデックスは0または1になるはずです。配列の最初の次元を超えるインデックスを渡すと、無効なインデックスエントリが操作ログに表示され、falseが返されます。
以下は、アクティブ化条件パラメータを返すメソッドです。
//+------------------------------------------------------------------+ //| Return a pending request activation source | //+------------------------------------------------------------------+ ENUM_PEND_REQ_ACTIVATION_SOURCE CPendRequest::GetActivationSource(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return WRONG_VALUE; } return (ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[index][0]; } //+------------------------------------------------------------------+ //| Return a controlled property to activate a request | //+------------------------------------------------------------------+ int CPendRequest::GetActivationProperty(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return WRONG_VALUE; } return (int)this.m_activated_control[index][1]; } //+------------------------------------------------------------------+ //| Return the object property comparison type | //| with the actual one for a pending request activation | //+------------------------------------------------------------------+ ENUM_COMPARER_TYPE CPendRequest::GetActivationComparerType(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return WRONG_VALUE; } return (ENUM_COMPARER_TYPE)this.m_activated_control[index][2]; } //+------------------------------------------------------------------+ //| Return the controlled property | //| value for activating a pending request | //+------------------------------------------------------------------+ double CPendRequest::GetActivationControlValue(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return EMPTY_VALUE; } return this.m_activated_control[index][3]; } //+------------------------------------------------------------------+ //| Return the actual value | //| of a controlled property in the request object | //+------------------------------------------------------------------+ double CPendRequest::GetActivationActualValue(const uint index) const { int range=::ArrayRange(this.m_activated_control,0); if((int)index>range-1 || range==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(4002)); return EMPTY_VALUE; } return this.m_activated_control[index][4]; } //+------------------------------------------------------------------+
ここではすべてがアクティブ化プロパティの設定と同様ですが、すべての条件プロパティが一度に1つずつ返されるため、各メソッドでリクエストされたアクティブ化条件インデックスを渡すだけで十分です。無効なインデックスが渡された場合、無効なインデックスエントリが操作ログに表示されます。整数値を返すメソッドの場合は-1の値が返され、実数値を返すメソッドの場合はEMPTY_VALUEが返されます。
以下は、指定した比較タイプで2つの値を比較するメソッドです。
//+------------------------------------------------------------------+ //| Compare two data source values by a comparison type | //+------------------------------------------------------------------+ bool CPendRequest::IsCompared(const double actual_value,const double control_value,const ENUM_COMPARER_TYPE compare) const { switch((int)compare) { case EQUAL : return(actual_value<control_value || actual_value>control_value ? false : true); case NO_EQUAL : return(actual_value<control_value || actual_value>control_value ? true : false); case MORE : return(actual_value>control_value ? true : false); case LESS : return(actual_value<control_value ? true : false); case EQUAL_OR_MORE : return(actual_value<control_value ? false : true); case EQUAL_OR_LESS : return(actual_value>control_value ? false : true); default : break; } return false; } //+------------------------------------------------------------------+
このメソッドは、比較されるプロパティの現在の値、現在の値が比較される参照値、および比較タイプを受け取ります。比較タイプに応じて、現在のプロパティ値は、比較結果を返す参照値と比較されます。
以下は、アクティブ化条件データ配列内のインデックスによるアクティブ化条件の正常な比較のフラグを返すメソッドです。
//+----------------------------------------------------------------------+ //| Return the flag of a successful check of a controlled object property| //| and the appropriate real property | //+----------------------------------------------------------------------+ bool CPendRequest::IsComparisonCompleted(const uint index) const { //--- If the controlled property is not set, return 'false' if(this.m_activated_control[index][1]==MSG_LIB_PROP_NOT_SET) return false; //--- Return the result of the specified comparison of a controlled property value with a real one ENUM_COMPARER_TYPE comparer=(ENUM_COMPARER_TYPE)this.m_activated_control[index][2]; return this.IsCompared(this.m_activated_control[index][4],this.m_activated_control[index][3],comparer); } //+------------------------------------------------------------------+
このメソッドは、保留中リクエストのアクティブ化条件のいずれかのアクティブ化フラグを返します。メソッド入力は、アクティブ化条件データ配列内の確認された条件のインデックスを渡します。IsCompared()メソッドを使用して比較が実行され、比較結果が返されます。
以下は、リクエストオブジェクトに対して作成されたすべてのアクティブ化条件の確認が成功したことを示すフラグを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the flag of successful check of all controlled properties | //| of the object and the appropriate actual properties | //+------------------------------------------------------------------+ bool CPendRequest::IsAllComparisonCompleted(void) const { bool res=true; int range=::ArrayRange(this.m_activated_control,0); if(range==0) return false; for(int i=0;i<range;i++) res &=this.IsComparisonCompleted(i); return res; } //+-------------------------------------------------------------------+
これは、アクティブ化時間について保留中リクエストオブジェクトを確認できる汎用メソッドです。
ここでは、アクティブ化条件データの配列の最初の次元をループして、 IsComparisonCompleted()メソッドを使用して、確認成功フラグを確認結果(res変数)に追加します。確認は、制御されたプロパティループがインデックスに一致するかどうかを定義します。ループが完了すると、すべての条件を確認した結果が返されます。少なくとも1つの条件が満たされない場合、またはデータ配列が最初の次元でサイズがゼロの場合、結果はfalseになります。
以下は、操作ログにアクティブ化条件の説明を正しく表示するために小数点以下の桁数を返すメソッドです。
//+-------------------------------------------------------------------+ //|Return the number of decimal places of a controlled property | //+-------------------------------------------------------------------+ int CPendRequest::DigitsControlledValue(const uint index) const { int dg=0; //--- Depending on the activation condition source, check the activation conditions //--- and write the required number of decimal places to the result switch((int)this.m_activated_control[index][0]) { //--- Account. If an activation condition is an integer value, then 0, //--- if it is a real value, then the number of decimal places in the current currency case PEND_REQ_ACTIVATION_SOURCE_ACCOUNT : dg=(this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE ? 0 : this.m_digits_currency); break; //--- Symbol. Depending on a condition, write either a number of decimal places in a symbol quote, //--- or a number of decimal places in the current currency, or a number of decimal places in the lot value, or zero case PEND_REQ_ACTIVATION_SOURCE_SYMBOL : //--- digits if( (this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS && this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY) || this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE || this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME || (this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL && this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL) ) dg=this.m_digits; //--- не digits else if( this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW) { //--- digits currency if( (this.m_activated_control[index][1]>PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE && this.m_activated_control[index][1]<PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME) || this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER ) dg=this.m_digits_currency; //--- digits lots else dg=(this.m_digits_lot==0 ? 1 : this.m_digits_lot); } //--- 0 else dg=0; break; //--- Default is zero default: break; } return dg; } //+------------------------------------------------------------------+
このメソッドは、ソースに応じてアクティブ化のソースと条件を確認します。アクティブ化に応じて、システムは、銘柄相場値の小数点以下の桁数、現在の口座通貨の小数点以下の桁数、または銘柄ロット値の小数点以下の桁数、またはゼロを返します。
以下は、制御プロパティのテキスト説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the controlled property description by index | //+------------------------------------------------------------------+ string CPendRequest::GetActivationPropertyDescription(const uint index) const { //--- Get the activation source and, depending on that source, create a description text for a type of comparison with the reference value ENUM_PEND_REQ_ACTIVATION_SOURCE source=(ENUM_PEND_REQ_ACTIVATION_SOURCE)this.m_activated_control[index][0]; string value= ( source==PEND_REQ_ACTIVATION_SOURCE_EVENT ? "" : ( this.m_activated_control[index][1]==MSG_LIB_PROP_NOT_SET ? "" : this.GetActivationComparerTypeDescription(index)+this.GetActivationControlValueDescription(index) ) ); //--- Return the activation conditions description + comparison type + controlled value return ( source==PEND_REQ_ACTIVATION_SOURCE_ACCOUNT ? CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_ACCOUNT_PROP)this.m_activated_control[index][1])+value : source==PEND_REQ_ACTIVATION_SOURCE_SYMBOL ? CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP)this.m_activated_control[index][1])+value : source==PEND_REQ_ACTIVATION_SOURCE_EVENT ? CMessage::Text((ENUM_PEND_REQ_ACTIVATE_BY_EVENT)this.m_activated_control[index][1])+value : "" ); } //+------------------------------------------------------------------+
このメソッドは、アクティブ化条件データ配列の条件インデックスを受け取ります。インデックスでアクティブ化ソースを取得し、それに応じて残りのテキストメッセージを取得します。これらのメッセージは、最終的なテキストを調整して返すために使用されます。
以下は、比較タイプの説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return the comparison type description | //+------------------------------------------------------------------+ string CPendRequest::GetActivationComparerTypeDescription(const uint index) const { return ComparisonTypeDescription((ENUM_COMPARER_TYPE)this.m_activated_control[index][2]); } //+------------------------------------------------------------------+
このメソッドは、パラメータによってメソッドに渡されたアクティブ化条件インデックスによってデータ配列に設定された比較タイプのテキスト説明を単に返します。
以下は、リクエストオブジェクトで制御されたプロパティ値の説明を返すメソッドです。
//+------------------------------------------------------------------+ //| Return a controlled property value description in an object | //+------------------------------------------------------------------+ string CPendRequest::GetActivationControlValueDescription(const uint index) const { return ( this.m_activated_control[index][3]!=EMPTY_VALUE ? ( this.m_activated_control[index][0]==PEND_REQ_ACTIVATION_SOURCE_SYMBOL && this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_TIME ? ::TimeToString((ulong)this.m_activated_control[index][3]) : ::DoubleToString(this.m_activated_control[index][3],this.DigitsControlledValue(index)) ) : "" ); } //+------------------------------------------------------------------+
メソッドは条件インデックスを受け取ります。
指定されたインデックスによって配列に設定された制御プロパティ値が確認されます。指定されたインデックスと等しく、「空の値」(EMPTY_VALUE)と等しくない場合、条件とそのタイプが確認されます。結果として銘柄時間が確認された場合は時間のテキストの説明が返され、それ以外の場合は整数または実数のテキストの説明が、正しい小数点以下の桁数で返されます。
以下は、リクエストオブジェクトで実際の制御プロパティ値の説明を返すメソッドです。
//+------------------------------------------------------------------+ //|Return an actual controlled property value description | //+------------------------------------------------------------------+ string CPendRequest::GetActivationActualValueDescription(const uint index) const { return ( this.m_activated_control[index][4]!=EMPTY_VALUE ? ( this.m_activated_control[index][0]==PEND_REQ_ACTIVATION_SOURCE_SYMBOL && this.m_activated_control[index][1]==PEND_REQ_ACTIVATE_BY_SYMBOL_TIME ? ::TimeToString((ulong)this.m_activated_control[index][4]) : ::DoubleToString(this.m_activated_control[index][4],this.DigitsControlledValue(index)) ) : "" ); } //+------------------------------------------------------------------+
このメソッドは前のメソッドと同じですが、アクティブ化条件データ配列の2番目の次元のインデックス4によってデータが取得される点が異なります。保留中抽象リクエストの基本オブジェクトの変更はこれですべてです。
次に、抽象リクエスト基本オブジェクトの子孫オブジェクトのクラスをいくつか改善してみましょう。
ここではエラーコードによるものとリクエストによるものの2種類の保留中リクエストを実装しているため、2番目のタイプのオブジェクトは、サーバの戻りコード(ここでは常にゼロに等しい)、リクエストのアクティブ化時間(2番目のタイプのリクエストの時間は、リクエストのアクティブ化条件の1つとして指定でき、保留中取引リクエストのアクティブ化条件データ配列にあります)、待機時間(ここではまったく使用されません)、現在の試行インデックス(ここでは1つの試行が行われます。標準の取引注文が後で送信され、取引サーバーの戻りコードによって処理されます)など、一部のプロパティの存在を意味しません。
これに関して、保留中リクエスト基本オブジェクトのすべての子孫オブジェクトをオブジェクトによる整数プロパティのサポートを返すメソッドで補足し、また、操作ログで保留中リクエストのアクティブ化条件のリストを表示するメソッドの呼び出しを、各子孫オブジェクトのPrintShort()メソッドに追加します。
抽象保留中リクエスト基本オブジェクト(CPendReqOpenクラスを例として使用 )のPendReqOpen.mqh、PendReqClose.mqh、PendReqSLTP.mqh、PendReqPlace.mqh、PendReqRemove.mqh、PendReqModify.mqhファイルに次の変更を追加します。//+------------------------------------------------------------------+ //| Return 'true' if an order supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CPendReqOpen::SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property) { if( (this.GetProperty(PEND_REQ_PROP_TYPE)==PEND_REQ_TYPE_REQUEST && (property==PEND_REQ_PROP_RETCODE || property==PEND_REQ_PROP_TIME_ACTIVATE || property==PEND_REQ_PROP_WAITING || property==PEND_REQ_PROP_CURRENT_ATTEMPT ) ) || property==PEND_REQ_PROP_MQL_REQ_ORDER || property==PEND_REQ_PROP_MQL_REQ_POSITION || property==PEND_REQ_PROP_MQL_REQ_POSITION_BY || property==PEND_REQ_PROP_MQL_REQ_EXPIRATION || property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME ) return false; return true; } //+------------------------------------------------------------------+
システムは、これがリクエストによって作成されたオブジェクトであることを確認します。確認できた場合、上記のプロパティは除外されます。
//+------------------------------------------------------------------+ //| Display a brief message with request data in the journal | //+------------------------------------------------------------------+ void CPendReqOpen::PrintShort(void) { string params=this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME),this.m_digits_lot)+" "+ OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE)); string price=CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE),this.m_digits); string sl=this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL)>0 ? ", "+CMessage::Text(MSG_LIB_TEXT_REQUEST_SL)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL),this.m_digits) : ""; string tp=this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP)>0 ? ", "+CMessage::Text(MSG_LIB_TEXT_REQUEST_TP)+" "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP),this.m_digits) : ""; string time=this.IDDescription()+", "+CMessage::Text(MSG_LIB_TEXT_CREATED)+" "+TimeMSCtoString(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)); string attempts=CMessage::Text(MSG_LIB_TEXT_ATTEMPTS)+" "+(string)this.GetProperty(PEND_REQ_PROP_TOTAL); string wait=CMessage::Text(MSG_LIB_TEXT_WAIT)+" "+::TimeToString(this.GetProperty(PEND_REQ_PROP_WAITING)/1000,TIME_SECONDS); string end=CMessage::Text(MSG_LIB_TEXT_END)+" "+ TimeMSCtoString(this.GetProperty(PEND_REQ_PROP_TIME_CREATE)+this.GetProperty(PEND_REQ_PROP_WAITING)*this.GetProperty(PEND_REQ_PROP_TOTAL)); //--- string message=CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+": "+ "\n- "+params+", "+price+sl+tp+ "\n- "+time+", "+attempts+", "+wait+", "+end; ::Print(message); this.PrintActivations(); } //+------------------------------------------------------------------+
「+ end」文字列の後、テキストラッピングコード(+ "\ n")の追加を削除し、::Print(message); 文字列の後にアクティブ化条件のリストを表示するメソッドの呼び出しを追加しました。条件配列のサイズがゼロの場合(エラーコードによって作成されたオブジェクト内)、PrintActivations()はテキストの折り返しコード("\n")以外は何も出力しません。それ以外の場合、メソッドはデータ配列に書き込まれたすべての条件の完全なリストを表示します。
一部のクラスでは、操作ログの表示に関連する小さな変更が行われています。ここでは説明しません。それらは添付ファイルでご覧になれます。
取引クラスを見てみましょう。
CTrading基本取引クラスで、3つのクラスメンバ変数とGetFreeID()メソッドをprivateセクションからprotectedセクションに移動します。
private: CArrayInt m_list_errors; // Error list bool m_is_trade_disable; // Flag disabling trading bool m_use_sound; // The flag of using sounds of the object trading events uchar m_total_try; // Number of trading attempts MqlTradeRequest m_request; // Trading request prices ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags; // Flags of error source in a trading method ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Behavior when handling error //--- Add the error code to the list
これらの変数とメソッドは、子孫クラスで必要です。したがって、子孫クラスがアクセスできるように、それらはprotectedセクションに配置する必要があります(外部アクセスは無効になっているので、public セクションでは必要ありません)。クラスのprotectedセクションに、保留中リクエストIDを持つ成行注文/ポジションのフラグを返すメソッドを追加します。
その結果、クラスのprotectedセクションは次のようになります。
//+------------------------------------------------------------------+ //| Trading class | //+------------------------------------------------------------------+ class CTrading : public CBaseObj { protected: CAccount *m_account; // Pointer to the current account object CSymbolsCollection *m_symbols; // Pointer to the symbol collection list CMarketCollection *m_market; // Pointer to the list of the collection of market orders and positions CHistoryCollection *m_history; // Pointer to the list of the collection of historical orders and deals CEventsCollection *m_events; // Pointer to the event collection list CArrayObj m_list_request; // List of pending requests uchar m_total_try; // Number of trading attempts MqlTradeRequest m_request; // Trade request structure ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags; // Flags of error source in a trading method //--- Look for the first free pending request ID int GetFreeID(void); //--- Return the flag of a market order/position with a pending request ID bool IsPresentOrderByID(const uchar id); private:
privateセクションから移動した変数とメソッドおよび新しいメソッド定義は、ここで色付きで強調表示されています。
クラスのpublicセクションで、リスト内のIDによるリクエストオブジェクトへのポインタを返すメソッドの宣言および銘柄取引オブジェクトのログレベルを返すメソッドの宣言を追加します。
//--- Create a pending request bool CreatePendingRequest(const ENUM_PEND_REQ_STATUS status, const uchar id, const uchar attempts, const ulong wait, const MqlTradeRequest &request, const int retcode, CSymbol *symbol_obj, COrder *order); //--- Return (1) the pointer to the request object by its ID in the list, //--- (2) the logging level of a symbol trading object CPendRequest *GetPendRequestByID(const uchar id); ENUM_LOG_LEVEL GetTradeObjLogLevel(const string symbol_name); }; //+------------------------------------------------------------------+
これらのメソッドをクラス本体の外側で実装しましょう。
以下は、銘柄取引オブジェクトのログレベルを返すメソッドを実装です。
//+------------------------------------------------------------------+ //| Return the logging level of a symbol trading object | //+------------------------------------------------------------------+ ENUM_LOG_LEVEL CTrading::GetTradeObjLogLevel(const string symbol_name) { CTradeObj *trade_obj=this.GetTradeObjBySymbol(symbol_name,DFUN); return(trade_obj!=NULL ? trade_obj.GetLogLevel() : LOG_LEVEL_NO_MSG); } //+------------------------------------------------------------------+
このメソッドは、取引オブジェクトのログレベルを受け取る必要がある銘柄の名前を受け取ります。銘柄オブジェクトから取引オブジェクトを取得します。オブジェクトが受信された場合は、オブジェクトのログレベルを返し、それ以外の場合はログの無効ステータスを返します。
リスト内のIDによってリクエストオブジェクトへのポインタを返すメソッドを実装します。
//+------------------------------------------------------------------+ //| Return the pointer to the request object by its ID in the list | //+------------------------------------------------------------------+ CPendRequest* CTrading::GetPendRequestByID(const uchar id) { int index=this.GetIndexPendingRequestByID(id); if(index==WRONG_VALUE) return NULL; return this.m_list_request.At(index); } //+------------------------------------------------------------------+
メソッドはリクエストIDを受信し、IDによってリスト内の保留中のリクエストオブジェクトインデックスを取得します。リストにオブジェクトがない場合、NULLを返します。それ以外の場合、リストから取得したインデックスによって、リストからオブジェクトを返します。
以下は、保留中リクエストIDを使用して成行注文/ポジションのフラグを返すメソッドの実装です。
//+------------------------------------------------------------------+ //| Return the flag of a market order/position | //| with a pending request ID | //+------------------------------------------------------------------+ bool CTrading::IsPresentOrderByID(const uchar id) { CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL); return(list==NULL ? false : list.Total()!=0); } //+------------------------------------------------------------------+
メソッドは保留中リクエストIDを受け取ります。次に、保留中リクエストIDとその値で並び替えられた成行注文/ポジションのリストを受け取ります。リストが受信されないか空の場合(目的のIDの注文/ポジションがない場合)はfalseを返し、それ以外の場合はtrueを返します。
空のIDを返すメソッドにさらに別のチェックを追加しましょう。
//+------------------------------------------------------------------+ //| Look for the first free pending request ID | //+------------------------------------------------------------------+ int CTrading::GetFreeID(void) { int id=WRONG_VALUE; CPendRequest *element=new CPendRequest(); if(element==NULL) return 0; for(int i=1;i<256;i++) { element.SetID((uchar)i); this.m_list_request.Sort(SORT_BY_PEND_REQ_ID); if(this.m_list_request.Search(element)==WRONG_VALUE) { if(this.IsPresentOrderByID((uchar)i)) continue; id=i; break; } } delete element; return id; } //+------------------------------------------------------------------+
さらに別の適切なIDを持つ注文/ポジションのチェックが必要なのはなぜでしょうか。アクティブな保留中リクエストがあり、それに基づいてポジションが開かれている場合、そのリクエストは保留中リクエストのリストから削除され、そのIDは新しい保留中リクエストを作成するときに使用できるようになります。
新しい保留中リクエストを作成するとき、そのIDは現在開いているポジションで使用されているものと同じになります。新しい保留中リクエストのアクティベーション条件が満たされると、同じIDのポジションの存在が確認され(以前のIDを使用して開かれたため、存在している必要があります)、新しい保留中リクエストは削除されます。同じIDのポジションが存在するため、リクエストは実行されたとみなされます。つまり、サーバに取引注文を送信する代わりに、リクエストが削除されます。
このような状況を回避するには、2つの解決策があります。これがポジションにある同じIDの別のリクエストかどうかを定義する追加のIDを導入するか、リストに同じIDの保留中リクエストがない場合には同じIDのポジションの存在を確認することです。
2番目のオプションは、リソースをあまり消費しないように見えますが、同じIDのポジションが決済されるまで、空いているIDを使用することはできないため、制限があります。つまり、保留中リクエストIDに異なる255ポジションという厳格な上限があります。
メインの取引クラスの改善はこれで終わりです。
次に、CTradingControlメイン取引クラスの子孫であるCTradingControl取引管理クラスを完成させましょう。
前の記事で保留中リクエスト管理クラスを開発する際に、クラスタイマーでの保留中リクエストオブジェクトの処理を紹介しました。
サーバの戻りコードを使用して作成された保留中リクエストの1つのタイプを処理したため、クラスタイマーに処理コード全体を配置するだけで十分です。
今日は、プログラム要求によって作成された保留中リクエストの2番目のタイプの処理を追加します。
つまり、2つのハンドラを作成する必要があります。最初のハンドラはエラーコードによって作成されたリクエスト用で、2番目のハンドラはリクエストによって作成されたハンドラ用です。
そのため、2つの保留中リクエストオブジェクトハンドラを、処理されたリクエストのタイプで分けて紹介しますが、両方のハンドラの同じコードは別々のメソッドで作成されます。この場合、タイマーでリクエストタイプを確認し、適切なハンドラを呼び出して両方のタイプの保留中リクエストを処理するだけで済みます。
クラスの本文に必要なすべての追加を行い、それらを分析してみましょう。
//+------------------------------------------------------------------+ //| Class for managing pending trading requests | //+------------------------------------------------------------------+ class CTradingControl : public CTrading { private: //--- Set actual order/position data to a pending request object void SetOrderActualProperties(CPendRequest *req_obj,const COrder *order); //--- Handler of pending requests created (1) by error code, (2) by request void OnPReqByErrCodeHandler(CPendRequest *req_obj,const int index); void OnPReqByRequestHandler(CPendRequest *req_obj,const int index); //--- Check a pending request relevance (activated or not) bool CheckPReqRelevance(CPendRequest *req_obj,const MqlTradeRequest &request,const int index); //--- Update relevant values of controlled properties in pending request objects, void RefreshControlActualDatas(CPendRequest *req_obj,const CSymbol *symbol); //--- Return the relevant (1) account, (2) symbol, (3) event data to control activation double GetActualDataAccount(const int property); double GetActualDataSymbol(const int property,const CSymbol *symbol); double GetActualDataEvent(const int property); public: //--- Return itself CTradingControl *GetObject(void) { return &this; } //--- Timer virtual void OnTimer(void); //--- Constructor CTradingControl(); //--- (1) Create a pending request (1) to open a position, (2) to place a pending order template<typename SL,typename TP> int OpenPositionPending(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename PS,typename PL,typename SL,typename TP> int PlaceOrderPending( const ENUM_ORDER_TYPE order_type, const double volume, const string symbol, const PS price_stop, const PL price_limit=0, const SL sl=0, const TP tp=0, const ulong magic=ULONG_MAX, const string comment=NULL, const datetime expiration=0, const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Set pending request activation criteria bool SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value); }; //+------------------------------------------------------------------+
制御されたパラメータのデータを保留中リクエストオブジェクトに設定する必要があるため(以前は、リクエストの基になる注文に関連データを追加しました)、混乱を避けるために、関連する注文データをリクエストオブジェクトに設定するSetActualProperties()の名前をSetOrderActualProperties()に変更します 。
この記事では、保留中リクエストを使用したポジションのみを扱っているため、保留中リクエストを作成するメソッドは、本稿の範囲外です。
ポジションを開くための保留中リクエストを作成するメソッドを見てみましょう。
//+------------------------------------------------------------------+ //| Create a pending request for opening a position | //+------------------------------------------------------------------+ template<typename SL,typename TP> int CTradingControl::OpenPositionPending(const ENUM_POSITION_TYPE type, const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { //--- Exit if the global trading ban flag is set if(this.IsTradingDisable()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE)); return false; } //--- Set the trading request result as 'true' and the error flag as "no errors" bool res=true; this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR; ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)type; ENUM_ACTION_TYPE action=(ENUM_ACTION_TYPE)order_type; //--- Get a symbol object by a symbol name. CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(symbol); //--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false' if(symbol_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ)); return false; } //--- get a trading object from a symbol object CTradeObj *trade_obj=symbol_obj.GetTradeObj(); //--- If failed to get - write the "internal error" flag, display the message in the journal and return 'false' if(trade_obj==NULL) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ)); return false; } //--- Set the prices //--- If failed to set - write the "internal error" flag, set the error code in the return structure, //--- display the message in the journal and return 'false' if(!this.SetPrices(order_type,0,sl,tp,0,DFUN,symbol_obj)) { this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR; trade_obj.SetResultRetcode(10021); trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode())); if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(10021)); // No quotes to process the request return false; } //--- Look for the least of the possible IDs. If failed to find, return 'false' int id=this.GetFreeID(); if(id<1) { //--- No free IDs to create a pending request if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS)); return false; } //--- Write the volume, deviation, comment and filling type to the request structure this.m_request.volume=volume; this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation); this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment); this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : trade_obj.GetTypeFilling()); //--- Write pending request object ID to the magic number, add group IDs to the magic number value //--- and fill in the remaining unfilled trading request structure fields uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic); this.SetPendReqID((uchar)id,mn); if(group_id1>0) this.SetGroupID1(group_id1,mn); if(group_id2>0) this.SetGroupID2(group_id2,mn); this.m_request.magic=mn; this.m_request.action=TRADE_ACTION_DEAL; this.m_request.symbol=symbol_obj.Name(); this.m_request.type=order_type; //--- As a result of creating a pending trading request, return either its ID or -1 if unsuccessful if(this.CreatePendingRequest(PEND_REQ_STATUS_OPEN,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,NULL)) return id; return WRONG_VALUE; } //+------------------------------------------------------------------+
このメソッドは、第26部 (およびそれ以降)からの、取引サーバエラーの場合に保留中リクエストを作成してポジションを開くためのメソッドの短縮バージョンです。詳細にコメントされているため、ここで説明する意味はありません。
このメソッドは、ポジションを開くために必要なすべてのデータを受け取ります。取引リクエスト構造体フィールドが入力され、保留中リクエスト作成メソッドに送信されます。
保留中リクエストが正常に作成された場合、新しく作成された保留中リクエストのIDが返されます。それ以外の場合は-1が返されます。
計算された可能な最大待機時間は、端末での最大可能時間と現在の時間との差として</ s4>繰り返し試行間の遅延として使用されます。したがって、可能な最大存続時間が保留中リクエストに使用されます(最大31.12.3000)。
以下は、保留中リクエストのアクティブ化基準を設定するメソッドです。
//+------------------------------------------------------------------+ //| Set pending request activation criteria | //+------------------------------------------------------------------+ bool CTradingControl::SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value) { CPendRequest *req_obj=this.GetPendRequestByID(id); if(req_obj==NULL) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED)); return false; } req_obj.SetNewActivationProperties(source,property,control_value,comparer_type,actual_value); return true; } //+------------------------------------------------------------------+
メソッドは、新しいアクティブ化条件を追加する必要がある保留中リクエストのID、リクエストのアクティブ化ソース(銘柄、口座またはイベント)、アクティブ化条件、参照値、比較タイプ、およびリクエストをアクティブ化するために制御されるプロパティの実際の値を受け取ります。
次に、メソッドに渡されたIDによって保留中リクエストオブジェクトを受け取り、メソッドに渡されたパラメータを使用して、そのオブジェクトの新しいアクティベーション条件を作成します。
以下は、保留中リクエストの関連性を確認するメソッドです。
//+------------------------------------------------------------------+ //| Checking the pending request relevance | //+------------------------------------------------------------------+ bool CTradingControl::CheckPReqRelevance(CPendRequest *req_obj,const MqlTradeRequest &request,const int index) { //--- If this is a position opening or placing a pending order if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()==0) || req_obj.Action()==TRADE_ACTION_PENDING) { //--- Get the pending request ID uchar id=this.GetPendReqID((uint)request.magic); //--- Get the list of orders/positions containing the order/position with the pending request ID CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) return false; //--- If the order/position is present, the request is handled: remove it and proceed to the next (leave the method for the external loop) if(list.Total()>0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); return false; } } //--- Otherwise: full and partial position closure, removing an order, modifying order parameters and position stop orders else { CArrayObj *list=NULL; //--- if this is a position closure, including a closure by an opposite one if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()>0) || req_obj.Action()==TRADE_ACTION_CLOSE_BY) { //--- Get a position with the necessary ticket from the list of open positions list=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Position(),EQUAL); if(::CheckPointer(list)==POINTER_INVALID) return false; //--- If the market has no such position, the request is handled: remove it and proceed to the next (leave the method for the external loop) if(list.Total()==0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); return false; } //--- Otherwise, if the position still exists, this is a partial closure else { //--- Get the list of all account trading events list=this.m_events.GetList(); if(list==NULL) return false; //--- In the loop from the end of the account trading event list int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- get the next trading event CEvent *event=list.At(j); if(event==NULL) continue; //--- If this event is a partial closure or there was a partial closure when closing by an opposite one if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL || event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS) { //--- If a position ticket in a trading event coincides with the ticket in a pending trading request if(event.PositionID()==req_obj.Position()) { //--- Get a position object from the list of market positions CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(list_orders.Total()-1); if(order==NULL) break; //--- Set actual position data to the pending request object this.SetOrderActualProperties(req_obj,order); //--- If (executed request volume + unexecuted request volume) is equal to the requested volume in a pending request - //--- the request is handled: remove it and break the loop by the list of account trading events if(req_obj.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)==event.VolumeOrderExecuted()+event.VolumeOrderCurrent()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); break; } } } } //--- If a handled pending request object was removed by the trading event list in the loop, move on to the next one (leave the method for the external loop) if(::CheckPointer(req_obj)==POINTER_INVALID) return false; } } //--- If this is a modification of position stop orders if(req_obj.Action()==TRADE_ACTION_SLTP) { //--- Get the list of all account trading events list=this.m_events.GetList(); if(list==NULL) return false; //--- In the loop from the end of the account trading event list int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- get the next trading event CEvent *event=list.At(j); if(event==NULL) continue; //--- If this is a change of the position's stop orders if(event.TypeEvent()>TRADE_EVENT_MODIFY_ORDER_TP) { //--- If a position ticket in a trading event coincides with the ticket in a pending trading request if(event.PositionID()==req_obj.Position()) { //--- Get a position object from the list of market positions CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(list_orders.Total()-1); if(order==NULL) break; //--- Set actual position data to the pending request object this.SetOrderActualProperties(req_obj,order); //--- If all modifications have worked out - //--- the request is handled: remove it and break the loop by the list of account trading events if(req_obj.IsCompleted()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); break; } } } } //--- If a handled pending request object was removed by the trading event list in the loop, move on to the next one (leave the method for the external loop) if(::CheckPointer(req_obj)==POINTER_INVALID) return false; } //--- If this is a pending order removal if(req_obj.Action()==TRADE_ACTION_REMOVE) { //--- Get the list of removed pending orders from the historical list list=this.m_history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL); if(::CheckPointer(list)==POINTER_INVALID) return false; //--- Leave a single order with the necessary ticket in the list list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,req_obj.Order(),EQUAL); //--- If the order is present, the request is handled: remove it and proceed to the next (leave the method for the external loop) if(list.Total()>0) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); return false; } } //--- If this is a pending order modification if(req_obj.Action()==TRADE_ACTION_MODIFY) { //--- Get the list of all account trading events list=this.m_events.GetList(); if(list==NULL) return false; //--- In the loop from the end of the account trading event list int events_total=list.Total(); for(int j=events_total-1; j>WRONG_VALUE; j--) { //--- get the next trading event CEvent *event=list.At(j); if(event==NULL) continue; //--- If this event involves any change of modified pending order parameters if(event.TypeEvent()>TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER && event.TypeEvent()<TRADE_EVENT_MODIFY_POSITION_SL_TP) { //--- If an order ticket in a trading event coincides with the ticket in a pending trading request if(event.TicketOrderEvent()==req_obj.Order()) { //--- Get an order object from the list CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Order(),EQUAL); if(list_orders==NULL || list_orders.Total()==0) break; COrder *order=list_orders.At(0); if(order==NULL) break; //--- Set actual order data to the pending request object this.SetOrderActualProperties(req_obj,order); //--- If all modifications have worked out - //--- the request is handled: remove it and break the loop by the list of account trading events if(req_obj.IsCompleted()) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED)); this.m_list_request.Delete(index); break; } } } } } } //--- Exit if the pending request object has been removed after checking its operation (leave the method for the external loop) return(::CheckPointer(req_obj)==POINTER_INVALID ? false : true); } //+------------------------------------------------------------------+
このメソッドは、保留中リクエストの実行を確認し、実行が確認された後に削除します。このコードについては、すでに取引管理クラスタイマーのコード内で検討しました。保留中リクエストオブジェクトの処理を保留中リクエストタイプごとに2つのハンドラに分割し、そのコードは両方のハンドラーで類似しているため、別のメソッドに配置しました。各ハンドラーで呼び出されます。
以下は、エラーコードによって作成された保留中リクエストのハンドラです。
//+------------------------------------------------------------------+ //| Handler of pending requests created by error code | //+------------------------------------------------------------------+ void CTradingControl::OnPReqByErrCodeHandler(CPendRequest *req_obj,const int index) { //--- get the request structure and the symbol object a trading operation should be performed for MqlTradeRequest request=req_obj.MqlRequest(); CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol); if(symbol_obj==NULL || !symbol_obj.RefreshRates()) return; //--- Set the flag disabling trading in the terminal by two properties simultaneously //--- (the AutoTrading button in the terminal and the Allow Automated Trading option in the EA settings) //--- If any of the two properties is 'false', the flag is 'false' as well bool terminal_trade_allowed=::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); terminal_trade_allowed &=::MQLInfoInteger(MQL_TRADE_ALLOWED); //--- if the error has been caused by trading disabled on the terminal side and has been eliminated if(req_obj.Retcode()==10027 && terminal_trade_allowed) { //--- if the current attempt has not exceeded the defined number of trading attempts yet if(req_obj.CurrentAttempt()<req_obj.TotalAttempts()+1) { //--- Set the request creation time equal to its creation time minus waiting time, i.e. send the request immediately //--- Also, decrease the number of a successful attempt since during the next attempt, its number is increased, and if this is the last attempt, //--- it is not executed. However, this is related to fixing the error cause by a user, which means we need to give more time for the last attempt req_obj.SetTimeCreate(req_obj.TimeCreate()-req_obj.WaitingMSC()); req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()>0 ? req_obj.CurrentAttempt()-1 : 0)); } } //--- if the current attempt exceeds the defined number of trading attempts, //--- or the current time exceeds the waiting time of all attempts //--- remove the current request object and proceed to the next (leave the method for the external loop) if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.TotalAttempts()+1))) { if(this.m_log_level>LOG_LEVEL_NO_MSG) ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED)); this.m_list_request.Delete(index); return; } //--- Check the relevance of a pending request and exit to the external loop if the request is handled or an error occurs if(!this.CheckPReqRelevance(req_obj,request,index)) return; //--- Set the request activation time in the request object req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1)); //--- If the current time is less than the request activation time, //--- this is not the request time - move on to the next request in the list (leave the method for the external loop) if((long)symbol_obj.Time()<(long)req_obj.TimeActivate()) return; //--- Set the attempt number in the request object req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Display the number of a trading attempt in the journal if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt()+":"); req_obj.PrintShort(); } //--- Depending on the type of action performed in the trading request switch(request.action) { //--- Opening/closing a position case TRADE_ACTION_DEAL : //--- If no ticket is present in the request structure - this is opening a position if(request.position==0) this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); //--- If the ticket is present in the request structure - this is a position closure else this.ClosePosition(request.position,request.volume,request.comment,request.deviation); break; //--- Modify StopLoss/TakeProfit position case TRADE_ACTION_SLTP : this.ModifyPosition(request.position,request.sl,request.tp); break; //--- Close by an opposite one case TRADE_ACTION_CLOSE_BY : this.ClosePositionBy(request.position,request.position_by); break; //--- //--- Place a pending order case TRADE_ACTION_PENDING : this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling); break; //--- Modify a pending order case TRADE_ACTION_MODIFY : this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling); break; //--- Remove a pending order case TRADE_ACTION_REMOVE : this.DeleteOrder(request.order); break; //--- default: break; } } //+------------------------------------------------------------------+
このコードについても、すでに取引管理クラスタイマー内で検討しました。その唯一の違いは、リクエストのアクティブ化チェックの処理が適切なメソッドの呼び出しに移されていることです。
以下は、リクエストによって作成された保留中リクエストのハンドラです。
//+------------------------------------------------------------------+ //| The handler of pending requests created by request | //+------------------------------------------------------------------+ void CTradingControl::OnPReqByRequestHandler(CPendRequest *req_obj,const int index) { //--- get the request structure and the symbol object a trading operation should be performed for MqlTradeRequest request=req_obj.MqlRequest(); CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol); if(symbol_obj==NULL || !symbol_obj.RefreshRates()) return; //--- Check the relevance of a pending request and exit to the external loop if the request is handled or an error occurs if(!this.CheckPReqRelevance(req_obj,request,index)) return; //--- Update relevant data on request activation conditions this.RefreshControlActualDatas(req_obj,symbol_obj); //--- If all pending request activation conditions are met if(req_obj.IsAllComparisonCompleted()) { //--- Set the attempt number in the request object req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1)); //--- Display the request activation message in the journal if(this.m_log_level>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_REQUEST_ACTIVATED)+(string)req_obj.ID()+":"); req_obj.PrintShort(); } //--- Depending on the type of action performed in the trading request switch(request.action) { //--- Opening/closing a position case TRADE_ACTION_DEAL : //--- If no ticket is present in the request structure - this is opening a position if(request.position==0) this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling); //--- If the ticket is present in the request structure - this is a position closure else this.ClosePosition(request.position,request.volume,request.comment,request.deviation); break; //--- Modify StopLoss/TakeProfit position case TRADE_ACTION_SLTP : this.ModifyPosition(request.position,request.sl,request.tp); break; //--- Close by an opposite one case TRADE_ACTION_CLOSE_BY : this.ClosePositionBy(request.position,request.position_by); break; //--- //--- Place a pending order case TRADE_ACTION_PENDING : this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling); break; //--- Modify a pending order case TRADE_ACTION_MODIFY : this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling); break; //--- Remove a pending order case TRADE_ACTION_REMOVE : this.DeleteOrder(request.order); break; //--- default: break; } } } //+------------------------------------------------------------------+
このメソッドはアクティブ化と、取引注文の送信の瞬間(保留中リクエストのアクティブ化条件を満たしている)のみを確認するため、前のメソッドよりも少し単純です。
リクエストのアクティブ化を確認するメソッドもその中で呼び出されます。また、パラメータに設定された保留中リクエストのアクティブ化条件がトリガーされたかどうかが追跡されます。すべての条件がトリガーされた場合、取引注文がサーバに送信されます。
以下は、保留中リクエストオブジェクトの制御されたプロパティの関連する値を更新するメソッドです。
//+------------------------------------------------------------------+ //| Update relevant values of controlled properties | //| in pending request objects | //+------------------------------------------------------------------+ void CTradingControl::RefreshControlActualDatas(CPendRequest *req_obj,const CSymbol *symbol) { //--- Exit if a request object has a request type based on an error code if(req_obj.GetProperty(PEND_REQ_PROP_TYPE)==PEND_REQ_TYPE_ERROR) return; double res=EMPTY_VALUE; //--- In the loop by all request object activation conditions, uint total=req_obj.GetActivationCriterionTotal(); for(uint i=0;i<total;i++) { //--- get the activation source ENUM_PEND_REQ_ACTIVATION_SOURCE source=req_obj.GetActivationSource(i); //--- receive the current value of a controlled property double value=req_obj.GetActivationActualValue(i),actual=EMPTY_VALUE; //--- Depending on the activation source, //--- write the current value of a controlled property to the activation conditions data array switch((int)source) { //--- Account case PEND_REQ_ACTIVATION_SOURCE_ACCOUNT : actual=this.GetActualDataAccount(req_obj.GetActivationProperty(i)); req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value)); break; //--- Symbol case PEND_REQ_ACTIVATION_SOURCE_SYMBOL : actual=this.GetActualDataSymbol(req_obj.GetActivationProperty(i),symbol); req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value)); break; //--- Event case PEND_REQ_ACTIVATION_SOURCE_EVENT : actual=this.GetActualDataEvent(req_obj.GetActivationProperty(i)); req_obj.SetActivationActualValue(i,(actual!=EMPTY_VALUE ? actual : value)); break; //--- Default is EMPTY_VALUE default: break; } } } //+------------------------------------------------------------------+
このメソッドは、アクティベーション条件データの配列サイズを受け取ります。さらに、ループ内のすべての条件に沿って移動します。アクティブ化条件のソースに応じて、適切なコレクションから実際の(現在の)データを取得し、保留中リクエストオブジェクトのアクティブ化条件のデータ配列に書き戻します 。
以下は、関連する口座データを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the relevant account data to control activation | //+------------------------------------------------------------------+ double CTradingControl::GetActualDataAccount(const int property) { switch(property) { case PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE : return (double)this.m_account.Leverage(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS : return (double)this.m_account.LimitOrders(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED : return (double)this.m_account.TradeAllowed(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT : return (double)this.m_account.TradeExpert(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE : return this.m_account.Balance(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT : return this.m_account.Credit(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT : return this.m_account.Profit(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY : return this.m_account.Equity(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN : return this.m_account.Margin(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE : return this.m_account.MarginFree(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL : return this.m_account.MarginLevel(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL : return this.m_account.MarginInitial(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE : return this.m_account.MarginMaintenance(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS : return this.m_account.Assets(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_LIABILITIES : return this.m_account.Liabilities(); case PEND_REQ_ACTIVATE_BY_ACCOUNT_COMMISSION_BLOCKED : return this.m_account.ComissionBlocked(); default: return EMPTY_VALUE; } } //+------------------------------------------------------------------+
口座タイプに応じて、口座条件タイプの列挙に従って、適切な口座オブジェクトプロパティの値を返します。
以下は、関連する銘柄データを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the relevant symbol data to control activation | //+------------------------------------------------------------------+ double CTradingControl::GetActualDataSymbol(const int property,const CSymbol *symbol) { switch(property) { case PEND_REQ_ACTIVATE_BY_SYMBOL_BID : return symbol.Bid(); case PEND_REQ_ACTIVATE_BY_SYMBOL_ASK : return symbol.Ask(); case PEND_REQ_ACTIVATE_BY_SYMBOL_LAST : return symbol.Last(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS : return (double)symbol.SessionDeals(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS : return (double)symbol.SessionBuyOrders(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS : return (double)symbol.SessionSellOrders(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME : return (double)symbol.Volume(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH : return (double)symbol.VolumeHigh(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW : return (double)symbol.VolumeLow(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TIME : return (double)symbol.Time()/1000; case PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD : return symbol.Spread(); case PEND_REQ_ACTIVATE_BY_SYMBOL_START_TIME : return (double)symbol.StartTime(); case PEND_REQ_ACTIVATE_BY_SYMBOL_EXPIRATION_TIME : return (double)symbol.ExpirationTime(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_STOPS_LEVEL : return symbol.TradeStopLevel(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FREEZE_LEVEL : return symbol.TradeFreezeLevel(); case PEND_REQ_ACTIVATE_BY_SYMBOL_BIDHIGH : return symbol.BidHigh(); case PEND_REQ_ACTIVATE_BY_SYMBOL_BIDLOW : return symbol.BidLow(); case PEND_REQ_ACTIVATE_BY_SYMBOL_ASKHIGH : return symbol.AskHigh(); case PEND_REQ_ACTIVATE_BY_SYMBOL_ASKLOW : return symbol.AskLow(); case PEND_REQ_ACTIVATE_BY_SYMBOL_LASTHIGH : return symbol.LastHigh(); case PEND_REQ_ACTIVATE_BY_SYMBOL_LASTLOW : return symbol.LastLow(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME_REAL : return symbol.VolumeReal(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH_REAL : return symbol.VolumeHighReal(); case PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW_REAL : return symbol.VolumeLowReal(); case PEND_REQ_ACTIVATE_BY_SYMBOL_OPTION_STRIKE : return symbol.OptionStrike(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_ACCRUED_INTEREST : return symbol.TradeAccuredInterest(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_FACE_VALUE : return symbol.TradeFaceValue(); case PEND_REQ_ACTIVATE_BY_SYMBOL_TRADE_LIQUIDITY_RATE : return symbol.TradeLiquidityRate(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_LONG : return symbol.SwapLong(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SWAP_SHORT : return symbol.SwapShort(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_VOLUME : return symbol.SessionVolume(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_TURNOVER : return symbol.SessionTurnover(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_INTEREST : return symbol.SessionInterest(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME : return symbol.SessionBuyOrdersVolume(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME : return symbol.SessionSellOrdersVolume(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_OPEN : return symbol.SessionOpen(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_CLOSE : return symbol.SessionClose(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_AW : return symbol.SessionAW(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_SETTLEMENT : return symbol.SessionPriceSettlement(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN : return symbol.SessionPriceLimitMin(); case PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX : return symbol.SessionPriceLimitMax(); default: return EMPTY_VALUE; } } //+------------------------------------------------------------------+
口座タイプに応じて、そして口座条件タイプの列挙に応じて、適切な銘柄オブジェクトプロパティの値を返します。そのポインタがメソッドに渡されますす。
以下は、関連するイベントデータを返すメソッドです。
//+------------------------------------------------------------------+ //| Return the relevant event data to control activation | //+------------------------------------------------------------------+ double CTradingControl::GetActualDataEvent(const int property) { if(this.m_events.IsEvent()) { ENUM_TRADE_EVENT event=this.m_events.GetLastTradeEvent(); switch(property) { case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED : return event==TRADE_EVENT_POSITION_OPENED; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED : return event==TRADE_EVENT_POSITION_CLOSED; case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_PLASED : return event==TRADE_EVENT_PENDING_ORDER_PLASED; case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_REMOVED : return event==TRADE_EVENT_PENDING_ORDER_REMOVED; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CREDIT : return event==TRADE_EVENT_ACCOUNT_CREDIT; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CHARGE : return event==TRADE_EVENT_ACCOUNT_CHARGE; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_CORRECTION : return event==TRADE_EVENT_ACCOUNT_CORRECTION; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BONUS : return event==TRADE_EVENT_ACCOUNT_BONUS; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION : return event==TRADE_EVENT_ACCOUNT_COMISSION; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_DAILY : return event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_MONTHLY : return event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_DAILY : return event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY : return event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_INTEREST : return event==TRADE_EVENT_ACCOUNT_INTEREST; case PEND_REQ_ACTIVATE_BY_EVENT_BUY_CANCELLED : return event==TRADE_EVENT_BUY_CANCELLED; case PEND_REQ_ACTIVATE_BY_EVENT_SELL_CANCELLED : return event==TRADE_EVENT_SELL_CANCELLED; case PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT : return event==TRADE_EVENT_DIVIDENT; case PEND_REQ_ACTIVATE_BY_EVENT_DIVIDENT_FRANKED : return event==TRADE_EVENT_DIVIDENT_FRANKED; case PEND_REQ_ACTIVATE_BY_EVENT_TAX : return event==TRADE_EVENT_TAX; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_REFILL : return event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL; case PEND_REQ_ACTIVATE_BY_EVENT_ACCOUNT_BALANCE_WITHDRAWAL : return event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL; case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED : return event==TRADE_EVENT_PENDING_ORDER_ACTIVATED; case PEND_REQ_ACTIVATE_BY_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL : return event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_OPENED_PARTIAL : return event==TRADE_EVENT_POSITION_OPENED_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_POS : return event==TRADE_EVENT_POSITION_CLOSED_BY_POS; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_POS : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_SL : return event==TRADE_EVENT_POSITION_CLOSED_BY_SL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_BY_TP : return event==TRADE_EVENT_POSITION_CLOSED_BY_TP; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_SL : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_CLOSED_PARTIAL_BY_TP : return event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET : return event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING : return event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL : return event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_MARKET : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOLUME_ADD_BY_PENDING : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE : return event==TRADE_EVENT_MODIFY_ORDER_PRICE; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_SL; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_TP : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_PRICE_SL_TP : return event==TRADE_EVENT_MODIFY_ORDER_PRICE_SL_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL_TP : return event==TRADE_EVENT_MODIFY_ORDER_SL_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_SL : return event==TRADE_EVENT_MODIFY_ORDER_SL; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_ORDER_TP : return event==TRADE_EVENT_MODIFY_ORDER_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL_TP : return event==TRADE_EVENT_MODIFY_POSITION_SL_TP; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_SL : return event==TRADE_EVENT_MODIFY_POSITION_SL; case PEND_REQ_ACTIVATE_BY_EVENT_MODIFY_POSITION_TP : return event==TRADE_EVENT_MODIFY_POSITION_TP; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_MARKET_PARTIAL : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_VOL_ADD_BY_PENDING_PARTIAL : return event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL; case PEND_REQ_ACTIVATE_BY_EVENT_TRIGGERED_STOP_LIMIT_ORDER : return event==TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER; case PEND_REQ_ACTIVATE_BY_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL : return event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL; default: return EMPTY_VALUE; } } return EMPTY_VALUE; } //+------------------------------------------------------------------+
条件タイプおよび口座での新しいイベントの現在の存在に応じて、口座の最後のイベントを取得します。イベント条件タイプの列挙に従って、最後のイベントの同等のフラグを、保留中リクエストオブジェクトで制御されている値に戻します(発生した制御イベントのフラグを返します)。
クラスタイマーがはるかにコンパクトになりました。
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CTradingControl::OnTimer(void) { //--- In a loop by the list of pending requests int total=this.m_list_request.Total(); for(int i=total-1;i>WRONG_VALUE;i--) { //--- receive the next request object CPendRequest *req_obj=this.m_list_request.At(i); if(req_obj==NULL) continue; //--- If a request object is created by an error code, use the handler of objects created by the error code if(req_obj.TypeRequest()==PEND_REQ_TYPE_ERROR) this.OnPReqByErrCodeHandler(req_obj,i); //--- Otherwise, this is an object created by request - use the handler of objects created by request else this.OnPReqByRequestHandler(req_obj,i); } } //+------------------------------------------------------------------+
次に、クラスタイマーで、保留中リクエストオブジェクトのリストから取得したリクエストオブジェクトのタイプを確認し、そのタイプに応じて、上記で調べた保留中リクエストの対応するハンドラーを呼び出します。
これらは現時点での取引管理クラスのすべての改善です。
完全なコードは添付ファイルでご覧になれます。
CEngineライブラリ基本オブジェクトクラスのpublicセクションに追加を行います。
取引オブジェクトのログレベルを受信してライブラリベースのプログラムメッセージを取得できるようにするには、銘柄によって取引オブジェクトのログレベルを受信するメソッドを追加します。
void TradingSetTotalTry(const uchar attempts) { this.m_trading.SetTotalTry(attempts); } //--- Return the logging level of a trading class symbol trading object ENUM_LOG_LEVEL TradingGetLogLevel(const string symbol_name) { return this.m_trading.GetTradeObjLogLevel(symbol_name); } //--- Set standard sounds (symbol==NULL) for a symbol trading object, (symbol!=NULL) for trading objects of all symbols
メソッドは、GetTradeObjLogLevel() 取引管理クラスメソッドの操作結果を返します。
買いポジションおよび売りポジションを開くための保留中リクエストを作成するメソッド、新しい保留中リクエストのアクティブ化条件を設定するメソッド、 IDで保留中リクエストオブジェクトへのポインタを返すメソッドを記述します。
//--- Remove a pending order bool DeleteOrder(const ulong ticket); //--- Create a pending request (1) to open Buy and (2) Sell positions template<typename SL,typename TP> int OpenBuyPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); template<typename SL,typename TP> int OpenSellPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE); //--- Set pending request activation criteria bool SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value); //--- Return the pointer to the request object by its ID in the list CPendRequest *GetPendRequestByID(const uchar id) { return this.m_trading.GetPendRequestByID(id); } //--- Return event (1) milliseconds, (2) reason and (3) source from its 'long' value
保留中リクエストオブジェクトへのポインタを返す GetPendRequestByID()メソッドは、同名の取引管理クラスメソッド操作の結果を返します。
以下は、買いポジションを開くための保留中リクエストを作成するメソッドの実装です。
//+------------------------------------------------------------------+ //| Create a pending request for opening a Buy position | //+------------------------------------------------------------------+ template<typename SL,typename TP> int CEngine::OpenBuyPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenPositionPending(POSITION_TYPE_BUY,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling); } //+------------------------------------------------------------------+
このメソッドは、取引管理クラスのポジションを開くための保留中リクエストを作成するメソッドを呼び出します。POSITION_TYPE_BUY定数をポジションタイプとして渡します(メソッドに渡される将来のポジションの他のパラメータと共に)。
以下は、売りポジションを開くための保留中リクエストを作成するメソッドの実装です。
//+------------------------------------------------------------------+ //| Create a pending request for opening a Sell position | //+------------------------------------------------------------------+ template<typename SL,typename TP> int CEngine::OpenSellPending(const double volume, const string symbol, const ulong magic=ULONG_MAX, const SL sl=0, const TP tp=0, const uchar group_id1=0, const uchar group_id2=0, const string comment=NULL, const ulong deviation=ULONG_MAX, const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE) { return this.m_trading.OpenPositionPending(POSITION_TYPE_SELL,volume,symbol,magic,sl,tp,group_id1,group_id2,comment,deviation,type_filling); } //+------------------------------------------------------------------+
このメソッドは、取引管理クラスのポジションを開くための保留中リクエストを作成するメソッドを呼び出します。POSITION_TYPE_SELL定数をポジションタイプとして渡します(メソッドに渡される将来のポジションの他のパラメータと共に)。
保留中のリクエストオブジェクトに新しいアクティブ化条件を設定するメソッドを実装します。
//+------------------------------------------------------------------+ //| Set pending request activation criteria | //+------------------------------------------------------------------+ bool CEngine::SetNewActivationProperties(const uchar id, const ENUM_PEND_REQ_ACTIVATION_SOURCE source, const int property, const double control_value, const ENUM_COMPARER_TYPE comparer_type, const double actual_value) { return this.m_trading.SetNewActivationProperties(id,source,property,control_value,comparer_type,actual_value); } //+------------------------------------------------------------------+
このメソッドは、新しいアクティブ化条件を取引管理クラスの保留中リクエストオブジェクトに追加するメソッドを呼び出します。ライブラリの改善は今のところこれですべてです。
テスト
ポジションに対して保留中リクエストの適用をテストするには、前の記事のEAを使用して、新しいフォルダー\MQL5\Experts\TestDoEasy\Part31\にTestDoEasyPart31.mq5という名前で保存します。
条件によって保留中リクエストの動作を確認するために、テストEAの取引パネルに追加のボタンを導入します。ボタンは、P(価格条件)およびT(時間条件)としてマークされています。保留中リクエストは、 PまたはT(または両方)が押された場合に買または売をクリックすると作成されます。両方が押された場合、保留中リクエストには、価格と時間の2つのアクティブ化条件があります。
また、2つの入力を追加して、現在の価格からの距離を設定し、制御された価格と、リクエストのアクティブ化時間を設定するための現在の時間枠のバーの数を指定します。
したがって、買ボタンとPボタンが押された場合、現在の価格の下の距離は、設定から指定されたポイント数に対して、現在の価格からの距離に設定されます。この値は、保留中リクエストをトリガーするための参照値として設定されます。価格が計算された値以下の場合、保留中リクエストがアクティブ化されます。
Tボタンを押すと、現在の時間+現在の時間枠の指定された数のバーの時間として計算された時間が現在の時間に追加されます。この時間は、保留中リクエストをトリガーするための基準時間として設定されます。現在の時間が計算された時間以上になると、保留中リクエストがアクティブ化されます。PボタンとTボタンの両方がトリガーされた場合、保留中リクエストのアクティブ化のために両方の条件が同時に満たされる必要があります。
売りのポジションを開くには、管理価格は現在の価格+設定で指定されたポイント数として計算されます。保留中リクエストを有効にするには、現在の価格が、保留中リクエストを作成したときの価格を超える必要があります(売ボタンを押します)。
リクエスト作成時の現在の価格からのリクエストアクティブ化の参照価格のインデント距離と、保留中リクエストアクティブ化時間を設定するための遅延バーの数を追加します。
//--- input variables input ushort InpMagic = 123; // Magic number input double InpLots = 0.1; // Lots input uint InpStopLoss = 150; // StopLoss in points input uint InpTakeProfit = 150; // TakeProfit in points input uint InpDistance = 50; // Pending orders distance (points) input uint InpDistanceSL = 50; // StopLimit orders distance (points) input uint InpDistancePReq = 50; // Distance for Pending Request's activate (points) input uint InpBarsDelayPReq = 5; // Bars delay for Pending Request's activate (current timeframe) input uint InpSlippage = 5; // Slippage in points input uint InpSpreadMultiplier = 1; // Spread multiplier for adjusting stop-orders by StopLevel input uchar InpTotalAttempts = 5; // Number of trading attempts sinput double InpWithdrawal = 10; // Withdrawal funds (in tester) sinput uint InpButtShiftX = 0; // Buttons X shift sinput 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) sinput ENUM_SYMBOLS_MODE InpModeUsedSymbols = SYMBOLS_MODE_CURRENT; // Mode of used symbols list sinput string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURCAD,EURGBP,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) sinput bool InpUseSounds = true; // Use sounds
アクティブ化価格のインデントとバーでの遅延を格納するための適切な変数をEAグローバル変数のブロックに追加して、保留中リクエストのアクティブ化時間と、保留中リクエストボタンの状態のフラグを設定します。
//--- global variables CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool pending_buy; bool pending_buy_limit; bool pending_buy_stop; bool pending_buy_stoplimit; bool pending_close_buy; bool pending_close_buy2; bool pending_close_buy_by_sell; bool pending_sell; bool pending_sell_limit; bool pending_sell_stop; bool pending_sell_stoplimit; bool pending_close_sell; bool pending_close_sell2; bool pending_close_sell_by_buy; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string used_symbols; string array_used_symbols[]; bool testing; uchar group1; uchar group2; //+------------------------------------------------------------------+
EAのOnInit()ハンドラで、変数に正しい入力値を割り当て、保留中リクエストボタンの状態をリセットします。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ 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(); //--- Set EA global variables prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_"; testing=engine.IsTester(); 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; distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq); bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq); //--- Initialize random group numbers group1=0; group2=0; srand(GetTickCount()); //--- Initialize DoEasy library OnInitDoEasy(); //--- Check and remove remaining EA graphical objects if(IsPresentObects(prefix)) ObjectsDeleteAll(0,prefix); //--- Create the button panel if(!CreateButtons(InpButtShiftX,InpButtShiftY)) return INIT_FAILED; //--- Set trailing activation button status ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on); //--- Reset states of the buttons for working using pending requests for(int i=0;i<14;i++) { ButtonState(butt_data[i].name+"_PRICE",false); ButtonState(butt_data[i].name+"_TIME",false); } //--- Check playing a standard sound by macro substitution and a custom sound by description engine.PlaySoundByDescription(SND_OK); Sleep(600); engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2")); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
保留中リクエストのアクティブ化価格の距離については、5ポイント未満の距離は5ポイントに等しく、現在の時間枠の少なくとも1バーの最小遅延がバーの遅延に設定されます。
保留中リクエストを有効にするためのボタンは、単に非アクティブになります。これはテストEAであるため、これらのボタンはテスト目的でのみ必要です。それらのステータスを追跡する必要はありません。
パネルボタンを作成する関数で、新しいボタンの幅を格納する変数を追加します。また、別のループで保留中リクエストを有効にする新しいボタンを作成します。
//+------------------------------------------------------------------+ //| Create the buttons panel | //+------------------------------------------------------------------+ bool CreateButtons(const int shift_x=20,const int shift_y=0) { int h=18,w=82,offset=2,wpt=14; int cx=offset+shift_x+wpt*2+2,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; } } h=18; offset=2; cx=offset+shift_x; cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1; x=cx; y=cy; shift=0; for(int i=0;i<14;i++) { y=(cy-(i-(i>6 ? 7 : 0))*(h+1)); if(!ButtonCreate(butt_data[i].name+"_PRICE",((i>6 && i<11) || i>10 ? x+wpt*2+w*2+5 : x),y,wpt,h,"P",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"P\""); return false; } if(!ButtonCreate(butt_data[i].name+"_TIME",((i>6 && i<11) || i>10 ? x+wpt*2+w*2+5+wpt+1 : x+wpt+1),y,wpt,h,"T",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue))) { Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"T\""); return false; } } ChartRedraw(0); return true; } //+------------------------------------------------------------------+
ボタンの状態を設定する関数(アクティブボタンの色)で、保留中リクエストを使用した取引用のアクティブボタンの色の設定を追加します。
//+------------------------------------------------------------------+ //| Set the button status | //+------------------------------------------------------------------+ void ButtonState(const string name,const bool state) { ObjectSetInteger(0,name,OBJPROP_STATE,state); //--- Trailing activation button 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'); } //--- Buttons enabling pending requests if(StringFind(name,"_PRICE")>0 || StringFind(name,"_TIME")>0) { if(state) ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'255,220,90'); else ObjectSetInteger(0,name,OBJPROP_BGCOLOR,C'240,240,240'); } } //+------------------------------------------------------------------+
保留中リクエストを処理するためにボタンを押すことを処理するコードを、ボタンを押す機能を処理する関数に追加します。
//+------------------------------------------------------------------+ //| Handle pressing the buttons | //+------------------------------------------------------------------+ void PressButtonEvents(const string button_name) { bool comp_magic=true; // Temporary variable selecting the composite magic number with random group IDs string comment=""; //--- Convert button name into its string ID string button=StringSubstr(button_name,StringLen(prefix)); //--- Random group 1 and 2 numbers within the range of 0 - 15 group1=(uchar)Rand(); group2=(uchar)Rand(); uint magic=(comp_magic ? engine.SetCompositeMagicNumber(magic_number,group1,group2) : magic_number); //--- If the button is pressed if(ButtonState(button_name)) { //--- If the BUTT_BUY button is pressed: Open Buy position if(button==EnumToString(BUTT_BUY)) { //--- If the pending request creation buttons are not pressed, open Buy if(!pending_buy) engine.OpenBuy(lot,Symbol(),magic,stoploss,takeprofit); // No comment - the default comment is to be set //--- Otherwise, create a pending request for opening a Buy position else { int id=engine.OpenBuyPending(lot,Symbol(),magic,stoploss,takeprofit); if(id>0) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE")) { double ask=SymbolInfoDouble(NULL,SYMBOL_ASK); double control_value=NormalizeDouble(ask-distance_pending_request*SymbolInfoDouble(NULL,SYMBOL_POINT),(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS)); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_ASK,control_value,EQUAL_OR_LESS,ask); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME")) { ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } } CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } //--- If the BUTT_BUY_LIMIT button is pressed: Place BuyLimit else if(button==EnumToString(BUTT_BUY_LIMIT)) { //--- If the pending request creation buttons are not pressed, set BuyLimit if(!pending_buy_limit) engine.PlaceBuyLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyLimit","Pending BuyLimit order")); //--- Otherwise, do nothing in this version else { } } //--- If the BUTT_BUY_STOP button is pressed: Set BuyStop else if(button==EnumToString(BUTT_BUY_STOP)) { //--- If the pending request creation buttons are not pressed, set BuyStop if(!pending_buy_stop) engine.PlaceBuyStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStop","Pending BuyStop order")); //--- Otherwise, do nothing in this version else { } } //--- If the BUTT_BUY_STOP_LIMIT button is pressed: Set BuyStopLimit else if(button==EnumToString(BUTT_BUY_STOP_LIMIT)) { //--- If the pending request creation buttons are not pressed, set BuyStopLimit if(!pending_buy_stoplimit) engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStopLimit","Pending order BuyStopLimit")); //--- Otherwise, do nothing in this version else { } } //--- If the BUTT_SELL button is pressed: Open Sell position else if(button==EnumToString(BUTT_SELL)) { //--- If the pending request creation buttons are not pressed, open Sell if(!pending_sell) engine.OpenSell(lot,Symbol(),magic,stoploss,takeprofit); // No comment - the default comment is to be set //--- Otherwise, create a pending request for opening a Sell position else { int id=engine.OpenSellPending(lot,Symbol(),magic,stoploss,takeprofit); if(id>0) { //--- If the price criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE")) { double bid=SymbolInfoDouble(NULL,SYMBOL_BID); double control_value=NormalizeDouble(bid+distance_pending_request*SymbolInfoDouble(NULL,SYMBOL_POINT),(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS)); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_BID,control_value,EQUAL_OR_MORE,bid); } //--- If the time criterion is selected if(ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME")) { ulong control_time=TimeCurrent()+bars_delay_pending_request*PeriodSeconds(); engine.SetNewActivationProperties((uchar)id,PEND_REQ_ACTIVATION_SOURCE_SYMBOL,PEND_REQ_ACTIVATE_BY_SYMBOL_TIME,control_time,EQUAL_OR_MORE,TimeCurrent()); } } CPendRequest *req_obj=engine.GetPendRequestByID((uchar)id); if(req_obj==NULL) return; if(engine.TradingGetLogLevel(Symbol())>LOG_LEVEL_NO_MSG) { ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS)," #",req_obj.ID(),":"); req_obj.PrintActivations(); } } } //--- If the BUTT_SELL_LIMIT button is pressed: Set SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- If the pending request creation buttons are not pressed, set SellLimit if(!pending_sell_limit) engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellLimit","Pending SellLimit order")); //--- Otherwise, do nothing in this version else { } } //--- If the BUTT_SELL_STOP button is pressed: Set SellStop else if(button==EnumToString(BUTT_SELL_STOP)) { //--- If the pending request creation buttons are not pressed, set SellStop if(!pending_sell_stop) engine.PlaceSellStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStop","Pending SellStop order")); //--- Otherwise, do nothing in this version else { } } //--- If the BUTT_SELL_STOP_LIMIT button is pressed: Set SellStopLimit else if(button==EnumToString(BUTT_SELL_STOP_LIMIT)) { //--- If the pending request creation buttons are not pressed, set SellStopLimit if(!pending_sell_stoplimit) engine.PlaceSellStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStopLimit","Pending SellStopLimit order")); //--- Otherwise, do nothing in this version else { } } //--- If the BUTT_CLOSE_BUY button is pressed: Close Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list and for the current symbol only list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Buy position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) engine.ClosePosition((ulong)position.Ticket()); } } //--- If the BUTT_CLOSE_BUY2 button is pressed: Close the half of the Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Buy positions from the list and for the current symbol only list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); //--- Close the Buy position partially if(position!=NULL) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); } } //--- If the BUTT_CLOSE_BUY_BY_SELL button is pressed: Close Buy with the maximum profit by the opposite Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)) { //--- In case of a hedging account if(engine.IsHedge()) { CArrayObj *list_buy=NULL, *list_sell=NULL; //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); if(list==NULL) return; //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- Select only Buy positions from the list list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); if(list_buy==NULL) return; //--- Sort the list by profit considering commission and swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); //--- Select only Sell positions from the list list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); if(list_sell==NULL) return; //--- Sort the list by profit considering commission and swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE) { //--- Select the Buy position with the maximum profit COrder* position_buy=list_buy.At(index_buy); //--- Select the Sell position with the maximum profit COrder* position_sell=list_sell.At(index_sell); //--- Close the Buy position by the opposite Sell one if(position_buy!=NULL && position_sell!=NULL) engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket()); } } } //--- If the BUTT_CLOSE_SELL button is pressed: Close Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list and for the current symbol only list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { //--- Get the Sell position object and close a position by ticket COrder* position=list.At(index); if(position!=NULL) engine.ClosePosition((ulong)position.Ticket()); } } //--- If the BUTT_CLOSE_SELL2 button is pressed: Close the half of the Sell with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL2)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only Sell positions from the list and for the current symbol only list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL); if(index>WRONG_VALUE) { COrder* position=list.At(index); //--- Close the Sell position partially if(position!=NULL) engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0); } } //--- If the BUTT_CLOSE_SELL_BY_BUY button is pressed: Close Sell with the maximum profit by the opposite Buy with the maximum profit else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)) { //--- In case of a hedging account if(engine.IsHedge()) { CArrayObj *list_buy=NULL, *list_sell=NULL; //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); if(list==NULL) return; //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- Select only Sell positions from the list list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL); if(list_sell==NULL) return; //--- Sort the list by profit considering commission and swap list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Sell position with the maximum profit int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL); //--- Select only Buy positions from the list list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL); if(list_buy==NULL) return; //--- Sort the list by profit considering commission and swap list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL); //--- Get the index of the Buy position with the maximum profit int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL); if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE) { //--- Select the Sell position with the maximum profit COrder* position_sell=list_sell.At(index_sell); //--- Select the Buy position with the maximum profit COrder* position_buy=list_buy.At(index_buy); //--- Close the Sell position by the opposite Buy one if(position_sell!=NULL && position_buy!=NULL) engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket()); } } } //--- If the BUTT_CLOSE_ALL is pressed: Close all positions starting with the one with the least profit else if(button==EnumToString(BUTT_CLOSE_ALL)) { //--- Get the list of all open positions CArrayObj* list=engine.GetListMarketPosition(); //--- Select only current symbol positions from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); if(list!=NULL) { //--- Sort the list by profit considering commission and swap list.Sort(SORT_BY_ORDER_PROFIT_FULL); int total=list.Total(); //--- In the loop from the position with the least profit for(int i=0;i<total;i++) { COrder* position=list.At(i); if(position==NULL) continue; //--- close each position by its ticket engine.ClosePosition((ulong)position.Ticket()); } } } //--- If the BUTT_DELETE_PENDING button is pressed: Remove pending orders starting from the oldest one else if(button==EnumToString(BUTT_DELETE_PENDING)) { //--- Get the list of all orders CArrayObj* list=engine.GetListMarketPendings(); //--- Select only current symbol orders from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); if(list!=NULL) { //--- Sort the list by placement time list.Sort(SORT_BY_ORDER_TIME_OPEN); int total=list.Total(); //--- In a loop from an order with the longest time for(int i=total-1;i>=0;i--) { COrder* order=list.At(i); if(order==NULL) continue; //--- delete the order by its ticket engine.DeleteOrder((ulong)order.Ticket()); } } } //--- If the BUTT_PROFIT_WITHDRAWAL button is pressed: Withdraw funds from the account if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL)) { //--- If the program is launched in the tester if(MQLInfoInteger(MQL_TESTER)) { //--- Emulate funds withdrawal 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(); } //--- Wait for 1/10 of a second Sleep(100); //--- "Unpress" the button (if this is neither a trailing button, nor the buttons enabling pending requests) if(button!=EnumToString(BUTT_TRAILING_ALL) && StringFind(button,"_PRICE")<0 && StringFind(button,"_TIME")<0) ButtonState(button_name,false); //--- If the BUTT_TRAILING_ALL button or the buttons enabling pending requests are pressed else { //--- Set the active button color for the button enabling trailing if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,true); trailing_on=true; } //--- Buying //--- Set the active button color for the button enabling pending requests for opening Buy by price or time if(button==EnumToString(BUTT_BUY)+"_PRICE" || button==EnumToString(BUTT_BUY)+"_TIME") { ButtonState(button_name,true); pending_buy=true; } //--- Set the active button color for the button enabling pending requests for placing BuyLimit by price or time if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_LIMIT)+"_TIME") { ButtonState(button_name,true); pending_buy_limit=true; } //--- Set the active button color for the button enabling pending requests for placing BuyStop by price or time if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP)+"_TIME") { ButtonState(button_name,true); pending_buy_stop=true; } //--- Set the active button color for the button enabling pending requests for placing BuyStopLimit by price or time if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME") { ButtonState(button_name,true); pending_buy_stoplimit=true; } //--- Set the active button color for the button enabling pending requests for closing Buy by price or time if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY)+"_TIME") { ButtonState(button_name,true); pending_close_buy=true; } //--- Set the active button color for the button enabling pending requests for closing 1/2 Buy by price or time if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME") { ButtonState(button_name,true); pending_close_buy2=true; } //--- Set the active button color for the button enabling pending requests for closing Buy by an opposite Sell by price or time if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME") { ButtonState(button_name,true); pending_close_buy_by_sell=true; } //--- Selling //--- Set the active button color for the button enabling pending requests for opening Sell by price or time if(button==EnumToString(BUTT_SELL)+"_PRICE" || button==EnumToString(BUTT_SELL)+"_TIME") { ButtonState(button_name,true); pending_sell=true; } //--- Set the active button color for the button enabling pending requests for placing SellLimit by price or time if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_LIMIT)+"_TIME") { ButtonState(button_name,true); pending_sell_limit=true; } //--- Set the active button color for the button enabling pending requests for placing SellStop by price or time if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP)+"_TIME") { ButtonState(button_name,true); pending_sell_stop=true; } //--- Set the active button color for the button enabling pending requests for placing SellStopLimit by price or time if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME") { ButtonState(button_name,true); pending_sell_stoplimit=true; } //--- Set the active button color for the button enabling pending requests for closing Sell by price or time if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL)+"_TIME") { ButtonState(button_name,true); pending_close_sell=true; } //--- Set the active button color for the button enabling pending requests for closing 1/2 Sell by price or time if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME") { ButtonState(button_name,true); pending_close_sell2=true; } //--- Set the active button color for the button enabling pending requests for closing Sell by an opposite Buy by price or time if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME") { ButtonState(button_name,true); pending_close_sell_by_buy=true; } } //--- re-draw the chart ChartRedraw(); } //--- Return a color for the inactive buttons else { //--- trailing button if(button==EnumToString(BUTT_TRAILING_ALL)) { ButtonState(button_name,false); trailing_on=false; } //--- Buying //--- the button enabling pending requests for opening Buy by price if(button==EnumToString(BUTT_BUY)+"_PRICE") { ButtonState(button_name,false); pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME")); } //--- the button enabling pending requests for opening Buy by time if(button==EnumToString(BUTT_BUY)+"_TIME") { ButtonState(button_name,false); pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE")); } //--- the button enabling pending requests for placing BuyLimit by price if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE") { ButtonState(button_name,false); pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_TIME")); } //--- the button enabling pending requests for placing BuyLimit by time if(button==EnumToString(BUTT_BUY_LIMIT)+"_TIME") { ButtonState(button_name,false); pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_PRICE")); } //--- the button enabling pending requests for placing BuyStop by price if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE") { ButtonState(button_name,false); pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_TIME")); } //--- the button enabling pending requests for placing BuyStop by time if(button==EnumToString(BUTT_BUY_STOP)+"_TIME") { ButtonState(button_name,false); pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_PRICE")); } //--- the button enabling pending requests for placing BuyStopLimit by price if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE") { ButtonState(button_name,false); pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")); } //--- the button enabling pending requests for placing BuyStopLimit by time if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME") { ButtonState(button_name,false); pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE")); } //--- the button enabling pending requests for closing Buy by price if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE") { ButtonState(button_name,false); pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_TIME")); } //--- the button enabling pending requests for closing Buy by time if(button==EnumToString(BUTT_CLOSE_BUY)+"_TIME") { ButtonState(button_name,false); pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_PRICE")); } //--- the button enabling pending requests for closing 1/2 Buy by price if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE") { ButtonState(button_name,false); pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_TIME")); } //--- the button enabling pending requests for closing 1/2 Buy by time if(button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME") { ButtonState(button_name,false); pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_PRICE")); } //--- the button enabling pending requests for closing Buy by an opposite Sell by price if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE") { ButtonState(button_name,false); pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")); } //--- the button enabling pending requests for closing Buy by an opposite Sell by time if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME") { ButtonState(button_name,false); pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE")); } //--- Selling //--- the button enabling pending requests for opening Sell by price if(button==EnumToString(BUTT_SELL)+"_PRICE") { ButtonState(button_name,false); pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME")); } //--- the button enabling pending requests for opening Sell by time if(button==EnumToString(BUTT_SELL)+"_TIME") { ButtonState(button_name,false); pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE")); } //--- the button enabling pending requests for placing SellLimit by price if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE") { ButtonState(button_name,false); pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_TIME")); } //--- the button enabling pending requests for placing SellLimit by time if(button==EnumToString(BUTT_SELL_LIMIT)+"_TIME") { ButtonState(button_name,false); pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_PRICE")); } //--- the button enabling pending requests for placing SellStop by price if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE") { ButtonState(button_name,false); pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_TIME")); } //--- the button enabling pending requests for placing SellStop by time if(button==EnumToString(BUTT_SELL_STOP)+"_TIME") { ButtonState(button_name,false); pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_PRICE")); } //--- the button enabling pending requests for placing SellStopLimit by price if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE") { ButtonState(button_name,false); pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")); } //--- the button enabling pending requests for placing SellStopLimit by time if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME") { ButtonState(button_name,false); pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE")); } //--- the button enabling pending requests for closing Sell by price if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE") { ButtonState(button_name,false); pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_TIME")); } //--- the button enabling pending requests for closing Sell by time if(button==EnumToString(BUTT_CLOSE_SELL)+"_TIME") { ButtonState(button_name,false); pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_PRICE")); } //--- the button enabling pending requests for closing 1/2 Sell by price if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE") { ButtonState(button_name,false); pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_TIME")); } //--- the button enabling pending requests for closing 1/2 Sell by time if(button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME") { ButtonState(button_name,false); pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_PRICE")); } //--- the button enabling pending requests for closing Sell by an opposite Buy by price if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE") { ButtonState(button_name,false); pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")); } //--- the button enabling pending requests for closing Sell by an opposite Buy by time if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME") { ButtonState(button_name,false); pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE")); } //--- re-draw the chart ChartRedraw(); } } //+------------------------------------------------------------------+
関数は非常に大きいですが、コードは詳細にコメント化されており、説明は必要ありません。ご質問がある場合は、下のコメント欄でお気軽にお問い合わせください。
EAをコンパイルしましょう。デフォルトでは、保留中リクエスト価格のシフトは50ポイントに等しく、バーの遅延は5バーに等しくなります。これらの設定を変更せずに、ストラテジーテスターでEAを起動します。
保留中リクエストのアクティブ化ボタンを有効にして、価格と時間で買いポジションをオープンします。次に、保留中リクエストのアクティブ化を待ちます。
その後、保留中リクエストのアクティブ化ボタンを有効にして、売りポジションを時間だけで開き、保留中リクエストのアクティブ化を待ちます。
操作ログからわかるように、買いの保留中リクエストが生成され、アクティブ化条件が設定されています。価格と時間が指定された条件に達すると、保留中リクエストの両方がアクティブ化され、保留中リクエストオブジェクトはそれらのアクティブ化により削除されます。
次に、5バーの後にアクティブ化される保留中の販売リクエストを作成します。ポジションが開かれた後、リクエストは実行済みとして削除されます。
次の段階
次の記事では、保留中取引リクエストのコンセプトの開発を継続し、条件ごとの保留中の注文を実装します。
現在のバージョンのライブラリのすべてのファイルは、テスト用EAファイルと一緒に以下に添付されているので、テストするにはダウンロードしてください。
質問、コメント、提案はコメント欄にお願いします。
シリーズのこれまでの記事:
第1部: 概念、データ管理第2部: 過去の注文と取引のコレクション
第3部: 注文と取引のコレクション、検索と並び替え
第4部: 取引イベント概念
第5部: 取引イベントのクラスとコレクション取引イベントのプログラムへの送信
第6部: ネッティング勘定イベント
第7部: StopLimit注文発動イベント、注文およびポジション変更イベントの機能の準備
第8部: 注文とポジションの変更イベント
第9部: MQL4との互換性 - データの準備
第10部: MQL4との互換性 - ポジションオープンイベントと指値注文発動イベント
第11部: MQL4との互換性 - ポジション決済イベント
第12部: 口座オブジェクトクラスと口座オブジェクトコレクション
第13部: 口座オブジェクトイベント第14部: 銘柄オブジェクト
第15部: 銘柄オブジェクトコレクション
第16部: 銘柄コレクションイベント
第17部: ライブラリオブジェクトの相互作用
第18部:口座と他のライブラリオブジェクトの相互作用
第19部:ライブラリメッセージのクラス
第20部:プログラムリソースの作成と格納
第21部:取引クラス - 基本クロスプラットフォーム取引オブジェクト
第22部:取引クラス - 基本取引クラス、制限の検証
第23部:取引クラス - 基本取引クラス、パラメータ有効性の検証
第24部:取引クラス - 基本取引クラス、無効なパラメータの自動修正
第25部:取引クラス - 取引サーバによって返されたエラーを処理する基本取引クラス
第26部:指値取引リクエストの使用 - 初期実装(ポジションを開く)
第27部:指値取引リクエストの使用 - 指数注文
第28部:指値取引リクエストの使用 - 決済、削除、変更
第29部:指値取引リクエストの使用 - リクエストオブジェクトクラス
第30部:保留中取引リクエスト - リクエストオブジェクトの管理