Órdenes, Posiciones y Transacciones en MetaTrader 5
Términos de Trading
El objetivo final de un comerciante es obtener beneficios a través de operaciones comerciales en los mercados financieros. Este artículo describe los términos y procedimientos de la plataforma de trading MetaTrader 5. Es necesario saber todo esto para un entendimiento correcto del trabajo de las funciones de trading del lenguaje MQL5.
- Las Órdenes — son las solicitudes de operaciones de comercio, que se reciben el el servidor de trading, formuladas en cumplimiento con los requisitos de la plataforma MetaTrader 5. Si la solicitud es incorrecta, no aparecerá en la plataforma de trading en forma de orden. Las órdenes pueden ser de ejecución inmediata, como por ejemplo comprar o vender un determinado volumen al precio de mercado actual en un instrumento financiero especificado. Otro tipo de órdenes son las órdenes pendientes, que contienen una orden para realizar una operación de trading cuando se cumple una determinada condición. Las órdenes pendientes pueden contener también un límite temporal en sus acciones: la fecha de caducidad de la orden.
Las órdenes pendientes que están esperando a que se cumpla la condición para su ejecución o cancelación se muestran en la pestaña "Trade" ("comercio") en el terminal. Estas órdenes se pueden modificar o cancelar. La realización, cancelación y modificación de órdenes se hace usando la funciónOrderSend(). Al cancelarse, expirar o ejecutarse una orden, esta pasa al historial de órdenes. Las órdenes ejecutadas y canceladas se muestran en la pestaña "Historial" del terminal de cliente. Las órdenes del historial no se pueden modificar.
-
Las Transacciones - son el resultado de la ejecución de una orden (un comando para realizar la transacción). Cada transacción se basa en una orden particular, pero una sola orden puede generar varias transacciones. Por ejemplo, una orden de comprar 10 lotes se puede realizar con varias transacciones sucesivas con ejecución parcial. Las transacciones se almacenan siempre en el historial de trading y no se pueden modificar. En el terminal, las transacciones se muestran en la pestaña "Historial".
-
Las Posiciones son los contratos de compra o venta en un instrumento financiero. Una posición larga (Long) se forma como resultado de compras en anticipación de un incremento de precio; una posición corta (Short) es el resultado de la venta de un bien en anticipación de una futura caída del precio. Para cada cuenta, para cada instrumento financiero, solo puede haber una posición. Para cada símbolo en cada momento solo puede haber una posición abierta, larga o corta.
El volumen de posición puede aumentar como resultado de una nueva operación de trading en la misma dirección. Esto significa que el volumen de las posiciones largas se incrementará después de la nueva compra (Transacción de compra), y se reducirá tras la venta (Transacción de venta). La posición se cierra si el volumen de compromisos es igual a cero como resultado de una operación de trading. Tal operación se llama "cerrar la posición".
Cómo el terminal recibe y almacena la información de trading del servidor
El terminal almacena el historial en una base especial, y recibe solo el historial que falta de las transacciones y órdenes completadas en la cuenta de trading en cada conexión al servidor de trading. Esto se hace para ahorrar tráfico. Al cerrar el terminal de cliente MetaTrader 5 o cambiar la cuenta activa actual, el historial entero se graba en el disco duro y se leerá en la próxima ejecución del terminal.
Todas las bases de datos se guardan en el disco de forma encriptada, y la clave del cifrado depende del ordenador en el que se haya instalado el terminal. Esto protege al usuario del terminal contra el acceso no autorizado a sus datos, en caso de copia.
Durante la conexión a la cuenta, el terminal carga la cuenta base guardada con el historial de la cuenta, y envía una solicitud al servidor de trading para sincronizar su propia base de datos de historia con el historial de la cuenta en el servidor de trading. Además, tras una conexión exitosa a la cuenta, el servidor de trading envía al terminal un informe sobre los eventos de trading actuales relativos a esta cuenta.
Los eventos de trading son los siguientes cambios en la cuenta:
- retiradas y operaciones de saldo;
- cambio de comisiones, trueques e impuestos;
- realización, eliminación y modificación de órdenes;
- la ejecución de transacciones basadas en órdenes;
- apertura y cierre de posiciones;
- cambio en el volumen y dirección de posiciones.
En caso de interrupción de la conexión con el servidor de trading, el terminal intentará reconectarse periódicamente. Tras la reconexión con el servidor, el terminal solicita todos los cambios recientes en el historial de trading para mantener la integridad de los datos en su propia base de datos del historial.
El historial de trading, que se muestra en la pestaña "Historial" del terminal, se toma de la base del historial del terminal, y los cambios del período que se muestran en el historial del terminal solo pueden incrementar el alcance del historial, almacenado en esta base de datos. Reducir el período del historial mostrado no lleva a una eliminación física del historial en la base del servidor.
Esto significa que la instalación de un intervalo más corto del historial expuesto no reduce la profundidad del historial de trading guardado. Pero si especificamos que queremos ver un alcance de intervalo más amplio en la pestaña "Historial", tal acción podría llevar a una solicitud del servidor de trading de un historial más profundo, si el historial propio del servidor todavía no tiene los datos solicitados para ese período.
El esquema general de interacción entre el terminal y el servidor de trading del MetaTrader 5 se muestra en la siguiente Figura:
El terminal de cliente envía una solicitud de sincronización a su propia base de historial de trading durante el inicio del terminal, durante la reconexión con el servidor tras un fallo de conexión, durante un cambio de una cuenta a otra, y durante la solicitud directa del historial de trading que falta.
A cambio, el servidor de trading envía un mensaje al cliente, independientemente y sin solicitudes del terminal, sobre los eventos de trading que tienen lugar en la cuenta: los cambios de estado de órdenes y posiciones, la realización de transacciones basadas en órdenes, el cambio de comisiones, el saldo y retirada de efectivo, etcétera.
Acceso al historial de trading del programa MQL5
El terminal puede operar simultáneamente con un número de indicadores, scripts, y EAs, y todos estos programas pueden solicitar la información que necesitan sobre trading: órdenes, transacciones y posiciones. El trabajo directo del programa MQL5 con la base de datos del terminal está excluido a causa de las consideraciones generales de estabilidad, seguridad y rendimiento.
Cada programa MQL5 recibe a peticiónpor su trabajo un "modelo" del entorno de trading en su caché. Una caché es un área especial de memoria para un acceso rápido a la información. Por ejemplo, antes de comenzar a procesar una orden, la orden debe obtenerse en la caché del programa MQL5. Todo el trabajo subsiguiente, cuando hablamos de la orden, se hará en la copia de caché de esa orden.
El trabajo con las posiciones, transacciones y órdenes del historial se lleva a cabo de la misma forma. El esquema general de obtención de la información de trading del programa MQL5 se puestra en esta Figura:
Antes de que los datos sobre el historial de trading se hagan accesible para el procesamiento del programa MQL5, se deben solicitar desde la base de datos del terminal. Tras la solicitud, los datos obtenidos se colocarán en su propia caché del programa MQL5.
Puede que haya consecuencias si la caché se usa de modo inapropiado.
- Si no fuera posible recibir los datos solicitados, la caché quedaría vacía y no contendría los datos necesarios.
- Si los datos en la caché requieren actualizaciones pero no se solicitó una actualización, entonces, trabajar con estos datos puede tener resultados impredecibles. Por ejemplo, los datos en la posición actual no se han actualizado, y el programa no sabe nada sobre la posición abierta para tal símbolo y sobre la creciente pérdida que supone.
La función para trabajar con la caché
El historial de trading puede contener miles de órdenes y transacciones ejecutadas que no se necesitan para el trabajo actual en el programa MQL5. Por tanto, el trabajo con caché se basa en el principio de solicitudes. La caché siempre contiene la información que se subió en la última conexión a la base de datos del terminal. Si necesita obtener el historial entero de órdenes y transacciones, debe solicitarlo explícitamente especificando el intervalo deseado.
Para cada tipo de información, se forma una caché independiente. Los datos sobre las órdenes se guardan en la caché de la orden; la información sobre las posiciones se guarda en la caché de la posición; los datos sobre transacciones y órdenes se guardan en las respectivas instancias del historial de la caché.
Antes de solicitar la información de la caché, esta debe llernarse.
Las funciones de trading se pueden separar en dos categorías: las funciones para llenar la caché y las funciones para leer la información de la caché.
La función para llenar la caché
Para procesar el historial de trading, primero se debe obtener y situar en la caché correcta. Las funciones que forman una caché se pueden dividir en dos subgrupos.
La función para llenar la caché (órdenes y posiciones activas):
- La función OrderSelect(ticket) - copia la orden activa por su ticket (de la base del terminal) en la caché de la orden actual para la solicitud subsiguiente de sus propiedades usando las funciones OrderGetDouble(), OrderGetInteger() y OrderGetString();
- La función OrderGetTicket(index) - copia la base del terminal de órdenes de la base del terminal de la orden activa por su índice en la lista de órdenes en la caché de las órdenes actuales para la subsiguiente solicitud de las propiedades usando las funciones OrderGetDouble(), OrderGetInteger() y OrderGetString(). El número total de órdenes en la base del terminal se puede obtener con la función OrdersTotal();
- La función PositionSelect(symbol) - copia la posición abierta por el nombre del símbolo (de la base del terminal) en la caché para la subsiguiente solicitud de las propiedades usando las funciones PositionGetDouble(), PositionGetInteger() y PositionGetString();
- La función PositionGetSymbol(index) - copia la posición abierta por su índice en la lista de posiciones (de la base del terminal) de la base del terminal en la caché para la subsiguiente solicitud de las propiedades usando las funciones PositionGetDouble(), PositionGetInteger() y PositionGetString(). El número total de posiciones en la base del terminal se puede obtener con la función PositionsTotal().
La función para llenar la caché del historial:
- La función HistoryOrderSelect(ticket) - copia el historial de órdenes por su ticket en la caché del historial de órdenes (de la base del terminal) para la subsiguiente solicitud de las propiedades con las funciones HistoryOrderGetDouble(), HistoryOrderGetInteger() y HistoryOrderGetString();
- La función HistoryDealSelect(ticket) - copia la transacción por su ticket en la caché de transacciones (de la base del terminal) para la subsiguiente solicitud de las propiedades con las funciones HistoryDealGetDouble(), HistoryDealGetInteger() y HistoryDealGetString();
Debemos considerar por separado las dos funciones que afectan el historial de trading en general accesible en la caché.
- La función HistorySelect(start, end) - rellena la caché del historial con transacciones y órdenes para el intervalo especificado del tiempo del servidor. De los resultados de la ejecución de esta función dependen los valores que devolverán las funciones HistoryDealsTotal() y HistoryOrdersTotal();
- La función HistorySelectByPosition (position_ID) - llena la caché del historial con transacciones y órdenes con la posición del identificador especificada. El resultado de la ejecución de esta función también afecta a las funciones HistoryDealsTotal() and HistoryOrdersTotal().
OrderSelect y OrderGetTicket
Las funciones generales OrderSelect(ticket) y OrderGetTicket() funcionan de la misma manera: llenan la caché con órdenes activas con una sola orden. La función OrderSelect(ticket) sirve para los casos en los que el ticket de una orden se conoce de antemano. La función OrderGetTicket(), en conjunción con OrdersTotal(), permite la revisión de todas las órdenes disponibles en la base de órdenes del terminal.
Tras una llamada a cualquiera de estas funcione, la caché de las órdenes activas contendrá la información solo de una orden, si la orden se seleccionó correctamente. De lo contrario, no habrá nada en la caché de órdenes activas. El resultado de la ejecución de la función OrdersTotal() no cambia: siempre devuelve el número real de órdenes activas en la base del terminal, independientemente de si la caché está llena o no.
PositionSelect y PositionGetSymbol
Al igual que con las órdenes, estas dos funciones también funcionan de forma similar para las posiciones: llenan la caché de posiciones con una sola posición. La función PositionGetSymbol(index) requiere el número en la lista de la base de posiciones, como parámetro, y la función PositionSelect(symbol) llena la caché basándose en el nombre del símbolo en el que se abrió la posición. El nombre del símbolo, a cambio, se puede obtener con la función PositionGetSymbol(index).
Tras ejecutar cualquiera de estas funciones, la caché de posiciones contendrá datos solo de una posición, si la función se ejecutó corrrectamente. De lo contrario, no habrá nada en la caché de posiciones. El resultado de la ejecución de la función PositionsTotal() no depende de si la caché está llena o no: siempre devuelve el número real de posiciones abiertas en la base del terminal para todos los símbolos.
HistoryOrderSelect
La función HistoryOrderSelect(ticket) elige en la caché la orden del historial de la base del terminal por su ticket. La función sirve para los casos en los que el ticket de la orden solicitada se conoce de antemano.
Si la ejecución es correcta, la caché contendrá una sola orden, y la función HistoryOrdersTotal() devolverá una sola unidad. De lo contrario, la caché de órdenes del historial quedará vacía, y la función HistoryOrdersTotal() devolverá cero.
HistoryDealSelect
La función HistoryDealSelect(ticket) selecciona la transacción de la base del terminal por su ticket. La función sirve para los casos en los que el ticket de la transacción se conoce de antemano.
Si la ejecución es correcta, la caché contendrá una sola transacción, y la función HistoryDealsTotal() devolverá 1. De lo contrario, la caché de transacciones quedará vacía, y la función HistoryDealsTotal() devolverá cero.
La función para obtener información de la caché
Antes de solicitar información sobre las propiedades de la posición, transacción u orden, es necesario actualizar la caché del programa MQL5 correspondiente. Esto es así porque la información solicitada puede haber sido actualizada antes, y esto significa que la copia guardada en la caché ya está obsoleta.
- Órdenes
Para obtener información en órdenes activas, se deben copiar primero en la cache de órdenes activas de una de las dos funciones: OrderGetTicket() o OrderSelect(). Los valores de propiedad se darán para la orden guardada en la caché cuando se llama a las funciones correspondientes: - OrderGetDouble(type_property)
- OrderGetInteger(type_property)
- OrderGetString(type_property)
Estas funciones obtienen todos los datos de la caché, y por tanto, para garantizar la obtención de datos precisos para la orden, se recomienda llamar a la función que llena la caché.
- Posiciones
Para obtener la información sobre una posición, debe estar seleccionada de antemano y copiada en la caché, usando una de las dos funciones: PositionGetSymbol o PositionSelect. Los valores de propiedad salen de esta caché cuando se llama a las funciones correspondientes:
- PositionGetDouble(type_property)
- PositionGetInteger(type_property)
- PositionGetString(type_property)
Puesto que estas funciones reciben todos sus datos de la caché, para garantizar la obtención de datos precisos para la orden, se recomienda llamar a la función que llena la caché de posiciones.<segment 1718>
- Órdenes de historial
Para obtener información sobre una orden del historial, se debe crear primero la caché de órdenes de historial usando una de estas tres funciones: HistorySelect(start, end), HistorySelectByPosition() o HistoryOrderSelect(ticket). Si la implementación tiene éxito, la caché almacenará el número de órdenes devueltas por la función HistoryOrdersTotal(). El acceso a las propiedades de estas órdenes se lleva a cabo con cada elemento del ticket usando la función apropiada:
- HistoryOrderGetDouble(ticket_order, type_property)
- HistoryOrderGetInteger(ticket_order, type_property)
- HistoryOrderGetString(ticket_order, type_property)
El ticket de la orden del historial se puede encontrar usando la función HistoryOrderGetTicket(index) por su índice en la caché de órdenes del historial. Para tener un resguardo garantizado de datos correctos en la orden, se recomienda llamar a la función que llena la caché de órdenes del historial.
- Transacciones
Para obtener información sobre una transacción concreta en el historial, se necesita crear primero la caché de transacciones usando una de estas tres funciones: HistorySelect (start, end), HistorySelectByPosition() o HistoryDealSelect (ticket). Si la implementación de la función tiene éxito, la caché almacenará la transacción en la cantidad devuelta por la función HistoryDealsTotal(). El acceso a las propiedades de estas transacciones se lleva a cabo usando las funciones apropiadas, basándose en el ticket:
- HistoryDealGetDouble(ticket_deals, type_property)
- HistoryDealGetInteger(ticket_deals, type_property)
- HistoryDealGetString(ticket_deals, type_property)
El ticket de las transacciones se puede obtener usando la función HistoryDealGetTicket(index) por su índice en la caché de transacciones. Para tener un resguardo garantizado de datos correctos sobre la transacción, se recomienda llamar a la función que llena la caché de transacciones.
La función para obtener el ticket del historial de la caché
La función HistoryOrderGetTicket (index) devuelve el ticket de la orden del historial por su índice de la caché de órdenes de historial (¡no de la base del terminal!). El ticket obtenido se puede usar en la función HistoryOrderSelect (ticket), que limpia la caché y la rellena solo con una orden, en caso de que tenga éxito. Recuerde que el valor devuelto por la función HistoryOrdersTotal() depende del número de órdenes en la caché.
La función HistoryDealGetTicket(index) devuelve el ticket de la transacción por su índice de la caché de transacciones. El ticket de la transacción se puede usar en la función HistoryDealSelect(ticket), que limpia la caché y la rellena solo con <b6>una </b6>transacción, en caso de que tenga éxito. El valor devuelto por la función HistoryDealsTotal() depende del número de transacciones en la caché.
Obtener información usando órdenes activas
Revisar las órdenes activas actuales es un procedimiento estándar. Si se necesita obtener información sobre una orden específica, se puede hacer con la función OrderSelect(ticket) si se conoce su ticket.
bool selected=OrderSelect(ticket); if(selected) { double price_open=OrderGetDouble(ORDER_PRICE_OPEN); datetime time_setup=OrderGetInteger(ORDER_TIME_SETUP); string symbol=OrderGetString(ORDER_SYMBOL); PrintFormat("Ордер #%d for %s was set at %s",ticket,symbol,TimeToString(time_setup)); } else { PrintFormat("Error al seleccionar la orden con el ticket %d. Error %d",ticket, GetLastError()); }
En el ejemplo de arriba, se entiende que el ticket de la orden se conoce de antemano, y se obtiene con la variable global. En casos generales, no obstante, no dispondremos de la información del ticket, y por tanto debemos recurrir a la ayuda de la función OrderGetTicket(index), que también selecciona una orden y la coloca en la caché. Pero solo se debe especificar como parámetro el número de orden en la lista de órdenes actuales.
El algoritmo general para trabajar con órdenes (análogamente con transacciones y posiciones) es el siguiente:
- Obtener el número total de órdenes con la función OrdersTotal();
- Organizar el bucle a través de una búsqueda de todas las órdenes por sus índices en la lista;
- Copiar las órdenes una a una en la caché usando la función OrderGetTicket();
- Obtener los datos correctos de la caché usando las funciones OrderGetDouble(), OrderGetInteger(), y OrderGetString(). Si es necesario, analice los datos obtenidos y tome las medidas apropiadas.
Este es un breve ejemplo de tal algoritmo:
input long my_magic=555; //+------------------------------------------------------------------+ //| Función de inicio de programa de Script | //+------------------------------------------------------------------+ void OnStart() { //--- obtener el número total de órdenes int orders=OrdersTotal(); //--- revisar la lista de órdenes for(int i=0;i<orders;i++) { ResetLastError(); //--- copiar en la caché la orden por su número en la lista ulong ticket=OrderGetTicket(i); if(ticket!=0)// si la orden se copió en la caché con éxito, trabaje con ella { double price_open =OrderGetDouble(ORDER_PRICE_OPEN); datetime time_setup=OrderGetInteger(ORDER_TIME_SETUP); string symbol =OrderGetString(ORDER_SYMBOL); long magic_number =OrderGetInteger(ORDER_MAGIC); if(magic_number==my_magic) { // procesar la orden con el ORDER_MAGIC especificado } PrintFormat("La orden #%d para %s se ejecuto %s, ORDER_MAGIC=%d",ticket,symbol,TimeToString(time_setup),magic_number); } else // la llamada a OrderGetTicket() se completó sin éxito { PrintFormat("Error al obtener una orden de la lista de la caché. Error code: %d",GetLastError()); } } }
Obtener información sobre posiciones abiertas
El monitoreo constante de posiciones abiertas no es solo un proceso estándar, sino que es recomendable implementarlo en cada instancia. Para obtener información sobre posiciones específicas, es suficiente saber el nombre del instrumento por el cual se abrió. Para hacer esto, use la función PositionSelect(symbol). Para los casos en los que el EA funciona solo en un símbolo (en el símbolo del gráfico al que está adjunto), el nombre del símbolo se pude obtener con una función Symbol() o de una variable predefinida _Symbol.
//--- buscaremos la posición por el símbolo del gráfico en el que trabaja el EA string symbol=Symbol(); //--- intentar obtener la posición bool selected=PositionSelect(symbol); if(selected) // si la posición está seleccionada { long pos_id =PositionGetInteger(POSITION_IDENTIFIER); double price =PositionGetDouble(POSITION_PRICE_OPEN); ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); long pos_magic =PositionGetInteger(POSITION_MAGIC); string comment =PositionGetString(POSITION_COMMENT); PrintFormat("Posición #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s", pos_id, symbol, pos_magic, price,EnumToString(type), comment); } else // si no se tuvo éxito al seleccionar la posición { PrintFormat("La selección de la posición por el símbolo %s no tuvo éxito. Error",symbol,GetLastError()); } }
En casos generales, la información sobre el símbolo se puede obtener usando la función PositionGetSymbol (index), que selecciona una posición y la coloca en la caché. Como parámetro, es necesario especificar el índice de posición en la lista de posiciones abiertas. Esto se puede hacer mejor a través de una búsqueda de todas las posiciones en el bucle.
El algoritmo general para las posiciones abiertas:
- Obtener el número total de posiciones con la función PositionsTotal();
- Organizar el bucle a través de una búsqueda de todas las posiciones por sus índices en la lista;
- Copiar las posiciones una por una en la caché usando la función PositionGetSymbol();
- Obtener los datos sobre posición requeridos de la caché usando las funciones PositionGetDouble(), PositionGetInteger(), y PositionGetString(). Si es necesario, analice los datos obtenidos y tome las medidas apropiadas.
Un ejemplo de tal algoritmo:
#property script_show_inputs input long my_magic=555; //+------------------------------------------------------------------+ //| Función de inicio de programa de Script | //+------------------------------------------------------------------+ void OnStart() { //--- obtener el número total de posiciones int positions=PositionsTotal(); //--- revisar la lista de posiciones for(int i=0;i<positions;i++) { ResetLastError(); //--- copiar en la caché la posición por su número en la lista string symbol=PositionGetSymbol(i); // obtener el nombre del símbolo con el que abrió la posición if(symbol!="") // la orden se copió en la caché con éxito, trabaje con ella { long pos_id =PositionGetInteger(POSITION_IDENTIFIER); double price =PositionGetDouble(POSITION_PRICE_OPEN); ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); long pos_magic =PositionGetInteger(POSITION_MAGIC); string comment =PositionGetString(POSITION_COMMENT); if(pos_magic==my_magic) { // procesar la posición con el POSITION_MAGIC especificado } PrintFormat("Posición #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s", pos_id,symbol,pos_magic,price,EnumToString(type),comment); } else // la llamada a PositionGetSymbol() se completó sin éxito { PrintFormat("Error al recibir en la caché la posición con el índice %d."+ " Error code: %d", i, GetLastError()); } } }
Reglas para trabajar con la caché del historial
A menudo, el programador escribe el código para trabajar con el historial de la caché de forma que funcione bien solo si el historial contiene de 5 a 10 transacciones y órdenes. Un ejemplo típico de enfoque incorrecto: cargar el historial de trading entero en la caché y procesarlo en un bucle, buscando a través de todas las órdenes y operaciones.
//--- datetime start=0; // fecha inicial establecida en el año 1970 datetime end=TimeCurrent(); // la fecha final establecida al tiempo presente del servidor //--- solicitar a la caché del programa el historial de trading entero HistorySelect(start,end); //--- obtener el número de todas las órdenes del historial int history_orders=HistoryOrdersTotal(); //--- ahora, revisar todas las órdenes for(int i=0;i<history_orders;i++) { // procesar cada orden en el historial } ... //--- obtener el número de todas las transacciones del historial int deals=HistoryDealsTotal(); //--- ahora revisar todas las transacciones for(int i=0;i<deals;i++) { // procesar cada transacción en el historial }
El intento de manejar todo el historial de trading es, en la mayoría de los casos, un movimiento incorrecto. Cuando el número de órdenes/transacciones procesadas se mueve en torno a los miles o decenas de miles, el trabajo del programa se ralentiza drásticamente.
Esto es importante, principalmente, para las simulaciones: el usuario descubrirá que el Probador de pronto se vuelve lento, y comienza a buscar las razones de ello en el terminal de cliente. Por tanto, siempre piense primero en optimizar el código del programa MQL5 (EA e indicadores, que se llaman desde el EA). No dependa del hecho de que el ordenador está hecho de hierro y tiene varios núcleos.
Para un rendimiento correcto del EA y el indicador online, esto es igual de importante. Un código del programa no óptimo puede paralizar el trabajo del ordenador más potente.
El algoritmo correcto para trabajar con el historial de trading:
- Determine la necesidad de solicitar el historial de trading en la caché. Si no es necesario, no realice las siguientes accione:
- Determinar la fecha final del historial de trading (quizás el historial hasta el momento presente no es necesario);
- Calcular la fecha inicial del historial de trading, empezando por la fecha de finalización. Generalmente, los EAs no requieren más de un día o una semana en el historial de trading;
- Obtener los tickets de transacciones y órdenes del historial para obtener las propiedades por los tickets conocidos:
- HistoryOrderGetDouble()
- HistoryOrderGetInteger()
- HistoryOrderGetString()
- HistoryDealGetDouble()
- HistoryDealGetInteger()
- HistoryDealGetString()
- Si se desconocen los tickets, organice un ciclo por distribución;
- En el bucle, obtenga el ticket para cada transacción/orden de la caché del historial de trading por el índice (HistoryOrderGetTicket(Index) y HistoryDealGetTicket(Index));
- Obtenga las propiedades necesarias de órdenes y transacciones por los tickets conocidos (vea el punto 4).
Un ejemplo de código para este algoritmo:
//--- la variable que se establece como “true” durante el cambio en el historial de trading bool TradeHistoryChanged=false; //--- aquí revisamos los cambios en el historial y utilizamos el TradeHistoryChanged=true si es necesario //... el código necesario //--- comprobar si hay cambios en el historial de trading o no if(!TradeHistoryChanged) return; //--- si el historial ha cambiado, tiene sentido cargarlo en la caché //--- fecha final: tiempo actual del servidor datetime end=TimeCurrent(); //--- fecha de comienzo: hace tres días datetime start=end-3*PeriodSeconds(PERIOD_D1); //--- solicitar en la caché del programa el historial de trading de los últimos tres días HistorySelect(start,end); //--- obtener el número de órdenes en la caché del historial int history_orders=HistoryOrdersTotal(); //--- ahora revisar las órdenes for(int i=0;i<history_orders;i++) { //--- obtener el ticket de la orden del historial ulong ticket=HistoryOrderGetTicket(i); //--- trabajar con esta orden – recibir sus problemas long order_magic=HistoryOrderGetInteger(ticket,ORDER_MAGIC); // obtener el resto de las propiedades de la orden por el ticket // ... }
La idea básica que se presenta en este ejemplo es que, primero, debe verificar el hecho de que se han dado cambios en el historial de trading. Una de las opciones es, dentro de la función OnTrade(), configurar en la variable global TradeHistoryChanged el valor "true", puesto que el evento Trade siempre vuelve con algún tipo de evento comercial.
Si el historial de trading no ha cambiado, entonces no hay necesidad de cargar el historial de trading en la caché de nuevo y malgastar recursos de la CPU. Esto es lógico, y no requiere explicación alguna. Si el historial de trading ha cambiado, entonces cargaremos solo la parte necesaria y trataremos cada transacción/orden solo una vez. Evite ciclos de repetición innecesarios.
Puede ver ejemplos de uso correcto e incorrecto del historial de trading en los archivos adjuntos a este artículo, WrongWorkWithHistory.mq5 y RightWorkWithHistory.mq5.
Obtener información por órdenes del historial
Trabajar con órdenes del historial apenas se diferencia de trabajar con órdenes activas, pero hay una excepción. Si el número de órdenes activas en la caché del programa MQL5 no puede ser más de uno, entonces el resultado de HistoryOrdersTotal() y el número de órdenes del historial en la caché dependerá de cuánto historial de trading se haya cargado con las funciones HistorySelect(start, end), HistorySelectByPosition() o HistoryOrderSelection().
Por ejemplo, facilitaremos un script que busca la última orden del último día, y muestra su información.
// --- determinar los intervalos de tiempo y el historial de trading requerido datetime end=TimeCurrent(); // tiempo actual del servidor datetime start=end-PeriodSeconds(PERIOD_D1);// establecer el comienzo a 24 horas atrás //--- solicitar en la caché del programa el historial de trading de un día HistorySelect(start,end); //--- recibir el número de órdenes en el historial int history_orders=HistoryOrdersTotal(); //--- obtener el ticket de la orden, con el último índice en la lista, del historial ulong order_ticket=HistoryOrderGetTicket(history_orders-1); if(order_ticket>0) // obtener la orden del historial en la caché, trabajar con ella { //--- estado de orden ENUM_ORDER_STATE state=(ENUM_ORDER_STATE)HistoryOrderGetInteger(order_ticket,ORDER_STATE); long order_magic =HistoryOrderGetInteger(order_ticket,ORDER_MAGIC); long pos_ID =HistoryOrderGetInteger(order_ticket,ORDER_POSITION_ID); PrintFormat("Order #%d: ORDER_MAGIC=#%d, ORDER_STATE=%d, ORDER_POSITION_ID=%d", order_ticket,order_magic,EnumToString(state),pos_ID); } else // intento de obtener la orden sin éxito { PrintFormat("En total, en el historial de %d órdenes, no pudimos seleccionar la orden"+ " with the index %d. Error %d",history_orders,history_orders-1,GetLastError()); }
En casos más generales, se deben organizar las órdenes en el bucle desde la caché y analizarlas. El algoritmo general será así:
- Determinar los intervalos de tiempo del historial suficiente. Si el historial se carga con a función HistorySelect() no es recomendable cargar el historial de trading entero en la caché;
- Cargar en la caché del programa las funciones de historial de trading HistorySelect(), HistorySelectByPosition() o HistoryOrderSelect (ticket);
- Obtener el número total de órdenes en la caché con la función HistoryOrdersTotal();
- Organizar el ciclo a través de una búsqueda de todas las órdenes por sus índices en la lista;
- Obtener un ticket de las órdenes de la caché usando la función HistoryOrderGetTicket() ;
- Obtener los datos de las órdenes de la caché usando las funciones HistoryOrderGetDouble(), HistoryOrderGetInteger() y HistoryOrderGetString(). Si es necesario, analice los datos obtenidos y tome las medidas apropiadas.
Un ejemplo de tal algoritmo:
#property script_show_inputs input long my_magic=999; //+------------------------------------------------------------------+ //| Función de inicio de programa de Script | //+------------------------------------------------------------------+ void OnStart() { // --- determinar los intervalos de tiempo del historial de trading requerido datetime end=TimeCurrent(); // tiempo actual del servidor datetime start=end-PeriodSeconds(PERIOD_D1);// establecer el comienzo a 24 horas atrás //--- solicitar en la caché del programa el intervalo de historial de trading necesario HistorySelect(start,end); //--- recibir el número de órdenes en el historial int history_orders=HistoryOrdersTotal(); //--- revisar todas las órdenes for(int i=0;i<history_orders;i++) { //--- obtener el ticket de la orden del historial por su número en la lista ulong order_ticket=HistoryOrderGetTicket(i); if(order_ticket>0) // obtener la orden del historial y trabajar con ella { //--- tiempo de ejecución datetime time_done=HistoryOrderGetInteger(order_ticket,ORDER_TIME_DONE); long order_magic =HistoryOrderGetInteger(order_ticket,ORDER_MAGIC); long pos_ID =HistoryOrderGetInteger(order_ticket,ORDER_POSITION_ID); if(order_magic==my_magic) { // procesar la orden con el ORDER_MAGIC especificado } PrintFormat("Order #%d: ORDER_MAGIC=#%d, time_done %s, ORDER_POSITION_ID=%d", order_ticket,order_magic,TimeToString(time_done),pos_ID); } else // intento de obtener la orden del historial sin éxito { PrintFormat("no pudimos seleccionar la orden con el índice %d. Error %d", i,GetLastError()); } } }
Obtener información de transacciones del historial
El procesamiento de transacciones tiene los mismos elementos que el procesamiento de órdenes del historial. El número de transacciones en el historial de trading y el resultado de la ejecución de HistoryDealsTotal() dependerán de cuánto historial de trading se haya cargado con las funciones HistorySelect(start, end), HistorySelectByPosition() o HistoryOrderSelection().
Para llenar la caché solo con una transacción por su ticket, use la función HistoryDealSelect(ticket).
// --- determinar los intervalos de tiempo del historial de trading requerido datetime end=TimeCurrent(); // tiempo actual del servidor datetime start=end-PeriodSeconds(PERIOD_D1);// establecer el comienzo a 24 horas atrás //--- solicitar en la caché del programa el historial de trading de un día HistorySelect(start,end); //--- recibir el número de transacciones en el historial int deals=HistoryDealsTotal(); //--- obtener el ticket de la orden, con el último índice en la lista, del historial ulong deal_ticket=HistoryDealGetTicket(deals-1); if(deal_ticket>0) // obtener la transacción del historial en la caché, trabajar con ella { //--- el ticket de la orden, basado en la transacción que se realizó ulong order =HistoryDealGetInteger(deal_ticket,DEAL_ORDER); long order_magic=HistoryDealGetInteger(deal_ticket,DEAL_MAGIC); long pos_ID =HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID); PrintFormat("Deal #%d for the order #%d with the ORDER_MAGIC=%d that participated in the position", deals-1,order,order_magic,pos_ID); } else // intento de obtener la transacción sin éxito { PrintFormat("En total, en el historial %d de transacciones, no pudimos seleccionar una transacción"+ " with the index %d. Error %d",deals,deals-1,GetLastError()); }
En casos más generales, se necesita buscar las transacciones en el bucle desde la caché y analizarlas. El algoritmo general será así:
- Determinar las barreras del historial suficiente . Si el historial se carga con la función HistorySelect(start, end), no es recomendable cargar el historial entero de trading en la caché;
- Cargar en la caché del programa el historial de trading de las funciones HistorySelect() o HistorySelectByPosition();
- Obtener el número total de transacciones con la función HistoryDealsTotal();
- Organizar el ciclo a través de una búsqueda de todas las transacciones por sus índices en la lista;
- Copiar las transacciones una por una en la caché usando la función HistoryDealGetTicket();
- Obtener los datos sobre la transacción requeridos de la caché usando las funciones HistoryDealGetDouble(), HistoryDealGetInteger(), y HistoryDealGetString(). Si es necesario, analice los datos obtenidos y tome las medidas apropiadas.
Un ejemplo de algoritmo para calcular los beneficios y pérdidas:
input long my_magic=111; //+------------------------------------------------------------------+ //| Función de inicio de programa de Script | //+------------------------------------------------------------------+ void OnStart() { // --- determinar los intervalos de tiempo del historial de trading requerido datetime end=TimeCurrent(); // tiempo actual del servidor datetime start=end-PeriodSeconds(PERIOD_D1);// establecer el comienzo a 24 horas atrás //--- solicitar en la caché del programa el intervalo necesario del historial de trading HistorySelect(start,end); //--- recibir el número de transacciones en el historial int deals=HistoryDealsTotal(); int returns=0; double profit=0; double loss=0; //--- revisar todas las transacciones en el historial for(int i=0;i<deals;i++) { //--- obtener el ticket de la transacción por su índice en la lista ulong deal_ticket=HistoryDealGetTicket(i); if(deal_ticket>0) // obtener la transacción del historial en la caché, trabajar con ella { string symbol =HistoryDealGetString(deal_ticket,DEAL_SYMBOL); datetime time =HistoryDealGetInteger(deal_ticket,DEAL_TIME); ulong order =HistoryDealGetInteger(deal_ticket,DEAL_ORDER); long order_magic =HistoryDealGetInteger(deal_ticket,DEAL_MAGIC); long pos_ID =HistoryDealGetInteger(deal_ticket,DEAL_POSITION_ID); ENUM_DEAL_ENTRY entry_type=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal_ticket,DEAL_ENTRY); //--- procesar las transacciones con el DEAL_MAGIC especificado if(order_magic==my_magic) { //... acciones necesarias } //--- calcular las pérdidas y beneficios con resultados fijos if(entry_type==DEAL_ENTRY_OUT) { //--- incrementar el número de transacciones returns++; //--- resultado de la fijación double result=HistoryDealGetDouble(deal_ticket,DEAL_PROFIT); //--- introducir los resultados positivos en los beneficios resumidos if(result>0) profit+=result; //--- introducir los resultados negativos en las pérdidas resumidas if(result<0) loss+=result; } } else // intento de obtener la transacción sin éxito { PrintFormat("No pudimos seleccionar una transacción con el índice %d. Error %d", i,GetLastError()); } } //--- imprimir los resultados de los cálculos PrintFormat("El número total de transacciones %d con un resultado financiero. Profit=%.2f , Loss= %.2f", returns,profit,loss); }
Obtener una posición en la caché del historial por el identificador de la posición (POSITION_IDENTIFIER)
La función HistorySelectByPosition (position_ID), al igual que la función HistorySelect (start, end), llena la caché con transacciones y órdenes del historial, pero solo con una condición: deben tener el identificador específico de la posición (POSITION_IDENTIFIER). El identificador de la posición es un número único que se asigna automáticamente a cada posición reabierta, y no cambia durante su vida. Mientras tanto... se debe tener en cuenta que el cambio de la posición (cambio del tipo de posición de POSITION_TYPE_BUY a POSITION_TYPE_SELL) no cambia el identificador de la posición.
Cada posición abierta es el resultado de una o más transacciones en ese instrumento. Por tanto, para analizar cambios de posición durante su vida, cada transacción y orden basada en la transacción que se realizó, se le asigna un identificador de la posición en la que la transacción tuvo lugar. Por tanto, sabiendo el identificador de las posiciones abiertas actuales, podemos reconstruir el historial entero, y encontrar todas las órdenes y transacciones que lo han cambiado.
La función HistorySelectByPosition(position_ID) sirve para ahorrarle al programador el tener que escribir su código propio de iteración a través del historial de trading entero en busca de tal información. Un algoritmo típico para trabajar con esta función:
- Obtener el identificador de la posición;
- Obtener todas las órdenes y transacciones cuyos identificadores se corresponden con el identificador de las posiciones actuales en la caché del historial de trading usando la función HistorySelectByPosition();
- Procesar el historial de trading según el algoritmo,
Conclusión
La plataforma del subsistema de trading MetaTrader 5 entera está bien pensada y es fácil de usar. Además, la abundancia de funciones de trading permite resolver cada problema específico de la forma más eficiente.
Pero a pesar de que las clases de trading especializadas de la Biblioteca estándar nos evitan tener que preocuparnos demasiado sobre los detalles y escribir programas en un nivel avanzado sin meternos en la implementación, entender los conceptos básicos nos permitirá crear EAs de trading más seguros y eficientes.
Todos los ejemplos expuestos se pueden encontrar en los archivos adjuntos a este artículo.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/211
- 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