
Por dónde comenzar a crear un robot comercial para la Bolsa de Moscú MOEX
Muchos tráders de la Bolsa de Moscú querrían automatizar sus algoritmos comerciales, pero no saben por dónde empezar. El lenguaje MQL5 propone no solo un conjunto enorme de funciones comerciales, sino también clases preparadas, que facilitan al máximo los primeros pasos en el trading automático. En este artículo mostraremos qué herramientas preparadas propone el lenguaje de estrategias comerciales MQL5 a los tráders del comercio algorítmico en la Bolsa de Moscú.
Dos tipos de solicitudes comerciales en la Bolsa de Moscú MOEX
La Bolsa de Moscú MOEX da soporte a dos tipos de solicitudes comerciales: de mercado y límite.- Las solicitudes de mercado llegan de inmediato a la bolsa y se ejecutan al precio de la mejor oferta. Para enviar estas solicitudes en MQL5 se usan órdenes de mercado del tipo ORDER_TYPE_BUY y ORDER_TYPE_SELL.
- Las solicitudes límite se guardan en el servidor de la bolsa y se ejecutan en cuanto aparezca una solicitud opuesta adecuada. En el lenguaje MQL5 a estas solicitudes les corresponden las órdenes del tipo ORDER_TYPE_BUY_LIMIT y ORDER_TYPE_SELL_LIMIT.
La solicitud de mercado garantiza (pero no siempre) la realización de la operación, sin embargo, no garantiza el precio. Esto significa que como resultado de una operación comercial usted podrá ejecutar una operación a un precio que se diferencia significativamente de la oferta actual. Al mismo tiempo, la solicitud límite garantiza el precio, pero no garantiza que la operación a este precio se llegue a ejecutar. Como resultado, usted puede quedarse fuera del mercado, sin llegar a conseguir la ejecución de la solicitud.
Todos los demás tipos de solicitudes que se proponen a los tráders de la Bolsa de Moscú son parte del complejo programático a través del cual los tráders interactúan con la bolsa. En otras palabras: el resto de las solicitudes son algorítmicas. Las solicitudes se guardan y procesan fuera de la Bolsa de Moscú, y se envían a esta en forma de solicitud de mercado o límite como resultado del procesamiento interno.
La plataforma MetaTrader 5 propone a los tráders los siguientes tipos de solicitudes comerciales, que se pueden usar para comerciar en la Bolsa de Moscú:
Identificador | Descripción | Guardado y ejecución |
ORDER_TYPE_BUY | Orden de mercado de compra | Se envía a la bolsa en forma de solicitud de mercado de compra al mejor precio de venta actual |
ORDER_TYPE_SELL | Orden de mercado de venta | Se envía a la bolsa en forma de solicitud de mercado de venta al mejor precio de compra actual |
ORDER_TYPE_BUY_LIMIT | Orden pendiente Buy Limit | Se envía a la bolsa en forma de solicitud límite de compra y se ejecuta al aparecer una oferta de venta al precio indicado o al mejor precio. |
ORDER_TYPE_SELL_LIMIT | Orden pendiente Sell Limit | Se envía a la bolsa en forma de solicitud límite de venta y se ejecuta al aparecer una oferta de compra al precio indicado o al mejor precio. |
ORDER_TYPE_BUY_STOP | Orden pendiente Buy Stop | Se guarda en el servidor de MetaTrader 5, y al activarse, se envía a la bolsa:
|
ORDER_TYPE_SELL_STOP | Orden pendiente Sell Stop | Se guarda en el servidor de MetaTrader 5, y al activarse, se envía a la bolsa:
|
ORDER_TYPE_BUY_STOP_LIMIT | Orden pendiente BUY STOP LIMIT | Se guarda en el servidor de MetaTrader 5, y al activarse, se envía a la bolsa en forma de solicitud límite de compra |
ORDER_TYPE_SELL_STOP_LIMIT | Orden pendiente SELL STOP LIMIT | Se guarda en el servidor de MetaTrader 5, y al activarse, se envía a la bolsa en forma de solicitud límite de venta |
Para las posiciones abiertas, la plataforma MetaTrader 5 permite definir los niveles de TakeProfit y StopLoss, que se guardan en el servidor comercial de MetaTrader 5 y se activan de forma automática incluso sin estar conectado a la cuenta comercial:
- El nivel de TakeProfit indica el precio para el cierre de la posición en la dirección adecuada, y al activarse en la bolsa, se envía una solicitud límite al precio de TakeProfit;
- el nivel de StopLoss sirve para implementar un stop defensivo en una dirección poco conveniente, y al activarse se envía a la bolsa una solicitud de mercado al precio StopLoss para la sección de valores y divisas, para FORTS se envía una solicitud límite al peor precio del límite del pasillo.
Además, la plataforma MetaTrader 5 permite establecer y modificar los niveles de StopLoss/TakeProfit para las órdenes pendientes, así como modificar los niveles de activación de todas las órdenes pendientes.
Operaciones comerciales en MetaTrader 5
MetaTrader 5 propone varios tipos principales de operaciones comerciales, que pueden serle necesarias en el robot comercial:
- compra/venta al precio actual;
- esteblecer una orden pendiente de compra/venta según una condición concreta;
- modificación/eliminación de una orden pendiente;
- cierre/incremento/disminución/viraje de la posición.
Todas las operaciones comerciales en MQL5 se realizan con la ayuda de la función OrderSend(), que retorna el control al programa en el momento que la orden comercial se haya enviado con éxito a la Bolsa de Moscú. El estado de la orden en ese momento adopta el valor ORDER_STATE_PLACED, y eso significa que su orden se ha ejecutado con éxito (estado de la orden ORDER_STATE_FILLED o ORDER_STATE_PARTIAL). El resultado final de la ejecución de su orden comercial depende del mercado actual, y la orden puede ser rechazada por la bolsa (estado ORDER_STATE_REJECTED) por diferentes motivos.
Existe también una variedad asincrónica de esta función: OrderSendAsync(), que funciona con mucha mayor rapidez que OrderSend(), puesto que no espera al envío de la orden al sistema comercial de la bolsa. La respuesta a esta función se envía de inmediato, en cuanto la solicitud ha sido enviada por el terminal MetaTrader 5 al exterior. Esto significa que su solicitud comercial ha superado la comprobación en el propio terminal y ahora ha sido enviada para su procesamiento en el servidor comercial de MetaTrader 5. Cuánto tiempo se tardará en poner su orden en la cola de la bolsa y cuándo se ejecutará o será rechazada, todo ello depende solo de la carga de trabajo de la bolsa y de la velocidad de su conexión a internet.
Toda la variedad de operaciones comerciales se explica con la estructura MqlTradeRequest, que contiene la descripción de la solicitud comercial. Por eso, las únicas dificultades con las operaciones comerciales pueden relacionarse con el rellenado de la estructura MqlTradeRequest y el procesamiento del resultado de la ejecución de la solicitud.
De acuerdo con las reglas de su sistema comercial, usted puede realizar la compra o la venta al precio del mercado (BUY o SELL), y puede ubicar una orden pendiente de compra/venta a una cierta distancia del precio de mercado actual:
- BUY STOP, SELL STOP — compra o venta al atravesar el nivel indicado (peor que el precio actual). Las órdenes de este tipo se guardan en el servidor comercial de MetaTrader 5 y se envían a la Bolsa de Moscú en el momento de la activación de la condición en forma de orden de mercado (sección de fondos y de divisas) o solicitud límite (FORTS).
- BUY LIMIT, SELL LIMIT — compra o venta al alcanzar el nivel indicado (mejor que el precio actual). Las órdenes de este tipo se envían de inmediato a la Bolsa de Moscú en forma de solicitud límite. Conviene destacar que en el Bolsa de Moscú en una solicitud límite se puede indicar un nivel dentro del spread o incluso en el lado contrario del spread. De esta forma se limita el deslizamiento al realizar una operación.
- BUY STOP LIMIT, SELL STOP LIMIT — establecer una orden BUY LIMIT o SELL LIMIT al alcanzar el precio indicado. Las órdenes se guardan en el servidor comercial MetaTrader 5, y en el momento de activación de la condición en la Bolsa de Moscú, se envía una solicitud límite normal. El nivel de apertura de esta solicitud límite puede ser tanto mayor como menor al precio de activación de la propia orden.
El principio de ejecución de las órdenes BUY STOP, SELL STOP y BUY LIMIT, SELL LIMIT, así como sus métodos de colocación directamente desde la profundidad de mercado se muestra más abajo.
Además, puede que le sea necesario a usted modificar o directamente eliminar una orden pendiente. Esto también se hace con la ayuda de las funciones OrderSend()/OrderSendAsync(). El trabajo con una posición abierta tampoco representa ninguna dificultad, puesto que tiene lugar como resultado de la ejecución de las propias operaciones comerciales.
En este artículo mostraremos lo fácil y simple que es programar la compra y la venta en MQL5, y demostraremos cómo trabajar con una cuenta comercial y las propiedades de los símbolos. En esto nos ayudarán las clases comerciales de la Biblioteca estándar.
Comerciar en la bolsa con ayuda de robots es muy sencillo
El lenguaje MQL5 desde el principio da soporte a todas las posibilidades comerciales de la plataforma MetaTrader 5: en él están presentes multitud de funciones comerciales para trabajar con órdenes, posiciones y solicitudes comerciales. Además, no importa el mercado en el que usted comercie: futuros, acciones opciones, etc.
Con los recursos de MQL5 usted podrá crear una solicitud comercial y enviarla al servidor con la ayuda de la función OrderSend() o OrderSendAsync(), obtener el resultado de su ejecución, ver la historia comercial, conocer las especificaciones del contrato para un instrumento, procesar un evento comercial y obtener toda la información imprescindible.
Para los desarrolladores de robots es importante comprender una circunstancia muy importante: cada operación comercial, sea una apertura de posición, la colocación de un StopLoss o un TakeProfit, o el cierre de posición de una operación opuesta, siempre consta de multitud de transacciones realizadas en el servidor de MetaTrader 5 y en la Bolsa de Moscú. Para ver cómo sucede esto, usted puede iniciar en su cuenta el asesor TradeTransactionListener.mql5, que simplemente oye los eventos de TradeTransaction y muestra una breve información sobre ellos:
//+------------------------------------------------------------------+ //| TradeTransactionListener.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- PrintFormat("LAST PING=%.f ms", TerminalInfoInteger(TERMINAL_PING_LAST)/1000.); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { //--- static int counter=0; // contador de llamadas de OnTradeTransaction() static uint lasttime=0; // hora de la última llamada de OnTradeTransaction() //--- uint time=GetTickCount(); //--- si la última transacción se ha realizado hace más de 1 segundo if(time-lasttime>1000) { counter=0; // significa que se trata de una nueva operación comercial y se puede resetear el contador if(IS_DEBUG_MODE) Print(" Nueva operación comercial"); } lasttime=time; counter++; Print(counter,". ",__FUNCTION__); //--- resultado de la ejecución de la solicitud comercial ulong lastOrderID =trans.order; ENUM_ORDER_TYPE lastOrderType =trans.order_type; ENUM_ORDER_STATE lastOrderState=trans.order_state; //--- nombre del símbolo del que ha tenido lugar la transacción string trans_symbol=trans.symbol; //--- tipo de transacción ENUM_TRADE_TRANSACTION_TYPE trans_type=trans.type; switch(trans.type) { case TRADE_TRANSACTION_POSITION: // cambio de la posición { ulong pos_ID=trans.position; PrintFormat("MqlTradeTransaction: Position #%I64u %s modified: SL=%.5f TP=%.5f", pos_ID,trans_symbol,trans.price_sl,trans.price_tp); } break; case TRADE_TRANSACTION_REQUEST: // envío de la solicitud comercial PrintFormat("MqlTradeTransaction: TRADE_TRANSACTION_REQUEST"); break; case TRADE_TRANSACTION_DEAL_ADD: // adición de una operación { ulong lastDealID =trans.deal; ENUM_DEAL_TYPE lastDealType =trans.deal_type; double lastDealVolume=trans.volume; //--- el identificador de la operación en el sistema externo es un ticket asignado a la Bolsa de Moscú string Exchange_ticket=""; if(HistoryDealSelect(lastDealID)) Exchange_ticket=HistoryDealGetString(lastDealID,DEAL_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("(MOEX deal=%s)",Exchange_ticket); PrintFormat("MqlTradeTransaction: %s deal #%I64u %s %s %.2f lot %s",EnumToString(trans_type), lastDealID,EnumToString(lastDealType),trans_symbol,lastDealVolume,Exchange_ticket); } break; case TRADE_TRANSACTION_HISTORY_ADD: // adición de una orden a la historia { //--- el identificador de la orden en el sistema externo es un ticket asignado a la Bolsa de Moscú string Exchange_ticket=""; if(lastOrderState==ORDER_STATE_FILLED) { if(HistoryOrderSelect(lastOrderID)) Exchange_ticket=HistoryOrderGetString(lastOrderID,ORDER_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("(MOEX ticket=%s)",Exchange_ticket); } PrintFormat("MqlTradeTransaction: %s order #%I64u %s %s %s %s",EnumToString(trans_type), lastOrderID,EnumToString(lastOrderType),trans_symbol,EnumToString(lastOrderState),Exchange_ticket); } break; default: // otras transacciones { //--- el identificador de la orden en el sistema externo es un ticket asignado a la Bolsa de Moscú string Exchange_ticket=""; if(lastOrderState==ORDER_STATE_PLACED) { if(OrderSelect(lastOrderID)) Exchange_ticket=OrderGetString(ORDER_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("MOEX ticket=%s",Exchange_ticket); } PrintFormat("MqlTradeTransaction: %s order #%I64u %s %s %s",EnumToString(trans_type), lastOrderID,EnumToString(lastOrderType),EnumToString(lastOrderState),Exchange_ticket); } break; } //--- ticket de la orden ulong orderID_result=result.order; string retcode_result=GetRetcodeID(result.retcode); if(orderID_result!=0) PrintFormat("MqlTradeResult: order #%d retcode=%s ",orderID_result,retcode_result); //--- } //+------------------------------------------------------------------+ //| pasa los códigos numéricos de las respuestas a códigos mnemónicos de línea | //+------------------------------------------------------------------+ string GetRetcodeID(int retcode) { switch(retcode) { case 10004: return("TRADE_RETCODE_REQUOTE"); break; case 10006: return("TRADE_RETCODE_REJECT"); break; case 10007: return("TRADE_RETCODE_CANCEL"); break; case 10008: return("TRADE_RETCODE_PLACED"); break; case 10009: return("TRADE_RETCODE_DONE"); break; case 10010: return("TRADE_RETCODE_DONE_PARTIAL"); break; case 10011: return("TRADE_RETCODE_ERROR"); break; case 10012: return("TRADE_RETCODE_TIMEOUT"); break; case 10013: return("TRADE_RETCODE_INVALID"); break; case 10014: return("TRADE_RETCODE_INVALID_VOLUME"); break; case 10015: return("TRADE_RETCODE_INVALID_PRICE"); break; case 10016: return("TRADE_RETCODE_INVALID_STOPS"); break; case 10017: return("TRADE_RETCODE_TRADE_DISABLED"); break; case 10018: return("TRADE_RETCODE_MARKET_CLOSED"); break; case 10019: return("TRADE_RETCODE_NO_MONEY"); break; case 10020: return("TRADE_RETCODE_PRICE_CHANGED"); break; case 10021: return("TRADE_RETCODE_PRICE_OFF"); break; case 10022: return("TRADE_RETCODE_INVALID_EXPIRATION"); break; case 10023: return("TRADE_RETCODE_ORDER_CHANGED"); break; case 10024: return("TRADE_RETCODE_TOO_MANY_REQUESTS"); break; case 10025: return("TRADE_RETCODE_NO_CHANGES"); break; case 10026: return("TRADE_RETCODE_SERVER_DISABLES_AT"); break; case 10027: return("TRADE_RETCODE_CLIENT_DISABLES_AT"); break; case 10028: return("TRADE_RETCODE_LOCKED"); break; case 10029: return("TRADE_RETCODE_FROZEN"); break; case 10030: return("TRADE_RETCODE_INVALID_FILL"); break; case 10031: return("TRADE_RETCODE_CONNECTION"); break; case 10032: return("TRADE_RETCODE_ONLY_REAL"); break; case 10033: return("TRADE_RETCODE_LIMIT_ORDERS"); break; case 10034: return("TRADE_RETCODE_LIMIT_VOLUME"); break; case 10035: return("TRADE_RETCODE_INVALID_ORDER"); break; case 10036: return("TRADE_RETCODE_POSITION_CLOSED"); break; default: return("TRADE_RETCODE_UNKNOWN="+IntegerToString(retcode)); break; } //--- } //+------------------------------------------------------------------+
Ejemplo de funcionamiento de este "oyente":
2016.06.09 14:51:19.763 TradeTransactionListener (Si-6.16,M15) LAST PING=14 ms Compra 2016.06.09 14:51:24.856 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:51:24.856 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_ADD order #49118594 ORDER_TYPE_BUY ORDER_STATE_STARTED 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeResult: order #49118594 retcode=TRADE_RETCODE_PLACED 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) 3. OnTradeTransaction 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118594 ORDER_TYPE_BUY ORDER_STATE_REQUEST_ADD 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) 4. OnTradeTransaction 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118594 ORDER_TYPE_BUY ORDER_STATE_PLACED 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) 5. OnTradeTransaction 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_DELETE order #49118594 ORDER_TYPE_BUY ORDER_STATE_PLACED 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) 6. OnTradeTransaction 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_HISTORY_ADD order #49118594 ORDER_TYPE_BUY Si-6.16 ORDER_STATE_FILLED (MOEX ticket=3377179723) 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) 7. OnTradeTransaction 2016.06.09 14:51:24.885 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_DEAL_ADD deal #6945344 DEAL_TYPE_BUY Si-6.16 1.00 lot (MOEX deal=185290434) Colocación de SL/TP 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: Position #0 Si-6.16 modified: SL=62000.00000 TP=67000.00000 Cierre de posición (venta) 2016.06.09 14:52:24.063 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:52:24.063 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_ADD order #49118750 ORDER_TYPE_SELL ORDER_STATE_STARTED 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeResult: order #49118750 retcode=TRADE_RETCODE_PLACED 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) 3. OnTradeTransaction 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118750 ORDER_TYPE_SELL ORDER_STATE_REQUEST_ADD 2016.06.09 14:52:24.071 TradeTransactionListener (Si-6.16,M15) 4. OnTradeTransaction 2016.06.09 14:52:24.071 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118750 ORDER_TYPE_SELL ORDER_STATE_PLACED 2016.06.09 14:52:24.073 TradeTransactionListener (Si-6.16,M15) 5. OnTradeTransaction 2016.06.09 14:52:24.073 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_DEAL_ADD deal #6945378 DEAL_TYPE_SELL Si-6.16 1.00 lot (MOEX deal=185290646) 2016.06.09 14:52:24.075 TradeTransactionListener (Si-6.16,M15) 6. OnTradeTransaction 2016.06.09 14:52:24.075 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_DELETE order #49118750 ORDER_TYPE_SELL ORDER_STATE_PLACED 2016.06.09 14:52:24.077 TradeTransactionListener (Si-6.16,M15) 7. OnTradeTransaction 2016.06.09 14:52:24.077 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_HISTORY_ADD order #49118750 ORDER_TYPE_SELL Si-6.16 ORDER_STATE_FILLED (MOEX ticket=3377182821)
Ahora es el momento de proceder al análisis de los ejemplos del código fuente.
Trabajo con la cuenta comercial
Para comenzar, al iniciar el robot comercial es necesario obtener información sobre la cuenta en la que va a comerciar.
Para trabajar con la cuenta existe la clase CAccountInfo, que ha sido desarrollada especialmente con estos fines. Añadimos a nuestro código la conexión del archivo AccountInfo.mqh y declaramos la variable de esta clase account:
#include <Trade\AccountInfo.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- objeto para trabajar con la cuenta CAccountInfo account; //--- obtenemos el número de la cuenta en la que ha sido iniciado el asesor long login=account.Login(); Print("Login=",login); //--- mostramos la divisa de la cuenta Print("Divisa de la cuenta: ",account.Currency()); //--- mostramos el balance y el beneficio actual de la cuenta Print("Balance=",account.Balance()," Profit=",account.Profit()," Equity=",account.Equity()); //--- mostramos el tipo de cuenta Print("Tipo de cuenta: ",account.TradeModeDescription()); //--- aclaramos si se puede comerciar en general en esta cuenta if(account.TradeAllowed()) Print("El comercio en esta cuenta está permitido"); else Print("El comercio en la cuenta está prohibido: quizá la entrada se realizó con la contraseña de inversor"); //--- modo de cálculo del margen Print("Modo de cálculo del margen: ",account.MarginModeDescription()); //--- aclaramos si está permitido comerciar en la cuenta con la ayuda de un experto if(account.TradeExpert()) Print("El comercio automático en la cuenta está permitido"); else Print("El comercio automático con la ayuda de expertos y scripts está prohibido"); //--- la cantidad de órdenes permitidas se ha definido o no int orders_limit=account.LimitOrders(); if(orders_limit!=0)Print("Número máximo permitido de órdenes pendientes activas: ",orders_limit); //--- mostramos el nombre de la compañía y el servidor Print(account.Company(),": server ",account.Server()); Print(__FUNCTION__," completed"); //--- }
Como podemos ver por el código mostrado, con la ayuda de la variable account en la función OnInit() se puede obtener mucha información útil. Puede añadir este código a su experto y le resultará bastante más sencillo descifrar el registro (log) al analizar su funcionamiento.
El resultado del inicio del experto se muestra en la imagen.
Obtener las propiedades de un instrumento financiero
Ya hemos obtenido la información sobre la cuenta, pero para realizar operaciones comerciales debemos conocer además las propiedades del activo con el que tenemos intención de comerciar. Para ello tenemos otra clase muy cómoda, CSymbolInfo que posee una gran cantidad de métodos. Mostraremos como ejemplo solo una pequeña parte.
#include<Trade\SymbolInfo.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- objeto para obtener las propiedades del símbolo CSymbolInfo symbol_info; //--- indicamos el nombre del símbolo para el que vamos a recibir información symbol_info.Name(_Symbol); //--- obtenemos las cotizaciones actuales y las mostramos symbol_info.RefreshRates(); Print(symbol_info.Name()," (",symbol_info.Description(),")", " Bid=",symbol_info.Bid()," Ask=",symbol_info.Ask()); //--- obtenemos el número de dígitos tras la coma y el tamaño del punto Print("Digits=",symbol_info.Digits(), ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits())); //--- solicitamos el tipo de ejecución de las órdenes y si hay limitaciones Print("Limitaciones en las operaciones comerciales: ",EnumToString(symbol_info.TradeMode()), " (",symbol_info.TradeModeDescription(),")"); //--- aclaramos el modo de ejecución de las operaciones Print("Modo de ejecución de las operaciones: ",EnumToString(symbol_info.TradeExecution()), " (",symbol_info.TradeExecutionDescription(),")"); //--- aclaramos el modo de cálculo del coste de los contratos Print("Cálculo del coste del contrato: ",EnumToString(symbol_info.TradeCalcMode()), " (",symbol_info.TradeCalcModeDescription(),")"); //--- tamaño del contrato Print("Tamaño del contrato estándar: ",symbol_info.ContractSize()); //--- tamaño del margen inicial para 1 contrato Print("Margen inicial para un contrato estándar: ",symbol_info.MarginInitial()," ",symbol_info.CurrencyBase()); //--- tamaños mínimo, máximo del volumen en las operaciones comerciales Print("Volume info: LotsMin=",symbol_info.LotsMin()," LotsMax=",symbol_info.LotsMax(), " LotsStep=",symbol_info.LotsStep()); //--- Print(__FUNCTION__," completed"); }
En la imagen se muestran las propiedades del símbolo Si-6.16 de la sección del mercado urgente de la Bolsa de Moscú (FORTS). Ahora usted está preparado para pasar directamente al comercio.
Programación de operaciones comerciales
Para enviar órdenes comerciales en el lenguaje MQL5 existen dos funciones: OrderSend() y OrderSendAsync(). En realidad son dos implementaciones de la misma función. Mientras que OrderSend() envía una solicitud comercial y espera el resultado de su ejecución, la OrderSendAsync() asincrónica simplemente lanza la solicitud y permite al programa seguir trabajando sin esperar la respuesta del servidor comercial. De esta forma, trabajar en MQL5 es realmente sencillo, basta con usar solo una función para todas las operaciones comerciales
Ambas funciones reciben como primer parámetro la estructura MqlTradeRequest, que contiene más de una decena de campos. Los componentes de los campos necesarios dependen del tipo de operación comercial, por eso no es necesario rellenar todos los campos. En caso de que se dé un valor incorrecto o no haya campo en absoluto, la solicitud no superará la revisión en el propio terminal y sencillamente no se enviará al servidor. Además, los valores de las enumeraciones predeterminadas de los 5 campos deben ser correctamente indicados.
Semejante cantidad de campos en la solicitud comercial es necesaria debido a la necesidad de describir la multitud de propiedades de la orden, que pueden cambiar dependiendo de la política de ejecución, el tiempo de expiración y otros parámetros. Usted no estará obligado a aprenderse de memoria todas estas sutilezas, solo tendrá que usar la clase CTrade. Este será el aspecto aproximado que tendrá el uso de esta clase en nuestro robot comercial:
#include<Trade\Trade.mqh> //--- objeto para la realización de operaciones comerciales CTrade trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicamos el Número Mágico para identificar nuestras órdenes int MagicNumber=123456; trade.SetExpertMagicNumber(MagicNumber); //--- establecemos el deslizamiento permisible en puntos al realizar una compra/venta int deviation=10; trade.SetDeviationInPoints(deviation); //--- modo de rellenado de una orden, hay que usar el modo que esté permitido por el servidor trade.SetTypeFilling(ORDER_FILLING_RETURN); //--- modo de registro (logueo): mejor no llamar a este método en absoluto, la clase pondrá ella misma el modo óptimo trade.LogLevel(1); //--- qué función usar para el comercio: true - OrderSendAsync(), false - OrderSend() trade.SetAsyncMode(true); //--- return(0); }
Para comerciar en la bolsa, normalmente se usa el modo de ejecución ORDER_FILLING_RETURN. La guía dice:
Este modo se usa dolo en los modos "Ejecución por mercado" y "Ejecución bursátil": para las órdenes de mercado (ORDER_TYPE_BUY y ORDER_TYPE_SELL), las órdenes límite y límite stop (ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT, ORDER_TYPE_BUY_STOP_LIMIT y ORDER_TYPE_SELL_STOP_LIMIT). En caso de ejecución parcial, una orden limitada con el volumen no cubierto no se anula, sino que sigue estando vigente.
Para las órdenes ORDER_TYPE_BUY_STOP_LIMIT y ORDER_TYPE_SELL_STOP_LIMIT, al activarse se creará la orden límite correspondiente ORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT con el tipo de ejecución ORDER_FILLING_RETURN.
Ha llegado el momento de ver cómo CTrade ayuda en las operaciones comerciales.
Compra/venta al precio actual
Con frecuencia en las estrategias comerciales es necesario realizar la compra o venta al precio actual en el mismo instante. CTrade está familiarizado con esa situación y solo pide el volumen necesario de la operación comercial. Todos los demás parámetros: el precio de apertura y el nombre del símbolo, los niveles de Stop Loss y Take Profit, los comentarios a la orden, no será necesario indicarlos.
//--- 1. ejemplo de compra con el símbolo actual if(!trade.Buy(1)) { //--- comunicamos el fracaso Print("El método Buy() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método Buy() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Por defecto, si no se indica el nombre del instrumento, CTrade utilizará el nombre del símbolo en cuyo gráfico ha sido iniciado. Esto es muy cómodo para las estrategias sencillas. Para un robot que comercia directamente con varios instrumentos, usted necesitará indicar claramente cada vez el símbolo con el que se realizará la operación comercial.
//--- 2. ejemplo de compra con el símbolo indicado if(!trade.Buy(1,"Si-6.16")) { //--- comunicamos el fracaso Print("El método Buy() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método Buy() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Es posible indicar todos los parámetros de la orden: los niveles de Stop Loss/Take Profit, el precio de apertura y los comentarios.
//--- 3. ejemplo de compra con el símbolo indicado, con los SL y TP definidos double volume=1; // indicamos el volumen de la operación comercial string symbol="Si-6.16"; // indicacmos el símbolo en el que se realiza la operación int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // cantidad de dígitos tras la coma double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // punto double bid=SymbolInfoDouble(symbol,SYMBOL_BID); // precio actual para el cierre de LONG double SL=bid-100*point; // valor no normalizado de SL SL=NormalizeDouble(SL,digits); // normalizamos el Stop Loss double TP=bid+100*point; // valor no normalizado de TP TP=NormalizeDouble(TP,digits); // normalizamos el Take Profit //--- obtenemos el precio de apertura actual para la posición LONG double open_price=SymbolInfoDouble(symbol,SYMBOL_ASK); string comment=StringFormat("Buy %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(open_price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); if(!trade.Buy(volume,symbol,open_price,SL,TP,comment)) { //--- comunicamos el fracaso Print("El método Buy() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método Buy() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Recordemos que el Número Mágico y el deslizamiento permisible los hemos establecido durante la inicialización del ejemplar CTrade, por eso no serán necesarios. Aunque también podemos definirlos antes de cada operación comercial, en el caso de que sean necesarios.
Colocar una orden límite
Para enviar una orden límite se usa el método correspondiente de la clase BuyLimit() o SellLimit(). Para la mayoría de los casos, puede bastar con la versión abreviada, cuando se indican solo el precio de apertura y el volumen. El precio de apertura para BuyLimit deberá ser inferior al precio actual, y para SellLimit deberá ser superior. Es decir, estas órdenes se usan para la entrada en el mercado al mejor precio, por ejemplo, en las estrategias con cálculo de retroceso con respecto al nivel de apoyo. Además, se usa el símbolo en el que se haya iniciado el experto.
//--- 1. ejemplo de colocación de una orden pendiente BuyLimit string symbol="Si-6.16"; // indicamos el símbolo en el que se coloca la orden int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // cantidad de dígitos tras la coma double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // punto double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // precio de compra actual double price=ask-100*point; // precio de apertura no normalizado price=NormalizeDouble(price,digits); // normalizamos el precio de apertura //--- todo está listo, enviamos al servidor la orden pendiente Buy Limit if(!trade.BuyLimit(1,price)) { //--- comunicamos el fracaso Print("El método BuyLimit() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método BuyLimit() se ha ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Se puede usar una versión más detallada con la indicación de todos los parámetros: los niveles de SL/TP, el tiempo de expiración, el nombre del instrumento y los comentarios a la orden.
//--- 2. ejemplo de colocación de una orden pendiente BuyLimit con todos los parámetros double volume=1; string symbol="Si-6.16"; // indicamos el símbolo en el que se coloca la orden int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // cantidad de dígitos tras la coma double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // punto double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // precio de compra actual double price=ask-100*point; // precio de apertura no normalizado price=NormalizeDouble(price,digits); // normalizamos el precio de apertura int SL_pips=100; // Stop Loss en puntos int TP_pips=100; // Take Profit en puntos double SL=price-SL_pips*point; // valor no normalizado de SL SL=NormalizeDouble(SL,digits); // normalizamos el Stop Loss double TP=price+TP_pips*point; // valor no normalizado de TP TP=NormalizeDouble(TP,digits); // normalizamos el Take Profit datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); string comment=StringFormat("Buy Limit %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); //--- todo está listo, enviamos al servidor la orden pendiente Buy Limit if(!trade.BuyLimit(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment)) { //--- comunicamos el fracaso Print("El método BuyLimit() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método BuyLimit() se ha ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
En esta variante es necesario indicar correctamente los niveles de SL y TP. No olvide que para la compra, el nivel de Take Profit debe estar por encima del precio de apertura, y el nivel de Stop Loss, por debajo del precio de apertura. Para las órdenes SellLimit todo es al revés. Usted podrá saber fácilmente si se ha equivocado al simular con el experto en los datos históricos, la clase CTrade automáticamente mostrará mensajes en estos casos (si usted mismo no ha llamado la función LogLevel).
Colocar una orden stop
Para enviar una orden stop se usan los métodos análogos BuyStop() y SellStop(). El precio de apertura para Buy Stop deberá ser superior al precio actual, y para SellStop, inferior. Las órdenes stop se usan en las estrategias que entran en la ruptura de un cierto nivel de resistencia, así como para la limitación de pérdidas. Variante sencilla:
//--- 1. ejemplo de colocación de una orden pendiente Buy Stop string symbol="RTS-6.16"; // indicaremos el símbolo en el que se coloca la orden int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // cantidad de dígitos tras la coma double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // punto double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // precio de compra actual double price=ask+100*point; // precio de apertura no normalizado price=NormalizeDouble(price,digits); // normalizamos el precio de apertura //--- todo está listo, enviamos al servidor la orden pendiente Buy Stop if(!trade.BuyStop(1,price)) { //--- comunicamos el fracaso Print("El método BuyStop() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método BuyStop() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Ahora con más detalle, en el caso en que haya que indicar el máximo de parámetros para una ordenpendiente BuyStop:
//--- 2. ejemplo de colocación de una orden pendiente Buy Stop con todos los parámetros double volume=1; string symbol="RTS-6.16"; // indicaremos el símbolo en el que se coloca la orden int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // cantidad de dígitos tras la coma double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // punto double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // precio de compra actual double price=ask+100*point; // precio de apertura no normalizado price=NormalizeDouble(price,digits); // normalizamos el precio de apertura int SL_pips=100; // Stop Loss en puntos int TP_pips=100; // Take Profit en puntos double SL=price-SL_pips*point; // valor no normalizado de SL SL=NormalizeDouble(SL,digits); // normalizamos el Stop Loss double TP=price+TP_pips*point; // valor no normalizado de TP TP=NormalizeDouble(TP,digits); // normalizamos el Take Profit datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); string comment=StringFormat("Buy Stop %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); //--- todo está listo, enviamos al servidor la orden pendiente Buy Stop if(!trade.BuyStop(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment)) { //--- comunicamos el fracaso Print("El método BuyStop() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método BuyStop() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Para enviar un orden SellStop se aplica el método correspondiente de la clase CTrade, lo más importante es indicar el precio correctamente.
Trabajar con una posición
Usted puede utilizar, en lugar de los métodos Buy() y Sell(), los métodos de apertura de posiciones. Sí es cierto que en este caso deberá indicar más detalles:
//--- número de dígitos tras la coma int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //--- valor del punto double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- obtenemos el precio de compra double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK); //--- calculamos y normalizamos los niveles de SL y TP double SL=NormalizeDouble(price-100*point,digits); double TP=NormalizeDouble(price+100*point,digits); //--- rellenamos los comentarios string comment="Buy "+_Symbol+" 1 at "+DoubleToString(price,digits); //--- todo está listo, intentamos abrir una posición de compra if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,price,SL,TP,comment)) { //--- comunicamos el fracaso Print("El método PositionOpen() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método PositionOpen() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Para cerrar una posición basta con indicar el nombre del instrumento, el resto lo hará la clase CTrade.
//--- cerramos la posición del símbolo actual if(!trade.PositionClose(_Symbol)) { //--- comunicamos el fracaso Print("El método PositionClose() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método PositionClose() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
A una posición abierta se le pueden cambiar los niveles de StopLoss y TakeProfit. Esto se hace con la ayuda del método ModifyPosition().
//--- número de dígitos tras la coma int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //--- valor del punto double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- obtenemos el precio Bid actual double price=SymbolInfoDouble(_Symbol,SYMBOL_BID); //--- calculamos y normalizamos los niveles de SL y TP double SL=NormalizeDouble(price-100*point,digits); double TP=NormalizeDouble(price+100*point,digits); //--- todo está listo, intentamos modificar la posición de compra if(!trade.PositionModify(_Symbol,SL,TP)) { //--- comunicamos el fracaso Print("El método PositionModify() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método PositionModify() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Modificación y eliminación de una orden
Para cambiar los parámetros de una orden pendiente en la clase CTrade se ha pensado el método OrderModify(), al que se le deben transmitir todos los parámetros necesarios.
//--- comprobamos si hay una orden if(!OrderSelect(ticket)) { Print("Orden #",ticket," no encontrada"); return; } //--- símbolo string symbol=OrderGetString(ORDER_SYMBOL); //--- número de dígitos tras la coma int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- valor del punto double point=SymbolInfoDouble(symbol,SYMBOL_POINT); //--- obtenemos el precio de apertura double price=OrderGetDouble(ORDER_PRICE_OPEN); //--- calculamos y normalizamos los niveles de SL y TP double SL=NormalizeDouble(price-200*point,digits); double TP=NormalizeDouble(price+200*point,digits); //--- todo está listo, intentamos modificar la orden if(!trade.OrderModify(ticket,price,SL,TP,ORDER_TIME_DAY,0)) { //--- comunicamos el fracaso Print("El método OrderModify() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método OrderModify() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Es imprescindible que obtenga el ticket de la orden a modificar y que, dependiendo de su tipo, indique los niveles correctos de StopLoss y TakeProfit. Además, el nuevo precio de apertura deberá ser igualmente correcto con respecto al precio actual.
Para eliminar una orden pendiente basta con conocer su ticket:
//--- comprobamos si hay una orden if(!OrderSelect(ticket)) { Print("Orden #",ticket," no encontrada"); return; } //--- todo está listo, intentamos eliminar la orden if(!trade.OrderDelete(ticket)) { //--- comunicamos el fracaso Print("El método OrderDelete() ha fracasado. Código de retorno=",trade.ResultRetcode(), ". Descripción del código: ",trade.ResultRetcodeDescription()); } else { Print("El método OrderDelete() ha sido ejecutado con éxito. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
En la clase también está el método universal OrderOpen(), que puede colocar órdenes pendientes de cualquier tipo. A diferencia de los métodos especializados BuyLimit, BuyStop, SellLimit y SellStop, necesita que se indiquen más parámetros obligatorios. Puede ser que a alguien le parezca más cómodo.
Qué más podemos ver en las clases comerciales
En este artículo hemos mostrado métodos sencillos para programar operaciones comerciales de compra y venta, así como el trabajo con órdenes pendientes. Pero en el apartado Clases comerciales hay también otros cómodos ayudantes para los desarrolladores de robots en MQL5:
- COrderInfo — para trabajar con órdenes;
- CHistoryOrderInfo — para trabajar con las órdenes procesadas que han entrado en la historia comercial;
- CPositionInfo — para trabajar con las posiciones;
- CDealInfo — para trabajar con las operaciones;
- CTerminalInfo — para obtener información sobre el propio terminal.
Con la ayuda de estas clases usted podrá concentrarse solo en la parte comercial de su estrategia, reduciendo al mínimo todas las cuestiones técnicas. Además, la clase CTrade se puede usar para estudiar los requerimientos comerciales, por ejemplo, para la depuración. Con el tiempo, usted también creará sobre su base sus propias clases, en las que implementará la lógica que necesite del procesamiento de los resultados de la ejecución del requerimiento comercial.
Comience su camino en el trading algorítmico con scripts sencillos
Los métodos propuestos en el artículo sobre el desarrollo de robots comerciales en MQL5 han sido pensados, en primer lugar, para los principiantes, aunque muchos desarrolladores con experiencia podrán encontrar cosas nuevas y útiles en ellos. Empiece ejecutando los scripts de este artículo y comprenderá que crear un robot coercial es bastante más sencillo de lo que piensa.
Para aquellos que hayan decidido ir un poco más allá, les proponemos otros dos artículos sobre este tema:
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/2513





- 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