Desarrollando un EA comercial desde cero

22 diciembre 2021, 12:09
Daniel Jose
0
1 604

Introducción

Con el aumento del número de nuevos usuarios en los mercados financieros, es de esperar que, a pesar de que muchos de ellos no tienen el conocimiento adecuado de cómo funciona el sistema de órdenes, hay quienes realmente quieren saber lo que está sucediendo y tratan de tener el mayor control posible sobre la situación o sobre lo que están haciendo.

Es muy cierto que MetaTrader 5 nos permite tener un gran control sobre las operaciones que queremos posicionar, pero utilizar el sistema que se encuentra en MetaTrader 5 para colocar las órdenes es algo bastante costoso y muy sujeto a errores por parte de los usuarios menos experimentados. Y si la intención es operar con contratos de futuros dentro de lo conocido donde podemos tener poco tiempo para colocar la orden, la cosa se convierte en una pesadilla, ya que rellenar todos los campos a tiempo y de forma correcta nos hace perder oportunidades de buenas entradas, o perder dinero si algo se llena de forma incorrecta.

A propósito de esto, ¿qué tal si se utiliza un EA que facilite las cosas, de manera que simplemente se indiquen algunas cosas, como el nivel de apalancamiento, cuánto se acepta perder y cuánto se quiere ganar (esto financieramente, ya que muchos no entienden la relación de puntos con lo financiero), y posicionar la orden directamente en el gráfico con el ratón e indicar dónde se quiere entrar, y si será en la mano de compra o de venta?...


Planificación

La parte más compleja cuando se va a crear algo es pensar en cómo tiene que funcionar, y para ello tendremos que crear el mínimo código posible, ya que cuanto más complejo sea el código a crear, mayor será la posibilidad de errores y fallos durante el tiempo de ejecución ( RUN TIME ). Pensando en esto, intenté hacer un código lo más sencillo posible y, a la vez, utilizar al máximo lo que ya nos da MetaTrader 5. Y la razón de esto es que la plataforma siempre está siendo puesta a prueba y con esto no tendremos posibilidades de errores por parte de ella.

El código fue diseñado para utilizar programación POO (programación orientada a objetos), con esto podemos aislar el código, facilitando su mantenimiento y crecimiento futuro con nuevas funcionalidades y mejoras.

Si bien el EA en cuestión está dirigido a la negociación en la B3 (Bolsa de Valores de Brasil), y más específicamente a futuros (Mini índice y Mini Dólar), podemos extenderlo a todos los mercados y esto de una manera muy práctica y sencilla, con el mínimo de ediciones. Para facilitar las cosas y no seguir repitiendo una prueba de qué activo estamos negociando, utilizamos la siguiente enumeración:

enum eTypeSymbolFast {WIN, WDO, OTHER};


Si deseamos operar otro activo, que use alguna característica especial, podemos agregarlo aquí; también será necesario realizar pequeños cambios en el código, pero usar esta enumeración lo hace mucho más fácil ya que reduce la posibilidad de errores. Una parte muy curiosa del código es la función AdjustPrice ...

   double AdjustPrice(const double arg)
     {
      double v0, v1;
      if(m_Infos.TypeSymbol == OTHER)
         return arg;
      v0 = (m_Infos.TypeSymbol == WDO ? round(arg * 10.0) : round(arg));
      v1 = fmod(round(v0), 5.0);
      v0 -= ((v1 != 0) || (v1 != 5) ? v1 : 0);
      return (m_Infos.TypeSymbol == WDO ? v0 / 10.0 : v0);
     };

La función anterior hará un ajuste en el valor que se utilizará en el precio, para posicionar las líneas en el punto correcto del gráfico. ¡¿Pero por qué no simplemente poner la línea en el gráfico?! La razón es que algunos activos tienen un valor de paso entre los precios, en el caso del WDO ( Mini Dólar ) este paso es de 0.5 puntos, en el caso del WIN ( Mini índice ) este paso es de 5 puntos, y en el caso accionista el paso es de 0.01 puntos. En otras palabras, cada tick tiene un valor diferente para diferentes activos, y esta función hace exactamente eso, corrige, o mejor dicho, ajusta el precio para el valor correcto acomodándolo a una equivalencia en ticks, y esto es importante para crear la orden en el punto correcto, de lo contrario el servidor de negociación puede negar la orden.

Sin esta función, sería complicado saber qué valores correctos usar para completar la orden OCO, en cuyo caso el servidor indicaría que la orden se ha completado incorrectamente e impediría que se ejecute o se posicione. Ahora veamos la función que es el corazón del EA, la función CreateOrderPendent, se puede ver a continuación.

   ulong CreateOrderPendent(const bool IsBuy, const double Volume, const double Price, const double Take, const double Stop, const bool DayTrade = true)
     {
      double last = SymbolInfoDouble(m_szSymbol, SYMBOL_LAST);
      ZeroMemory(TradeRequest);
      ZeroMemory(TradeResult);
      TradeRequest.action        = TRADE_ACTION_PENDING;
      TradeRequest.symbol        = m_szSymbol;
      TradeRequest.volume        = Volume;
      TradeRequest.type          = (IsBuy ? (last >= Price ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_BUY_STOP) : (last < Price ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_SELL_STOP));
      TradeRequest.price         = NormalizeDouble(Price, m_Infos.nDigits);
      TradeRequest.sl            = NormalizeDouble(Stop, m_Infos.nDigits);
      TradeRequest.tp            = NormalizeDouble(Take, m_Infos.nDigits);
      TradeRequest.type_time     = (DayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
      TradeRequest.stoplimit     = 0;
      TradeRequest.expiration    = 0;
      TradeRequest.type_filling  = ORDER_FILLING_RETURN;
      TradeRequest.deviation     = 1000;
      TradeRequest.comment       = "Order Generated by Experts Advisor.";
      if(!OrderSend(TradeRequest, TradeResult))
        {
         MessageBox(StringFormat("Error Number: %d", TradeResult.retcode), "Nano EA");
         return 0;
        };
      return TradeResult.order;
     };

Esta función es extremadamente sencilla y enfocada a ser lo más segura posible, aquí crearemos una orden OCO que será enviada al servidor de comercio, nótese que estamos usando órdenes del tipo LIMIT o STOP. Esto se debe a que este tipo de orden es más sencilla y su ejecución está garantizada, incluso en caso de movimientos bruscos de los precios.

Tenga en cuenta que el tipo de orden que se utilizará dependerá del precio de ejecución y del precio actual del activo, así como de si entrará en la mano de compra o de venta, todo esto se hace en esta línea:

TradeRequest.type = (IsBuy ? (last >= Price ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_BUY_STOP) : (last < Price ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_SELL_STOP));

También puede hacer algo que muchos desean, ejecutar CROSS ORDER, esto se logra indicando el activo a negociar en la siguiente línea:

TradeRequest.symbol = m_szSymbol;

pero al hacer esto tendremos que agregar algunas cosas al código para poder manejar las órdenes abiertas o pendientes a través del sistema CROSS ORDER, ya que tendremos el gráfico "incorrecto". Entendamos esto con un ejemplo. Podemos estar en el gráfico del índice completo ( IND ) y operando el Mini índice ( WIN ), pero MetaTrader 5 no mostrará la posición abierta o pendiente en WIN cuando se está utilizando en el gráfico IND, por lo que es necesario añadir algo de código con el fin de hacer esta presentación, esto se logra mediante la lectura de los valores de la posición y su uso para presentar una línea en el gráfico actual. Esto es muy interesante cuando se está operando y se quiere observar el historial del activo, en el caso de los contratos de futuros, estos tienen una fecha de vencimiento, pero utilizando el CROSS ORDER, se puede por ejemplo, operar el WIN ( mini índice ) usando el gráfico WIN$ ( que es el gráfico del historial del mini índice ), esto es perfectamente posible.

Lo siguiente a tener en cuenta son las siguientes líneas:

      TradeRequest.price         = NormalizeDouble(Price, m_Infos.nDigits);
      TradeRequest.sl            = NormalizeDouble(Stop, m_Infos.nDigits);
      TradeRequest.tp            = NormalizeDouble(Take, m_Infos.nDigits);

estas 3 líneas crearán los límites de la orden OCO, junto con el precio de apertura de la posición, en caso de operaciones a corto plazo, que pueden durar solo unos segundos, no es recomendable entrar en la operación sin utilizar órdenes OCO, ya que la volatilidad puede hacer que el precio pase de un punto a otro sin mucha dirección, utilizando esta estructura, el propio servidor de comercio se encargará de nuestra posición, pero no todo es perfecto. Aparecerá un orden OCO como se muestra en la imagen de abajo.

la misma orden aparecerá de la siguiente manera si abrimos el editor para manipular los datos:

Con estos datos completados, el servidor comercial se encargará de la orden, y tan pronto como llegue a uno de los puntos indicados como Perdida Máxima o Beneficio Máximo la orden será finalizada por el sistema de comercio, pero si retiramos el valor de Pérdida Máxima o Beneficio Máximo, la orden permanecerá abierta hasta que ocurra algún otro evento, y si está seleccionada como Day Trade, el sistema cerrará la orden al final del período de negociación del día; de lo contrario, permanecerá abierta hasta que cerremos o nos quedemos sin fondos para mantener la posición.

Algunos sistemas, respecto a EAs, usan órdenes para cerrar órdenes, es decir, una vez que la posición está abierta, se envía una contraorden del mismo volumen para cerrar la posición en un punto dado, pero esto no es muy adecuado en algunos escenarios, ya que si por alguna razón el activo sale a subasta durante la sesión de negociación, la orden pendiente puede descartarse y debe ser reemplazada, y esto complica el EA, ya que será necesario probar qué órdenes están o no activas, y si esto no se hace correctamente, causará dolor de cabeza, ya que el EA seguirá enviando pedidos uno tras otro sin ningún criterio.

   void Initilize(int nContracts, int FinanceTake, int FinanceStop, color cp, color ct, color cs, bool b1)
     {
      string sz0 = StringSubstr(m_szSymbol = _Symbol, 0, 3);
      double v1 = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) / SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
      m_Infos.Id = ChartID();
      m_Infos.TypeSymbol = ((sz0 == "WDO") || (sz0 == "DOL") ? WDO : ((sz0 == "WIN") || (sz0 == "IND") ? WIN : OTHER));
      m_Infos.nDigits = (int) SymbolInfoInteger(m_szSymbol, SYMBOL_DIGITS);
      m_Infos.Volume = nContracts * (m_VolMinimal = SymbolInfoDouble(m_szSymbol, SYMBOL_VOLUME_MIN));
      m_Infos.TakeProfit = AdjustPrice(FinanceTake * v1 / m_Infos.Volume);
      m_Infos.StopLoss = AdjustPrice(FinanceStop * v1 / m_Infos.Volume);
      m_Infos.IsDayTrade = b1;
      CreateHLine(m_Infos.szHLinePrice, m_Infos.cPrice = cp);
      CreateHLine(m_Infos.szHLineTake, m_Infos.cTake = ct);
      CreateHLine(m_Infos.szHLineStop, m_Infos.cStop = cs);
      ChartSetInteger(m_Infos.Id, CHART_COLOR_VOLUME, m_Infos.cPrice);
      ChartSetInteger(m_Infos.Id, CHART_COLOR_STOP_LEVEL, m_Infos.cStop);
     };

La rutina anterior es responsable de iniciar los datos del EA según lo indicado por el usuario y así crear la orden OCO, el único cambio real que deberá realizarse en esta rutina estará en la siguiente línea

m_Infos.TypeSymbol = ((sz0 == "WDO") || (sz0 == "DOL") ? WDO : ((sz0 == "WIN") || (sz0 == "IND") ? WIN : OTHER));

aquí es cuando agregamos un tipo de activo, además de los actuales, y necesitamos proporcionar algo específico que no es cubierto por el código actual.

      m_Infos.Volume = nContracts * (m_VolMinimal = SymbolInfoDouble(m_szSymbol, SYMBOL_VOLUME_MIN));
      m_Infos.TakeProfit = AdjustPrice(FinanceTake * v1 / m_Infos.Volume);
      m_Infos.StopLoss = AdjustPrice(FinanceStop * v1 / m_Infos.Volume);

las 3 líneas anteriores harán los ajustes necesarios para la correcta creación de la orden, nContracts es un factor de apalancamiento, es decir, debemos usar valores como 1, 2, 3, etc., es decir, no necesitamos saber el volumen mínimo que el activo necesita para negociarse, todo lo que realmente necesitamos es indicar el factor de apalancamiento de este volumen mínimo, por ejemplo: si un activo necesita un volumen mínimo de 5 contratos, al indicar un apalancamiento de 3 el sistema automáticamente sabrá que debe abrir una posición con 15 contratos, esto hace que sea mucho más fácil cuando se trabaja con activos que tienen diferentes niveles de apalancamiento. Las otras 2 líneas ajustarán en consecuencia lo que deberá ser el Take Profit y el Stop Loss basándose en lo financiero que el usuario está indicando, y esto se hará sobre el volumen a negociar, es decir, si el volumen aumenta manteniendo lo financiero, estos puntos disminuirán, si lo financiero aumenta manteniendo el volumen, estos puntos aumentarán, algo muy sencillo y directo, para que podamos ajustar fácilmente las cosas de forma que no tengamos que estar haciendo cálculos para colocar la posición, el propio EA hará los cálculos, o bien diremos qué activo, cuál será el apalancamiento, cuánto de fondos estamos arriesgando y buscando, y el EA posicionará la orden por nosotros de forma correcta.

   inline void MoveTo(int X, int Y, uint Key)
     {
      int w = 0;
      datetime dt;
      bool bEClick, bKeyBuy, bKeySell;
      double take = 0, stop = 0, price;
      bEClick  = (Key & 0x01) == 0x01;    //Clique esquerdo
      bKeyBuy  = (Key & 0x04) == 0x04;    //SHIFT Pressionada
      bKeySell = (Key & 0x08) == 0x08;    //CTRL Pressionada
      ChartXYToTimePrice(m_Infos.Id, X, Y, w, dt, price);
      ObjectMove(m_Infos.Id, m_Infos.szHLinePrice, 0, 0, price = (bKeyBuy != bKeySell ? AdjustPrice(price) : 0));
      ObjectMove(m_Infos.Id, m_Infos.szHLineTake, 0, 0, take = price + (m_Infos.TakeProfit * (bKeyBuy ? 1 : -1)));
      ObjectMove(m_Infos.Id, m_Infos.szHLineStop, 0, 0, stop = price + (m_Infos.StopLoss * (bKeyBuy ? -1 : 1)));
      if((bEClick) && (bKeyBuy != bKeySell))
         CreateOrderPendent(bKeyBuy, m_Infos.Volume, price, take, stop, m_Infos.IsDayTrade);
      ObjectSetInteger(m_Infos.Id, m_Infos.szHLinePrice, OBJPROP_COLOR, (bKeyBuy != bKeySell ? m_Infos.cPrice : clrNONE));
      ObjectSetInteger(m_Infos.Id, m_Infos.szHLineTake, OBJPROP_COLOR, (take > 0 ? m_Infos.cTake : clrNONE));
      ObjectSetInteger(m_Infos.Id, m_Infos.szHLineStop, OBJPROP_COLOR, (stop > 0 ? m_Infos.cStop : clrNONE));
     };

La rutina anterior mostrará la orden a crear, utilizará el movimiento del mouse para mostrar dónde se pondrá la orden, pero para que esto realmente suceda, debemos indicar si queremos comprar (presionando SHIFT) o vender (presionando CTRL), así, una vez que se produce un clic, se creará una orden pendiente en dado punto.

En caso de que queramos mostrar más datos, como el punto de cobertura, basta con añadir el objeto en esta rutina.

Hasta este punto tenemos todo el EA funcionando y siendo capaz de crear ordenes OCO, pero como no todo es perfecto...


El problema de las ordenes OCO

Las órdenes OCO tienen un problema, que no es culpa del sistema MetaTrader 5 o del servidor comercial, sino más bien de la volatilidad que está presente en el mercado en ocasiones. Teóricamente el precio debería moverse linealmente, sin rebotes, pero a veces tenemos una alta volatilidad que produce Gaps dentro de la vela, y cuando estas brechas se produzcan en el punto donde se encuentra el precio de la orden Stop Loss o Take Profit, estos puntos no se accionarán y, por tanto, la orden no se cerrará, también puede suceder que, durante el movimiento de estos puntos por parte del usuario, el precio se quede fuera de este túnel, cuyos límites están representados por el Stop y el Take, y si esto ocurre tampoco se cerrará la orden. Esto es muy peligroso, y es difícil predecir cuándo ocurrirá, pero como programador, hay que preverlo y crear mecanismos para minimizar los daños.

Para actualizar e intentar mantener el precio dentro del túnel o límites, usamos 2 rutinas. La primera se muestra a continuación:

   void UpdatePosition(void)
     {
      for(int i0 = PositionsTotal() - 1; i0 >= 0; i0--)
         if(PositionGetSymbol(i0) == m_szSymbol)
           {
            m_Take      = PositionGetDouble(POSITION_TP);
            m_Stop      = PositionGetDouble(POSITION_SL);
            m_IsBuy     = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
            m_Volume    = PositionGetDouble(POSITION_VOLUME);
            m_Ticket    = PositionGetInteger(POSITION_TICKET);
           }
     };

ella será invocada por OnTrade que es la rutina que MetaTrader 5 llama en cada cambio ejecutado en las posiciones. La siguiente rutina a utilizar es llamada por OnTick para comprobar y asegurarse de que el precio se mantiene en el túnel o dentro de los límites de la orden OCO, veámosla a continuación:

   inline bool CheckPosition(const double price = 0, const int factor = 0)
     {
      double last;
      if(m_Ticket == 0)
         return false;
      last = SymbolInfoDouble(m_szSymbol, SYMBOL_LAST);
      if(m_IsBuy)
        {
         if((last > m_Take) || (last < m_Stop))
            return ClosePosition();
         if((price > 0) && (price >= last))
            return ClosePosition(factor);
        }
      else
        {
         if((last < m_Take) || (last > m_Stop))
            return ClosePosition();
         if((price > 0) && (price <= last))
            return ClosePosition(factor);
        }
      return false;
     };

Esta rutina es muy crítica, ya que se ejecutará en cada variación de tick del activo, es decir, tiene que ser lo más simple posible para que el cálculo y las pruebas se realicen de la manera más eficiente, en la medida de lo posible. Pero tenga en cuenta que mientras mantenemos el precio dentro del túnel, también verificamos algo curioso, que puede eliminarse si lo deseamos, entraré en más detalles sobre esta prueba adicional en la siguiente sección. Dentro de esta rutina tenemos una llamada a una función, cuyo código se muestra a continuación:

   bool ClosePosition(const int arg = 0)
     {
      double v1 = arg * m_VolMinimal;
      if(!PositionSelectByTicket(m_Ticket))
         return false;
      ZeroMemory(TradeRequest);
      ZeroMemory(TradeResult);
      TradeRequest.action     = TRADE_ACTION_DEAL;
      TradeRequest.type       = (m_IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
      TradeRequest.price      = SymbolInfoDouble(m_szSymbol, (m_IsBuy ? SYMBOL_BID : SYMBOL_ASK));
      TradeRequest.position   = m_Ticket;
      TradeRequest.symbol     = m_szSymbol;
      TradeRequest.volume     = ((v1 == 0) || (v1 > m_Volume) ? m_Volume : v1);
      TradeRequest.deviation  = 1000;
      if(!OrderSend(TradeRequest, TradeResult))
        {
         MessageBox(StringFormat("Error Number: %d", TradeResult.retcode), "Nano EA");
         return false;
        }
      else
         m_Ticket = 0;
      return true;
     };

Esta función cerrará un volumen dado, es nuestra protección, pero recordemos, para que funcione tendremos que estar conectados, ya que se ejecuta en la plataforma cliente MetaTrader 5, y si por casualidad falla la conexión al servidor de negociación, esta función será completamente inútil, sin ningún propósito.

Pero al mirar estos 2 últimos códigos, vemos que podemos terminar un volumen dado en un punto específico, y al hacer esto estamos haciendo una exposición parcial o reduciendo nuestra exposición, así que entendamos cómo usar esta función.


Trabajando con ordenes parciales

Las parciales son algo que a muchos les gusta y quieren hacer cuando están operando, el EA presentado permite hacer esto, aunque no muestro cómo implementar dicho código, y la razón de esto es que las parciales son un problema aparte, pero si deseamos hacer esto, todo lo que necesitamos hacer es llamar a la rutina CheckPosition indicando el precio donde la orden se producirá y el volumen en que se llevará a cabo, el propio EA hará el resto.

El hecho de que diga que las parciales son un caso aparte, es porque son algo muy personal, es difícil crear algo lo suficientemente genérico para satisfacer a todos. El mero hecho de que podamos estar haciendo swing hará que una solución de matriz dinámica sea inviable, ya que para el day trade sería adecuado siempre y cuando no cierre el EA mientras está posicionado, pero si por alguna razón necesita cerrar el EA, la solución de matriz no es muy adecuada, se deberá utilizar algún medio de almacenamiento y la forma en que se formatearán los datos dentro del archivo dependerá de lo que pretendamos hacer.

Una cosa es segura, debemos evitar tanto como sea posible hacer divisiones usando el orden de apertura de posición, el riesgo de que esto dé un dolor de cabeza es enorme. Paso a explicar: digamos que estamos posicionados con 3x de apalancamiento en la mano de compra y deseamos obtener una ganancia con 2x mientras seguimos apalancados en 1x, la forma de hacerlo es vender 2x de apalancamiento, sin embargo, si hacemos que un EA envíe una orden de venta a precio de mercado, puede ser que la volatilidad haga que el precio alcance su Take Profit antes de que la orden de venta se ejecute realmente, esto provocará que entremos en una posición corta en una tendencia que puede ser desfavorable. Sin embargo, es posible que queramos enviar una orden Sell Limit o Sell Stop para ejecutar este desapalancamiento en 2x, por un lado esto parece a primera vista adecuado, pero pensémoslo bien, porque si la orden se envía antes de que el precio llegue al punto del lote parcial, podemos llegar a tener una sorpresa muy desagradable, y nuestra posición abierta venir a ser parada y un poco más tarde la orden colocada venir a ser abierta para, así, aumentar nuestras pérdidas, sin embargo si la volatilidad viene con mucha fuerza, puede suceder lo mismo que sucede en un caso de entrar a precio de mercado, como se explicó anteriormente.

Así que en mi visión de las cosas, como programador, la mejor opción cuando se quiere hacer ordenes parciales es emular el envío de ordenes a precio de mercado, pero cuidando que el volumen nunca supere el volumen aún abierto y eso es lo que estoy aportando con este EA, una forma de emular tal cosa, pero la forma de usar parciales, se la dejo a cada uno.


Conclusión

Construir un EA para el trading no es algo tan trivial como algunos piensan, la cosa en sí es bastante simple frente a algunos otros problemas que a menudo enfrentamos en la programación, sin embargo crear algo que sea lo suficientemente estable y confiable al punto de arriesgar nuestro dinero al usar la solución, esto es algo que a menudo es un reto. Pero aquí propuse algo que facilitará la vida de los que están empezando a usar MetaTrader 5 y no tienen los conocimientos necesarios sobre cómo programar un EA, eso es porque el EA no abrirá órdenes sino que sólo ayudará a hacer una orden abierta de una manera más fiable, una vez colocada la orden, el EA no tendrá más cosas que hacer, el trabajo lo empieza a hacer MetaTrader 5 y ya no el EA, salvo las partes declaradas arriba durante la explicación del código.

Este EA se puede mejorar en muchos aspectos para poder poner a funcionar algún conjunto de parámetros, es cierto que habrá que añadir más código para que sea más independiente de  MetaTrader 5, pero esto depende de la creatividad y necesidad de cada uno.

El gran acierto de este EA es el hecho de que utiliza el propio MetaTrader 5 para hacer las cosas que no están en su código, y con lo cual es extremadamente estable y fiable.





Traducción del portugués realizada por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/pt/articles/10085

Archivos adjuntos |
EA_Nano_rvl_1.1.mq5 (23.44 KB)
Casi un constructor para crear asesores Casi un constructor para crear asesores
Ofrecemos nuestro propio conjunto de funciones comerciales como asesor listo para usar. El método presentado nos permite obtener multitud de estrategias comerciales con solo añadir indicadores y cambiar los parámetros de entrada.
Combinatoria y teoría de la probabilidad en el trading (Parte V): Análisis de curvas Combinatoria y teoría de la probabilidad en el trading (Parte V): Análisis de curvas
En este artículo, hemos decidido investigar un poco sobre la conversión de varios estados en estados dobles. El objetivo principal es el propio análisis y las conclusiones útiles que extraigamos, que nos pueden ayudar en el desarrollo posterior de algoritmos comerciales escalables basados ​​en la teoría de la probabilidad. Obviamente, no hemos podido evitar el uso de matemáticas, pero, teniendo en cuenta la experiencia de artículos anteriores, hemos observado que la información general resulta mucho más útil que los detalles en sí.
Gráficos en la biblioteca DoEasy (Parte 88): Colección de objetos gráficos - matriz dinámica bidimensional para almacenar propiedades de objetos que cambian dinámicamente Gráficos en la biblioteca DoEasy (Parte 88): Colección de objetos gráficos - matriz dinámica bidimensional para almacenar propiedades de objetos que cambian dinámicamente
En este artículo, crearemos una clase de matriz multidimensional dinámica con capacidad de cambiar la cantidad de datos en cualquier dimensión. Basándonos en la clase creada, crearemos una matriz dinámica bidimensional para guardar algunas propiedades de objetos gráficos que cambian dinámicamente.
Usando AutoIt con MQL5 Usando AutoIt con MQL5
Descripción breve. En este artículo, exploraremos la creación de scripts del terminal MetraTrader 5 integrando MQL5 con AutoIt. En el presente material, abarcaremos cómo automatizar varias tareas manipulando la interfaz de usuario de los terminales, y también presentaremos una clase que utiliza la biblioteca AutoItX.