Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXXI): Solicitudes comerciales pendientes - Apertura de posiciones según condiciones
Contenido
- Concepto
- Preparando los datos
- El objeto de solicitud pendiente creado según una solicitud
- Simulación
- ¿Qué es lo próximo?
Concepto
Al proyectar la funcionalidad de la biblioteca, se planeamos el concepto de comercio con la ayuda de solicitudes pendientes. Dicho concepto incluye dos variantes de trabajo: el procesamiento de errorres del servidor comercial y el envío normal de órdenes comerciales según condiciones establecidas de forma programática. En los artículos anteriores, partiendo del artículo 26, creamos paso a paso el procesamiento de errores del servidor comercial con la ayuda de solicitudes pendientes que permiten procesar el envío repetido de órdenes comerciales en los casos en que, para procesar un error, se requiere el envío repetido de una orden al servidor después de corregir los parámetros erróneos y transcurrir un cierto tiempo de espera.
A partir de este artículo, vamos a crear una funcionalidad que permita comerciar con la ayuda de solicitudes comerciales según una cierta condición.
En resumen, ¿qué nos aportará esto? Esta funcionalidad de la biblioteca permitirá al usuario crear de forma programática y autónoma las condiciones cuyo cumplimiento provocará el envío de una orden comercial al servidor.
Por ejemplo:
- Abrir una posición Buy al darse o superarse una cierta hora, y con la condición de que el precio sea menor al valor establecido (dos condiciones según los valores de las propiedades del símbolo).
- Cerrar parte de una posición al superarse el parámetro de beneficio establecido (una condición de acuerdo con el valor de la propiedad de la cuenta).
- Abrir una posición opuesta al registrarse el evento de cierre de posición por stop loss (una condición de acuerdo con el valor de la propiedad de la cuenta).
Estos son tan solo tres ejemplos sencillos. Pero las condiciones y sus posibles combinaciones pueden ser muchas. En esta etapa, vamos a desarrollar el control del cambio de propiedades de la cuenta, el símbolo y los eventos que suceden en la cuenta actual. Las condiciones de estas tres listas se podrán establecer en cualquier combinación de las mismas.
Vamos a comenzar por lo más sencillo, el control de los cambios de los valores de las propiedades del símbolo y la cuenta. El control de los eventos de la cuenta y la reacción a los mismos los trataremos más tarde.
Para que un objeto de solicitud pendiente pueda funcionar como parte de la lógica comercial (enviar órdenes comerciales según una condición), deberemos añadir a este objeto datos adicionales para guardar las condiciones de activación de la solicitud pendiente y sus métodos de control y procesamiento. El portador de estos datos será una matriz bidimensional. La primera dimensión guardará el número de la condición (o condiciones, pueden ser cuantas deseemos), mientras que la segunda guardará todos los datos de la condición cuyo número se indica en la primera dimensión: el tipo de fuente de la condición (símbolo, cuenta o evento), la propia condición (crearemos una enumeración para cada una de las fuentes), el método de comparación (>,<,==,!=,>=,<=), el valor de control de la propiedad monitoreada y su valor actual.
El control de las condiciones establecidas en los objetos de solicitudes pendientes se realizará en el temporizador de la clase de control de solicitudes pendientes, desde donde también se enviarán las solicitudes pendientes "que hayan esperado su momento" al servidor, justo después de registrarse la ejecución.
Hoy, en el marco del presente artículo, crearemos y pondremos a prueba el comercio con la ayuda de solicitudes pendientes: la apertura de posiciones según una condición. Solo tendremos dos condiciones a monitorear en el asesor de prueba: el valor del precio y el valor temporal. Las condiciones se podrán establecer tanto por separado (o bien según el valor del precio, o bien según el valor temporal), como de forma conjunta (según el valor de precio y tiempo).
Preparando los datos
Vamos a comenzar por la adición de los índices de los nuevos mensajes de la biblioteca y los textos de los mensajes que se corresponden con los índices.
Incluimos en el archivo Datas.mqh todos los índices de los mensajes necesarios:
//--- 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 }; +//+------------------------------------------------------------------+
y los textos de los mensajes que se corresponden con los índices nuevamente añadidos:
//--- 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"}, }; //+---------------------------------------------------------------------+
Dado que con un solo objeto de solicitud pendiente se procesarán las condiciones controladas de fuentes totalmente diferentes (en este caso, la cuenta, el símbolo y los eventos de la cuenta, e incluso podríamos añadir algo después), para monitorear la activación de esta condición, necesitaremos indicar la fuente de los datos, por ejemplo, cuyos parámetros queremos investigar. Y es que al monitorear los parámetros de la cuenta y el símbolo, en ellos pueden coincidir los índices de las propiedades, pero serán propiedades completamente distintas. Para que no haya ninguna confusión, indicaremos la fuente de los datos en la que se monitorean los valores de sus propiedades.
Escribimos en el archivo Defines.mqh una enumeración con las fuentes de activación de las órdenes pendientes:
+//+------------------------------------------------------------------+ //| 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 {
Y ahí mismo añadimos las enumeraciones de los posibles criterios por los que se activarán las solicitudes pendientes.
Usaremos enumeraciones aparte para los criterios de activación según las propiedades de la cuenta, el símbolo y los eventos:
+//+------------------------------------------------------------------+ //| 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) }; +//+------------------------------------------------------------------+
Los valores de las constantes de las enumeraciones se igualan a los valores de las constantes de los mensajes de texto de las propiedades correspondientes del símbolo, la cuenta y el evento. Al mostrar los mensajes en el diario, esto nos dará la posibilidad de no tener que identificar adicionalmente en los métodos de descripción de las condiciones de activación de la solicitud la pertenencia de la constante descrita a un símbolo, cuenta o evento: bastará con usar el índice de la propia constante para mostrar el mensaje.
Como resultado, usando tres enumeraciones distintas de las condiciones de activación, podremos establecer cualquier combinación de constantes a partir de las tres enumeraciones, para así componer el criterio necesario de activación de solicitudes pendientes.
Añadimos al archivo de funciones de servicio DELib.mqh la función que retorna la descripción del tipo de comparación:
+//+------------------------------------------------------------------+ //| 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 " != "; } } +//+------------------------------------------------------------------+
En muchos archivos de la biblioteca, se han sustituido las denominaciones de las constantes de las enumeraciones en las que estaban escritas las líneas "STOP_LOSS" y "TAKE_PROFIT". Ahora, las entradas de estas líneas han sido sustituidas por "SL" y "TP", respectivamente.
El objeto de solicitud pendiente creado según una solicitud
El objeto básico de la solicitud pendiente abstracta ahora se heredará del objeto básico de todos los objetos de la biblioteca.
Incluimos el archivo del objeto básico de todos los objetos de la biblioteca en el archivo de la clase CPendRequest y convertimos la clase en heredera del objeto básico:
+//+------------------------------------------------------------------+ //| PendRequest.mqh | //| Copyright 2019, MetaQuotes Software Corp. | //| https://mql5.com/es/users/artmedia70 | +//+------------------------------------------------------------------+ #property copyright "Copyright 2019, MetaQuotes Software Corp." #property link "https://mql5.com/es/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 +//+------------------------------------------------------------------+ //| Archivos de inclusión | +//+------------------------------------------------------------------+ #include <Object.mqh> #include "..\..\Services\DELib.mqh" #include "..\..\Objects\BaseObj.mqh" +//+------------------------------------------------------------------+ //| Abstract pending trading request class | +//+------------------------------------------------------------------+ class CPendRequest : public CBaseObj {
Delcaramos en la sección privada de la clase la matriz para el guardado de datos de las propiedades monitoreadas de los criterios de activación de una solicitud pendiente:
+//+------------------------------------------------------------------+ //| 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);
Y escribimos allí mismo — en la sección privada — los métodos que retornan el número mágico, el indentificador del número mágico establecido en los ajustes del asesor, y los identificadores del pimer y el segundo grupo.
Asimismo, declararemos el método para retornar la bandera de verificación exitosa de la propiedad controlada con su valor real,
el método para comparar los dos valores de la propiedad controlada, y el método que retorna el número de dígitos tras la coma para la muestra correcta de valores en el diario:
//--- 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:
Los métodos de retorno de los identificadores del número mágico y los grupos usan los métodos homónimos del objeto padre CBaseObj, del que hemos heredado ahora el objeto de solicitud pendiente abstracta básica.
En la sección pública de la clase, en el bloque con los métodos para el acceso simplificado a las propiedades del objeto de solicitud, añadimos las declaraciones de todos los métodos públicos necesarios que analizaremos más adelante:
+//+------------------------------------------------------------------+ //| 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;
El método SetTypeRequest() asigna a la propiedad "tipo de solicitud pendiente" el tipo transmitido al método. El tipo puede ser o bien "una solicitud pendiente creada según un código de error", o bien "una solicitud pendiente creada según una solicitud". El tipo de solicitud se establece en el objeto de forma automática en el constructor de la clase, dependiendo del valor del parámetro "código de error". Si el código es igual a cero, se tratará de una solicitud pendiente creada según una solicitud del programa. Así, este método no se usa ahora en ningún lugar: solo se ha creado por si necesitáramos cambiar desde fuera el tipo de solicitud pendiente de forma operativa (por ahora, no nos parece un extremo posible).
Añadimos al bloque de métodos que retornan las descripciones de las propiedades del objeto de solicitud pendiente las declaraciones de los métodos que retornan las descripciones de las propiedades controladas:
+//+------------------------------------------------------------------+ //| 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;} }; +//+------------------------------------------------------------------+
El método GetActivationCriterionTotal() retorna el tamaño de la primera dimensión de la matriz de datos con las condiciones de activación, en otras palabras, el número de condiciones de activación en el objeto de solicitud pendiente.
En el constructor de clase, asignamos a la matriz de datos con las condiciones de activación un tamaño cero en la primera dimensión:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
El tamaño de la matriz de datos con las condiciones de activación se modificará automáticamente al añadir cada nueva condición de activación.
Vamos a añadir la muestra de la lista con las condiciones de activación en el método que muestra la lista completa de datos del objeto de solicitud pendiente, tras la muestra de todas sus propiedades:
+//+------------------------------------------------------------------+ //| 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"); } +//+------------------------------------------------------------------+
Implementación del método que muestra en el diario los datos de las condiciones de activación de una solicitud pendiente:
+//+------------------------------------------------------------------+ //| 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(""); } +//+------------------------------------------------------------------+
Método para crear una nueva condición de activación de una solicitud pendiente en la matriz de datos de las condiciones de activación:
+//+------------------------------------------------------------------+ //| 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; } //+---------------------------------------------------------------------+
Transmitimos al método la fuente de datos de activación, la condición de activación, los valores controlado y real de la condición de activación, y el método de comparación.
El tamaño de la matriz de datos de las condiciones de activación aumenta en 1 y se rellenan todos los datos necesarios en la matriz con los valores transmitidos en los parámetros de entrada del método. Este método se debe usar solo para añadir una nueva condición de activación.
Para corregir en un objeto de solicitud condiciones de activación ya existentes respecto al mismo, usaremos los siguientes métodos:
//+---------------------------------------------------------------------+ //| 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; } +//+------------------------------------------------------------------+
Transmitimos al método de establecimiento de la propiedad controlada SetActivationProperty() el índice y los dos parámetros de condición: la fuente de la condición (símbolo, cuenta y evento) y la propia condición de activación (de las enumeraciones correspondientes analizadas más arriba), dado que la condición de activación consta de dos parámetros (la fuente y el tipo de cambio de la propiedad). A los demás métodos de establecimiento de los valores de activación se transmiten solo el índice y el valor.
El índice es el número de la condición de activación. Si solo hay una condición, el índice deberá ser cero. Si hay dos condiciones, el índices deberá ser 0 o 1, dependiendo de qué condición querramos cambiar, etcétera. Si se transmite un índice que se salga de los límites del tamaño de la primera dimensión de la matriz, se mostrará una entrada en el diario sobre el índice erróneo y se retornará false.
Métodos que retornan los parámetros de las condiciones de activación:
+//+------------------------------------------------------------------+ //| 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]; } +//+------------------------------------------------------------------+
Aquí todo es similar al establecimiento de las propiedades de activación, salvo que todas las propiedades de los eventos se retornan de una en una, por eso, basta con transmitir en cada método solo el índice de la condición de activación solicitada. En caso de transmitir incorrectamente el índice, en el diario se mostrará la entrada sobre el índice erróneo. Para los métodos que devuelven valores de tipo entero, se retornará -1, mientras que para los métodos que devuelven valores de tipo real, se retornará EMPTY_VALUE.
Método de comparación de dos magnitudes según el tipo de comparación establecido:
+//+------------------------------------------------------------------+ //| 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; } +//+------------------------------------------------------------------+
Transmitimos al método el valor actual de la propiedad comparada, la magnitud de control con la que se compara el valor actual y el tipo de comparación.
Dependiendo del tipo de comparación, se comparan los valores del valor actual de la propiedad con su valor de control y se retorna el resultado de la comparación.
Método que retorna la bandera de comparación exitosa de la condición de activación según su índice en la matriz de datos de las condiciones de activación:
+//+----------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
El método retorna la bandera de activación de una de las condiciones de activación de la solicitud pendiente. En el parámetro de entrada del método se transmite el índice de la condición comprobada en la matriz de datos de las condiciones de activación. La comparación se realiza con el método analizado IsCompared(), y se retorna el resultado de la comparación.
Método que retorna la bandera de comprobación exitosa de todas las condiciones de activación creadas para el objeto de solicitud:
+//+------------------------------------------------------------------+ //| 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; } //+-------------------------------------------------------------------+
Se trata de un método universal que permite comprobar si ha llegado el momento de activación de cualquier objeto de solicitud pendiente.
Aquí, en un ciclo por la primera dimensión de la matriz de datos de las condiciones de activación, con la ayuda del método IsComparisonCompleted() y como resultado de la comprobación (variable res), se añade la bandera de comprobación exitosa del ciclo de la propiedad controlada correspondiente al índice. Al finalizar el ciclo, se retorna el resultado de la comprobación de todas las acciones. Si aunque sea una de las condiciones no ha sido cumplida o la matriz de datos tiene un tamaño cero en la primera dimensión, el resultado será false.
Método que retorna el número de dígitos tras la coma para mostrar correctamente en el diario la descripción de la condición de activación:
//+-------------------------------------------------------------------+ //|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; } +//+------------------------------------------------------------------+
Comprobamos la fuente de activación en el método, y ya dependiendo de la fuente, comprobamos la condición de activación. Dependiendo de la condición de activación, se puede retornar: el número de dígitos tras la coma en el valor de la cotización del símbolo, el número de dígitos tras la coma en el valor de la divisa actual del símbolo, el número de dígitos tras la coma en el valor del lote del símbolo, o bien cero.
Método que retorna la descripción de texto de la propiedad controlada:
+//+------------------------------------------------------------------+ //| 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 : "" ); } +//+------------------------------------------------------------------+
Transmitimos al método el índice de la condición en la matriz de datos de las condiciones de activación. Obtenemos la fuente de activación según el índice, y dependiendo de esta, obtenemos las demás descripciones, a partir de las cuales componemos y después retornamos el texto final.
Método que retorna la descripción del tipo de comparación:
+//+------------------------------------------------------------------+ //| Return the comparison type description | +//+------------------------------------------------------------------+ string CPendRequest::GetActivationComparerTypeDescription(const uint index) const { return ComparisonTypeDescription((ENUM_COMPARER_TYPE)this.m_activated_control[index][2]); } +//+------------------------------------------------------------------+
Simplemente retorna la descripción de texto del tipo de comparación grabado en la matriz de datos según el índice de la condición de activación transmitido por el parámetro del método.
Método que retorna la descripción del valor de la propiedad controlada en el objeto de solicitud:
+//+------------------------------------------------------------------+ //| 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)) ) : "" ); } +//+------------------------------------------------------------------+
Al método se transmite el índice de la condición.
Se comprueba el valor de la propiedad controlada grabada en la matriz según el índice establecido, y si no es igual a "valor vacío" ( EMPTY_VALUE), se retornará la condición y su tipo. Si finalmente se comprueba la hora del símbolo, se retornará la descripción de texto de la hora,
de lo contrario, la descripción del valor de tipo entero o real con el número correcto de dígitos tras la coma.
Método que retorna la descripción del valor factual de la propiedad controlada en el objeto de solicitud:
+//+------------------------------------------------------------------+ //|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)) ) : "" ); } +//+------------------------------------------------------------------+
El método es idéntico al anterior, con la excepción de que obtenemos los datos según el índice 4 en la segunda dimensión de la matriz de datos de las condiciones de activación. Estos son todos los cambios en el objeto básico de la solicitud comercial pendiente abstracta.
Ahora, vamos a introducir algunas mejoras en las clase de los objetos herederos del objeto básico de la solicitud abstracta.
Dado que ahora hemos implementado dos tipos de órdenes pendientes (según el código de error y según la solicitud), el segundo tipo de objetos no presupone la existencia de ciertas propiedades, tales como el código de retorno del servidor (aquí siempre es cero), la hora de activación (esta hora en las solicitudes del segundo tipo se puede establecer como una de las condiciones de activación de la solicitud, y, en este caso, se encuentra en la matriz de datos con las condiciones de activación de la solicitud comercial pendiente), el tiempo de espera (aquí no se usa en absoluto) y el número del intento actual (aquí se da un intento, después ya funcionará como envío estándar de la orden comercial y su procesamiento se dará según el código de retorno del servidor comercial).
En relación con ello, introducimos una adición en todos los objetos herederos del objeto de solicitud pendiente básico, en concreto, en sus métodos encargados de retornar el soporte de propiedades de tipo entero por parte del objeto: añadimos al método PrintShort() de cada uno de los objetos herederos la llamada del método que muestra en el diario la lista de condiciones de activación de la solicitud pendiente.
Introducimos en los objetos herederos del objeto básico de la solicitud pendiente abstracta PendReqOpen.mqh, PendReqClose.mqh, PendReqSLTP.mqh, PendReqPlace.mqh, PendReqRemove.mqh y PendReqModify.mqh los mismos cambios (usando como ejemplo la clase CPendReqOpen):+//+------------------------------------------------------------------+ //| 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; } +//+------------------------------------------------------------------+
Aquí, comprobamos que sea un objeto creado según la solicitud, y si es así, excluimos las propiedades enumeradas anteriormente.
+//+------------------------------------------------------------------+ //| 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(); } +//+------------------------------------------------------------------+
Aquí, hemos quitado después de las líneas "+end" la adición del código de traslado a una nueva línea (+"\n"), y hemos añadido después de la línea ::Print(message); la llamada del método que muestra la lista desplegable con las condiciones de activación. Si la matriz con las condiciones tiene un tamaño cero (en los objetos creados según el código de error), el método PrintActivations() no imprimirá nada salvo el código de traslado a una nueva línea ("\n"). En caso contrario, el método mostrará la lista completa de todas las condiciones registradas en la matriz de datos.
En algunas de esta clases, se han introducido correcciones "cosméticas" relacionadas solo con la muestra en el diario, por eso no vamos a detenernos en ellas, el lector podrá familiarizarse con todos los cambios en los archivos anexos al artículo.
Ahora, nos ocuparemos de las clases comerciales.
En la clase comercial principal CTrading, trasladamos de la sección privada a la protegida las tres variables de miembros de clase y el método GetFreeID():
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
Estos métodos y variables son necesarios en la clase heredera, por eso, para que esta pueda tener acceso a los mismos, deberán encontrarse en la sección protegida (en la sección pública no son necesarios, el acceso a ellos desde el exterior está prohibido). Asimismo, añadimos a la sección protegida el método que retorna la bandera de presencia de la orden/posición de mercado con el identificador de la solicitud pendiente.
Como resultado, la sección protegida de la clase tiene el aspecto siguiente:
+//+------------------------------------------------------------------+ //| 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:
Aquí se destacan con colores las variables y métodos trasladados de la sección privada , y la definición del nuevo método.
En la sección privada de la clase, añadimos la declaración del método que retorna el puntero al objeto de solicitud según su identificador en la lista , y la declaración del método que retorna el nivel de logueo del objeto de comercial del símbolo:
//--- 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); }; +//+------------------------------------------------------------------+
Implementamos estos métodos fuera del cuerpo de la clase:
Implementación del método que retorna el nivel de logueo del objeto comercial del símbolo:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
Transmitimos al método el nombre del símbolo y el nivel de logueo del objeto comercial que debemos obtener; luego obtenemos el objeto comercial del objeto de símbolo, y si el objeto ha sido obtenido, retornamos el nivel de logueo de este objeto, de lo contrario, retornamos la prohibición del logueo.
Implementación del método que retorna el puntero al objeto de solicitud según un identificador en la lista:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
Transmitimos el indetificador de la solicitud al método, y después obtenemos el índice del objeto de solicitud pendiente en la lista según su identificador . Si el objeto no está la lista, retornamos NULL, de lo contrario, retornamos un objeto de la lista según su índice obtenido en la misma.
Implementación del método que retorna la bandera de presencia de una orden/posición de mercado con el identificador de la solicitud pendiente:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
Transmitimos al método el identificador de la solicitud pendiente. A continuación, obtenemos la lista de órdenes/posiciones de mercado, filtrada según el identificador de solicitud pendiente y su valor. Si no obtenemos la lista, o esta se encuentra vacía (no tiene órdenes/posiciones con el indentificador buscado), retoramos false, de lo contrario, retornamos true.
Vamos a añadir otra comprobación más, el método que retorna el número de identificador libre:
+//+------------------------------------------------------------------+ //| 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; } +//+------------------------------------------------------------------+
¿Y para qué necesitamos otra comprobación sobre la presencia en el mercado de una orden/posición con este identificador? La cosa es que si tenemos una solicitud pendiente activada, y hay, por ejemplo, una posición abierta de la misma, esta solicitud será eliminada de la lista de solicitudes pendientes, y su identificador estará disponible para el uso al crear nuevas solicitudes pendientes.
Al crear una nueva solicitud pendiente, se creará una solicitud pendiente con el mismo identificador con el que ahora mismo hay una posición abierta. Al darse las condiciones de activación de esta nueva solicitud pendiente, se comprueba que haya una posición con el mismo identificador (y la hay, abierta según ese mismo identificador en el pasado), y simplemente se elimina la nueva solicitud pendiente, considerando que, como ya existe una posición con esta ID, la solicitud ha sido procesada. Es decir, la solicitud no enviará la orden comercial al servidor, simplemente será eliminada.
Existen un par de soluciones para evitar esta situación: la primera sería identificar adicionalmente de alguna forma que se trata de otra solicitud con el mismo identificador que la posición ya abierta, o bien simplemente comprobar la presencia de una posición con esa ID cuando no existe en la lista una solicitud pendiente con la misma ID.
La segunda variante parece menos laboriosa, aunque oculta una limitación: no es posible usar un identificador con la ID libre hasta que no se cierre la posición con el mismo identificador. Nos referimos a la limitación rigurosa de 255 posiciones con diferentes identificadores de solicitudes pendientes.
Con esto, damos por finalizada la mejora de la clase comercial principal.
Ahora, vamos a mejorar la clase de control comercial CTradingControl, una clase heredera de la clase comercial principal CTrading.
Al crear la clase de control de solicitudes pendientes en el artículo anterior, implementamos el procesamiento de los objetos de solicitudes pendientes en el temporizador de la clase.
Dado que allí solo procesábamos un tipo de solicitudes pendientes, las creadas según el código de retorno del servidor, bastaría con ubicar el código de procesamiento completo en el temporizador de la clase.
En esta ocasión, vamos a añadir el procesamiento del segundo tipo de solicitudes pendientes: las creadas según la solicitud desde el programa.
Por consiguiente, necesitaremos crear dos manejadores: el primero para las solicitudes creadas según el código de error, y el segundo para las solicitudes creadas según la solicitud.
Por eso, vamos a hacer lo siguiente: crearemos dos manejadores para los objetos de solicitudes pendientes divididos según el tipo de solicitudes procesadas, y luego implementaremos un código idéntico para ambos manejadores con un método aparte. Entonces, para procesar ambos tipos de solicitudes pendientes, bastará con comprobar el tipo de solicitud en el temporizador y llamar al manejador correspondiente.
Sacamos del cuerpo de la clase todas las adiciones correspondientes, y después las analizamos:
+//+------------------------------------------------------------------+ //| 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); }; +//+------------------------------------------------------------------+
Dado que ahora tenemos la posibilidad de establecer en el objeto de solicitud pendiente los datos de los parámetros configurables, y antes, para monitorear la activación de una solicitud pendiente también incluíamos en el objeto de solicitud los datos actuales de la orden a la que corresponde esta solicitud, ahora, para evitar confusiones, renombraremos el método de establecimiento de los datos actuales de la orden en el objeto de solicitud de SetActualProperties() a SetOrderActualProperties().
Hoy realizaremos solo el trabajo de apertura de posiciones con la ayuda de solicitudes pendientes, por eso, dejaremos fuera del marco del presente artículo el método de creación de solicitudes para la colocación de una orden pendiente.
Vamos a ver el método de creación de una solicitud pendiente para la apertura de una posición:
+//+------------------------------------------------------------------+ //| 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; } +//+------------------------------------------------------------------+
El método supone una versión acortada del método para la apertura de posiciones del artículo 26 (y los artículos siguientes), que crea una solicitud pendiente si obtenemos error del servidor comercial. Ya lo hemos analizado en profundidad, por lo que no nos detendremos a comentarlo aquí.
Transmitimos al método todos los datos necesarios para la apertura de la posición, rellenamos los campos de la estructura de la solicitud comercial y los enviamos al método de creación de solicitudes pendientes.
Como resultado, al crearse con éxito una solicitud pendiente, se retornará el identificador de la solicitud pendiente nuevamente creada; en caso contrario, se retornará -1.
Como retraso entre intentos repetidos, aquí se usa el tiempo máximo posible de espera calculado como la diferencia entre el tiempo máximo posible en el terminal y la hora actual. De esta forma, el tiempo de acción de la solicitud actual será el máximo posible (hasta el 31.12.3000).
Método que establece el criterio de activación de una solicitud pendiente:
+//+------------------------------------------------------------------+ //| 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; } +//+------------------------------------------------------------------+
Al método se transmiten el identificador de la solicitud pendiente a la que se debe añadir su condición de activación, la fuente de activación de la solicitud (símbolo, cuenta o evento), la condición de activación, el valor de control, el tipo de comparación y el valor factual de la propiedad controlada para la activación de la solicitud.
A continuación, obtenemos el objeto de solicitud pendiente según el identificador transmitido al método y creamos para él una nueva condición de activación con los parámetros transmitidos al método.
Método que comprueba si la solicitud pendiente es actual:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
Este método comprueba si se ha ejecutado la solicitud pendiente y elimina esta de confirmarse dicho hecho. Ya hemos visto este código anteriormente dentro del código del temporizador de la clase de control comercial. Ahora, debido a que hemos dividido el procesamiento de los objetos de solicitudes pendientes entre dos manejadores según el tipo de solicitudes pendientes, y que dicho código es igual para ambos manejadores, hemos decidido sacar este a un método aparte que llamaremos en cada manejador.
Manejador de solicitudes pendientes creadas según el código de error:
+//+------------------------------------------------------------------+ //| 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; } } +//+------------------------------------------------------------------+
También hemos analizado este código dentro de la clase de control comercial, y solo se diferencia en que el procesamiento de la comprobación de la activación de la solicitud ahora se ha sacado a la llamada del método correspondiente.
Manejador de solicitudes pendientes creadas según la solicitud:
+//+------------------------------------------------------------------+ //| 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; } } } +//+------------------------------------------------------------------+
Este método es un poco más sencillo que el anterior, dado que para él solo se requiere la comprobación de su activación, y la comprobación de la llegada del momento del envío de la orden comercial (la activación de las condiciones de activación de la solicitud pendiente).
En este, se llama exactamente de la misma forma el método de comprobación de la activación de la solicitud. A continuación, se controla ya la activación de las condiciones de activación de la solicitud pendiente que han sido registradas en sus parámetros, y, al confirmarse que se han activado todas las condiciones, se envía la orden comercial al servidor.
Método que actualiza los valores actuales de las propiedades controladas en los objetos de solicitudes pendientes:
+//+------------------------------------------------------------------+ //| 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; } } } +//+------------------------------------------------------------------+
En el método, obtenemos el tamaño de la matriz de datos de las condiciones de activación, pasamos en un ciclo por todas las condiciones dependiendo de la fuente de las condiciones de activación, obtenemos los datos factuales (reales) de las colecciones correspondientes y los registramos de vuelta en la matriz de datos de las condiciones de activación del objeto de solicitud pendiente.
Método que retorna los datos actuales de la cuenta:
+//+------------------------------------------------------------------+ //| 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; } } +//+------------------------------------------------------------------+
Dependiendo del tipo de condición, retornamos el valor de la propiedad correspondiente del objeto de cuenta de acuerdo con la enumeración de los tipos de condiciones de la cuenta.
Método que retorna los datos actuales del símbolo:
+//+------------------------------------------------------------------+ //| 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; } } +//+------------------------------------------------------------------+
Dependiendo del tipo de condición, retornamos el valor de la propiedad correspondiente del objeto de símbolo cuyo puntero ha sido transmitido al método de acuerdo con la enumeración de los tipos de condiciones del símbolo.
Método que retorna los datos actuales del evento:
+//+------------------------------------------------------------------+ //| 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; } +//+------------------------------------------------------------------+
Dependiendo del tipo de condición, y de si se ha registrado un nuevo evento en la cuenta en este momento, obtenemos el último evento en la cuenta. Luego, de acuerdo con la enumeración de los tipos de condiciones del evento, retornamos la bandera de igualdad del último evento al valor controlado en el objeto de solicitud pendiente (retornamos la bandera de evento controlado sucedido).
El temporizador de la clase es ahora mucho más compacto:
+//+------------------------------------------------------------------+ //| Temporizador | +//+------------------------------------------------------------------+ 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); } } +//+------------------------------------------------------------------+
Ahora, en el temporizador de la clase solo comprobamos el tipo de objeto de solicitud obtenido de la lista de objetos de solicitudes pendientes, y, dependiendo de su tipo, llamamos al manejador correspondiente de solicitudes pendientes que hemos analizado anteriormente.
Sin entrar en detalles, estas son todas las mejoras de la clase de control del comercio en estos momentos.
En cualquier caso, el lector podrá estudiar el código completo en los archivos anexos al artículo.
Vamos a introducir algunas mejoras en la clase pública del objeto principal de la biblioteca CEngine.
Para tener la posibilidad de obtener los valores del nivel de logueo de los objetos comerciales para mostrar a partir de ellos los mensajes de un programa creado sobre la base de la biblioteca, vamos a añadir el método de obtención del nivel de logueo del objeto comercial según el símbolo:
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
El método retorna el resultado del funcionamiento del método de la clase de control del comercio GetTradeObjLogLevel().
Declaramos los métodos para la creación de una solicitud pendiente de apertura de una posición Buy y de apertura de una posición Sell, el método para establecer una nueva condición de activación de una solicitud pendiente, y también escribimos el método que retorna el puntero a un objeto de solicitud pendiente según su identificador:
//--- 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
El método GetPendRequestByID(), que retorna el puntero a un objeto de solicitud pendiente, retorna el resultado del funcionamiento del método de clase homónimo para el control del comercio.
Implementación del método de creación de una solicitud pendiente para la apertura de una posición Buy:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
El método llama al método de creación de una solicitud pendiente para la apertura de una posición de la clase de control del comercio que hemos visto anteriormente. Como tipo de posición abierta, transmitimos al método la constante POSITION_TYPE_BUY, así como los demás parámetros de la futura posición transmitidos al método.
Implementación del método de creación de una solicitud pendiente para la apertura de una posición Sell:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
El método llama al método de creación de una solicitud pendiente para la apertura de una posición de la clase de control del comercio que hemos visto anteriormente. Como tipo de posición abierta, transmitimos al método la constante POSITION_TYPE_SELL, así como los demás parámetros de la futura posición transmitidos al método.
Implementación del método para establecer una nueva condición de activación en un objeto de solicitud pendiente:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
El método llama al método de adición de una nueva condición de activación al objeto de solicitud pendiente de la clase de control del comercio que hemos visto anteriormente. Y estas son todas las mejoras de la biblioteca hasta el momento.
Simulación
Para simular solicitudes pendientes de apertura de posiciones, tomaremos el asesor del artículo anterior y lo guardaremos en la nueva carpeta \MQL5\Experts\TestDoEasy\ Part31\ con el nombre TestDoEasyPart31.mq5.
Para comprobar el funcionamiento de las solicitudes pendientes según las condiciones, añadiremos botones adicionales al panel comercial del asesor de prueba. Los botones estarán designados como " P" — condición según el precio, y como "T" — condición según el tiempo. Las solicitudes pendientes se crearán al pulsar sobre el botón "Buy" o " Sell", con la condición de que esté pulsado uno de los botones "P" o "T", o ambos al mismo tiempo. Si están ambos pulsados a la vez, esto significará que la solicitud pendiente tiene dos condiciones de activación: el valor de precio y el valor de tiempo.
Asimismo, añadiremos dos parámetros de entrada, para indicar la distancia del precio actual y establecer el precio controlado, y el número de barras del marco temporal actual para establecer la hora de activación de la solicitud.
De esta forma, si está pulsado el botón Buy y el botón "P" que le corresponde, se establecerá respecto al precio actual una distancia por debajo del mismo a un número de puntos igual al indicado en los ajustes, y este valor se establecerá como valor de control para la activación de la solicitud pendiente: cuando el precio sea igual o inferior al calculado, la solicitud pendiente se activará.
Si está activado el botón "T", a la hora actual se le sumará la hora calculada como hora actual + la hora del número de barras indicadas del marco temporal actual, y esta será registrada como tiempo de control para la activación de la solicitud pendiente: cuando el tiempo actual sea igual o superior al calculado, la solicitud pendiente se activará.Si pulsamos ambos botones "P" y "T", para la activación de la solicitud pendiente deberán cumplirse ambas condiciones al mismo tiempo.
Para abrir una posición Sell, el precio controlado deberá calcularse como el precio actual + el número de puntos indicado en los ajustes, es decir, para que se active una solicitud pendiente, el precio actual deberá estar por encima del precio que había al crearse la solicitud pendiente (en el momento en que se pulsó el botón "Sell").
Añadimos al bloque de parámetros del asesor la distancia de retraso del valor de control del precio de activación de la solicitud respecto al precio actual al momento de crearse la solicitud y el número de barras de retardo para establecer la hora de activación de la solicitud pendiente:
//--- 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
Añadimos al bloque de variables globales del asesor las variables correspondientes para guardar el retraso del precio de activación y el retardo en barras para establecer la hora de activación de la solicitud pendiente, así como las banderas de estado de los botones de las solicitudes pendientes:
//--- 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; +//+------------------------------------------------------------------+
En el manejador OnInit() del asesor, asiganamos a las variables los valores correctos de los parámetros de entrada y reseteamos el estado de los botones de las solicitudes pendientes:
+//+------------------------------------------------------------------+ //| 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); } +//+------------------------------------------------------------------+
Para la distancia del precio de activación de la solicitud pendiente, igualamos a cinco puntos la distancia inferior a cinco puntos, y para el retardo en barras, establecemos un retardo mínimo no inferior a una barra del marco temporal actual.
Los botones de activación del trabajo con solicitudes pendientes los reseteamos al estado inactivo: el asesor es de prueba, los botones han sido creados solo para realizar comprobaciones; no vamos a monitorear su estado para guardarlo.
En la función de creación de los botones del panel, añadimos la variable que guarda la anchura de los nuevos botones y en un nuevo ciclo, creamos nuevos botones para activar el comercio con solicitudes pendientes:
+//+------------------------------------------------------------------+ //| 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; } +//+------------------------------------------------------------------+
En la función para establecer el estado de los botones (los colores del botón activo), añadimos la indicación del color de los botones activos de comercio con solicitudes pendientes:
+//+------------------------------------------------------------------+ //| 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'); } } +//+------------------------------------------------------------------+
Asimismo, añadimos a la función de procesamiento de la pulsación de los botones los códigos de procesamiento de la pulsación de los botones para el trabajo con solicitudes pendientes:
+//+------------------------------------------------------------------+ //| 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(); } } +//+------------------------------------------------------------------+
La función es bastante voluminosa, pero el código ha sido comentado con todo detalle, y, a nuestro parecer, no requiere de aclaraciones adicionales. En cualquier caso, podrán escribir cualquier duda en los comentarios al artículo.
Vamos a compilar el asesor. Por defecto, el retraso del precio para una solicitud pendiente será de 50 puntos, mientras que el retardo en barras será igual a cinco barras. Dejamos estos ajustes sin cambios e iniciamos el asesor en el simulador de estrategias.
Activamos los botones de activación de solicitudes pendientes para la apertura de una posición Buy según el precio y la hora, después de lo cual, esperamos la activación de las solicitudes pendientes.
A continuación, activamos el botón de activación de solicitudes pendientes para la apertura de una posición Sell solo según la hora, y esperamos igualmente la activación de la solicitud pendiente:
Como podemos ver por las entradas en el diario, las solicitudes pendientes de compra son creadas y reciben sus condiciones de activación. Cuando el precio y la hora alcanzan las condiciones establecidas, ambas solicitudes pendientes se activan, y los objetos de solicitudes pendientes son eliminados a causa de su activación.
A continuación, creamos una solicitud pendiente de venta, y esta se activa al pasar cinco barras, tras lo cual, como resultado de la apertura de la posición, la solicitud es eliminada como ejecutada.
¿Qué es lo próximo?
En el próximo artículo, continuaremos desarrollando el concepto de comercio con solicitudes pendientes, y también implementaremos la colocación de órdenes pendientes según una condición.
Más abajo se adjuntan todos los archivos de la versión actual de la biblioteca y los archivos del asesor de prueba. Puede descargarlo todo y ponerlo a prueba por sí mismo.
Si tiene preguntas, observaciones o sugerencias, podrá concretarlas en los comentarios al artículo.
Artículos de esta serie:
Parte 1: Concepto y organización de datosParte 2: Colecciones de las órdenes y transacciones históricas
Parte 3: Colección de órdenes y posiciones de mercado, organización de la búsqueda
Parte 4: Eventos comerciales. Concepto
Parte 5: Clases y colección de eventos comerciales. Envío de eventos al programa.
Parte 6. Eventos en la cuenta con compensación
Parte 7. Eventos de activación de órdenes StopLimit, preparación de la funcionalidad para el registro de los eventos de modificación de órdenes y posiciones
Parte 8. Eventos de modificación de órdenes y posiciones
Parte 9. Compatibilidad con MQL4 - Preparando los datos
Parte 10. Compatibilidad con MQL4 - Eventos de apertura de posición y activación de órdenes pendientes
Parte 11. Compatibilidad con MQL4 - Eventos de cierre de posiciones
Parte 12. Implementando la clase de objeto "cuenta" y la colección de objetos de cuenta
Parte 13. Eventos del objeto "cuenta"
Parte 14. El objeto "Símbolo"
Parte 15. Colección de objetos de símbolo
Parte 16. Eventos de la colección de símbolos
Parte 17. Interactividad de los objetos de la biblioteca
Parte 18. Interactividad del objeto de cuenta y cualquier otro objeto de la biblioteca
Parte 19. Clase de mensajes de la biblioteca
Parte 20. Creación y guardado de los recursos del programa
Parte 21. Clases comerciales - El objeto comercial multiplataforma básico
Parte 22. Clases comerciales - Clase comercial principal, control de limitaciones
Parte 23. Clase comercial principal - Control de parámetros permitidos
Parte 24. Clase comercial principal - corrección automática de parámetros erróneos
Parte 25. Procesando los errores retornados por el servidor comercial
Parte 26. Trabajando con las solicitudes comerciales pendientes - primera implementación (apertura de posiciones)
Parte 27. Trabajando con las solicitudes comerciales pendientes - Colocación de órdenes pendientes
Parte 28. Trabajando con las solicitudes comerciales pendientes - Cierre, eliminación y modificación
Parte 29. Solicitudes comerciales pendientes - Clases de objetos de solicitudes
Parte 30. Solicitudes comerciales pendientes - Control de los objetos de solicitudes
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/7521
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso