Русский Português
preview
Simulación de mercado (Parte 01): Orden cruzada (I)

Simulación de mercado (Parte 01): Orden cruzada (I)

MetaTrader 5Probador |
216 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior, Desarrollo de un sistema de repetición (Parte 78): Un nuevo Chart Trade (V), mostré cómo el Asesor Experto interpretará la información proporcionada por Chart Trade. La información que Chart Trade transmitirá dependerá de la interacción del usuario con él. Es decir, cuando el usuario interactúe con el botón de compra, venta o cierre de posición, un mensaje será transmitido al gráfico. Una de las tareas del Asesor Experto en el gráfico es interceptar, decodificar y ejecutar la tarea indicada en el mensaje.

Aunque este mecanismo sea bastante simple y también bastante confiable, tenemos un pequeño problema. Bueno, en realidad, no se trata exactamente de un problema, sino de un inconveniente. Es necesario resolverlo antes de que enviemos órdenes al servidor de trading.

Si tú no tienes idea de qué se trata, tal vez sea porque no operas con símbolos, o mejor dicho, contratos futuros. Estos tipos de símbolos tienen un plazo de validez. Muchas veces, tenemos dos tipos siendo negociados: un tipo completo, definido por tener un volumen mayor, y el tipo mini, que normalmente puede entenderse como una fracción del tipo completo, facilitando ciertos tipos de estrategia.

No entraré aquí en el mérito ni en la descripción de tales estrategias. Pero el hecho es que muchas veces necesitamos un número menor de contratos para crear nuestra estrategia. Quien se interese por el tema debe investigar sobre estrategias de HEDGE. Para nosotros, programadores, lo que realmente nos interesa es cómo realizar una operación en el minicontrol con el contrato completo en el gráfico.

Sin embargo, además de este detalle, tenemos otro problema relacionado con estrategias a largo plazo. Nuevamente, no entraré en los detalles, pero siempre que un contrato vence, y este vencimiento es siempre conocido por todos, un nuevo contrato, o mejor dicho, una nueva serie, se inicia. Esto representa un gran problema para quien opera en plazos más largos, ya que implica el uso de varias series subsecuentes. Esto porque todos los indicadores y medias tendrán que ser recalculados.

Para comprender a fondo este problema, vamos a usar el contrato de dólar futuro, que se negocia en B3 (Bolsa de Valores de Brasil). Este contrato tiene vencimiento mensual. Es decir, cada mes, una serie se cierra y una nueva serie comienza. Considerando que tenemos, en promedio, 20 días de negociación (cinco días hábiles, en promedio, durante cuatro semanas por mes), tendremos serios problemas al utilizar, por ejemplo, una media móvil de 20 períodos, pues, cuando la media llegue a ser efectivamente calculada , el contrato ya habrá vencido y uno nuevo habrá comenzado. Eso considerando solo la media de 20 períodos; sin contar otros indicadores que necesitan de períodos aún mayores para poder ser representados gráficamente. Es decir, un gran problema.

Por esta razón, tenemos la posibilidad de usar el historial del contrato futuro. Sin embargo, el hecho de que podamos usar el historial no resuelve el problema. En realidad, crea otros problemas para nosotros, programadores. Recuerda que, para el operador del contrato, no importa cómo el servidor recibirá la información o cómo será representada gráficamente en el gráfico. Él solo quiere que la información y sus operaciones sean mostradas y ejecutadas. Nos corresponde a nosotros, programadores, resolver cómo representar gráficamente la información y transmitir las órdenes hechas en el gráfico al servidor de trading.

En el artículo Desarrollo de un EA comercial desde cero (Parte 11): Sistema de órdenes cruzadas, expliqué algunos detalles al respecto. Sin embargo, aquí en esta secuencia, que también implica el uso de un sistema de repetición/simulador, nuestro problema se vuelve aún mayor. No obstante, seguiremos el ejemplo de Napoleón: dividir para conquistar. Vamos a dividir este problema para lograr desarrollar la solución y, así, conquistar terreno para finalmente desarrollar un sistema de órdenes simulado. Empezaremos, entonces, abordando la cuestión del uso de indicadores en contratos futuros. Como el dólar futuro es el caso más extremo que conozco, nos enfocaremos en él. Pero tú debes entender que lo que se explicará puede y debe ser adaptado a otros casos igualmente complicados o que involucren los mismos conceptos aplicados aquí, como operaciones que involucren otros tipos de contratos.


Comenzar a implementar la solución

En el caso del artículo mencionado arriba, en el que se desarrolló un sistema de órdenes cruzadas (o sistema de órdenes cruzadas), hacer algunos ajustes para cubrir otros tipos de contrato era relativamente bastante complicado. Sin embargo, por motivos prácticos, aquí haremos las cosas de manera que tales ajustes sean mucho más simples. No para el operador, sino para nosotros, que necesitamos programarlo todo. El operador tendrá que adaptarse a lo que haremos. Pero, para él, será mucho más sencillo, pues le daremos la posibilidad de seleccionar si desea operar con el contrato completo o con el mini.

Para hacer que las cosas funcionen, al menos en este primer momento, en que la comunicación se dará con el servidor real de trading, necesitaremos hacer algunos pequeños y puntuales cambios en el código ya existente. Vamos a empezar entendiendo el siguiente hecho: como mencioné en la introducción, el mejor gráfico a ser utilizado es el del historial del contrato. Sin embargo, el historial del contrato no puede ser negociado directamente.

Para hacer esto, necesitamos usar un sistema que nos permita encaminar las órdenes dadas en el gráfico del historial al contrato deseado por el operador. En este caso, recuerda que el operador podrá desear realizar la operación en el contrato completo o en el mini. Por ahora, no nos preocuparemos con eso. Primero, necesitamos entender que el contenido presente en el gráfico es el mismo del historial del contrato. Punto final.

En el caso de B3 (Bolsa de Valores de Brasil), los contratos futuros tienen seis nomenclaturas diferentes. Esto se aplica a cada contrato específico. Es decir, para los contratos completos son seis tipos, y para los mini también son seis tipos. Es decir, una gran complicación. Pero, a pesar de la aparente complejidad, en realidad son tres tipos divididos en dos modelos.

Esto facilita bastante las cosas para nosotros. De cualquier manera, te aconsejo estudiar las diferencias entre estos tres tipos, pues la información presentada en el gráfico difiere mucho entre cada uno. Si tú solo vas a programar la solución, no dejes de advertir al operador sobre estos tres tipos, ya que muchos los desconocen por completo. Ahora bien, si tú no solo vas a programar la solución, sino también a hacer uso de ella, redoblo el consejo, porque podrías salir muy perjudicado si no entiendes las diferencias entre los tipos.

Entonces, sabemos que hay tres tipos de nomenclatura, con dos variaciones en cada uno. De acuerdo. Pero, para nosotros, programadores, eso no es lo que importa. Eso es importante para el operador. Para nosotros, lo que importa es si existe alguna regla en esta nomenclatura. Y, si la hay, ¿cómo podemos hacer uso de esa regla para poder crear un sistema de órdenes cruzadas?

La regla existe, de hecho. Y ya la estamos utilizando. Y lo hacemos desde hace algún tiempo. Mira cómo se está haciendo ese uso en el fragmento de código a continuación.

38. //+------------------------------------------------------------------+
39.       void CurrentSymbol(void)
40.          {
41.             MqlDateTime mdt1;
42.             string sz0, sz1;
43.             datetime dt = macroGetDate(TimeCurrent(mdt1));
44.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
45.       
46.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
47.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
48.             switch (eTS)
49.             {
50.                case DOL   :
51.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
52.                case IND   :
53.                case WIN   : sz1 = "GJMQVZ";       break;
54.                default    : return;
55.             }
56.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
57.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
58.          }
59. //+------------------------------------------------------------------+

Fragmento de código del archivo C_Terminal.mqh

Este fragmento forma parte del código fuente del archivo de encabezado C_Terminal.mqh. Observa que, en la línea 44, definimos los nombres de los contratos futuros que serán cubiertos. Puedes agregar otros nombres a esta lista, si deseas trabajar con otros contratos, como maíz, ganado, S&P, euro, etc. Pero recuerda observar las reglas de nomenclatura para cada contrato, con el fin de saber cómo definir el contrato vigente. Porque este procedimiento, como se muestra arriba, no devuelve un contrato pasado, mucho menos un contrato que estará vigente dentro de dos o más vencimientos. Siempre devuelve el contrato actualmente vigente.

Para hacer esto, captura los tres primeros caracteres del nombre del símbolo en la línea 46. Independientemente del símbolo, siempre capturará los tres primeros caracteres. Esto se debe a que la regla de nomenclatura de B3 (Bolsa de Valores de Brasil) indica que los tres primeros caracteres del nombre del símbolo indican qué símbolo estamos utilizando, ya que el símbolo en el gráfico puede no ser necesariamente un contrato futuro. En esta misma línea 46, el nombre del símbolo se almacena en la variable, que contendrá el nombre para todos los demás códigos. Presta atención a este hecho.

Ahora, en la línea 47, hacemos un escaneo en la enumeración de los contratos que estamos implementando. Observa que la idea aquí es capturar el nombre correcto. Por eso, en la enumeración de la línea 44, necesitamos que el nombre se parezca al nombre del contrato. Como B3 (Bolsa de Valores de Brasil) utiliza caracteres en mayúsculas, necesitamos que también estén en mayúsculas en la enumeración. Cuando el contrato sea encontrado o la lista termine, el bucle de la línea 47 finalizará.

Entonces, podremos probar, en la línea 48, qué valor fue encontrado. Si no se encuentra ninguna coincidencia, el código será ejecutado en la línea 54. En cualquier otro caso, iniciaremos la creación de la nomenclatura del contrato. La nomenclatura final se da en la línea 57, donde probamos y verificamos si el contrato creado por el procedimiento es el actual. Es decir, el propio procedimiento escaneará la lista de posibles contratos futuros hasta encontrar el contrato actualmente vigente.

Sin embargo, hay un punto en todo este procedimiento al cual tú debes prestar atención. Usará el nombre base del contrato en función del nombre del símbolo. Es decir, solo podrás transcribir el historial del contrato al nombre del contrato actual. No es posible hacer la transición del historial del contrato completo al contrato actual vigente del contrato mini, conforme al código mostrado arriba. Este es el inconveniente que necesitamos resolver en este artículo.

Al hacer esto, daremos al operador la posibilidad de seleccionar si desea usar el contrato completo o el contrato mini en la operación, usando el historial de uno de los contratos en el gráfico. Muy bien, vamos entonces a hacer esto de forma que sea necesario modificar y, si es necesario, agregar el mínimo de código, ya que, cuanto más código se modifique y agregue, mayor será la posibilidad de errores.

Para cumplir este objetivo, el nuevo código del fragmento anterior fue modificado y quedó como se muestra a continuación:

38. //+------------------------------------------------------------------+
39.       void CurrentSymbol(bool bUsingFull)
40.          {
41.             MqlDateTime mdt1;
42.             string sz0, sz1;
43.             datetime dt = macroGetDate(TimeCurrent(mdt1));
44.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
45.       
46.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
47.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
48.             switch (eTS)
49.             {
50.                case DOL   :
51.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
52.                case IND   :
53.                case WIN   : sz1 = "GJMQVZ";       break;
54.                default   : return;
55.             }
56.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
57.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
58.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
59.          }
60. //+------------------------------------------------------------------+

Fragmento de código del archivo C_Terminal.mqh

Noten que los cambios fueron mínimos. El primero de ellos fue agregar un parámetro que será recibido por la función, en la línea 39. Tal argumento tiene como propósito informar al procedimiento si el nombre del contrato que se va a construir debe ser el contrato completo o el minicontrol. Esta elección será hecha por el operador. Nosotros, como programadores, debemos ofrecer al operador la oportunidad de usar el gráfico que desee. Esto, por supuesto, siempre que los datos del símbolo que se colocará en el gráfico representen, de alguna forma, el contrato que se va a negociar. Podemos, como programadores, hacer cosas mucho más exóticas, pero no vamos a complicar lo que tenemos que hacer.

Además de este cambio en la línea 39, se agregó una nueva línea. Aunque tal adición no sea necesaria, ya que podríamos modificar el código, opté por agregarla para facilitar la explicación y, principalmente, para que tú, querido lector, consigas entender lo que se está haciendo. Podría colocarse en el lugar en que se encuentra la variable sz0 en la línea 58 y funcionaría de la misma manera. Sin embargo, la explicación quedaría muy confusa y de difícil comprensión.

Entonces, para quien no está logrando entender lo que esta línea 56 está haciendo con la variable sz0, presentaremos la explicación detallada. Observa que estamos ignorando cualquier cosa relacionada con el nombre del símbolo. Lo que estamos haciendo es transformar la enumeración de la línea 44 en una cadena. Esto se debe a que MQL5 nos permite hacer eso al usar la función EnumToString.

Ahora viene un detalle que tal vez pueda dificultarte las cosas, en caso de que estés trabajando con algún contrato futuro que no tenga un contrato completo y un mini. Este tipo de situación es bastante común cuando se habla de COMMODITIES. Pero, para lo que quiero mostrar, que son índices y moneda (en este caso, el dólar), existen ambos contratos: el mini y el completo.

La enumeración siempre comienza con un valor igual a cero, salvo que definas un valor para el inicio de la enumeración. Siendo así, los minicontratos se definen como valores pares, y los contratos completos, como valores impares. Es importante entender esto. Los valores están en forma binaria, y este es otro punto importante. Pero lo principal es saber cómo aislar un bit en particular. En el caso de los valores binarios, el bit menos significativo, es decir, el más a la derecha, indica si el valor es par o impar. Entonces, al hacer una operación AND para aislar ese bit menos significativo, podemos verificar si el valor es par o impar. Recuerda que, en la enumeración, los minicontratos son pares y los contratos completos son impares.

Entonces, si el valor es impar, se ejecutará la primera parte del operador ternario. Si el valor es par, se ejecutará la segunda parte del operador ternario. Hasta aquí, creo que tú has entendido. Entonces, hacemos una nueva prueba en cada parte del operador ternario, de modo que tendremos un nuevo operador ternario. En este segundo operador ternario, la prueba tiene como objetivo ajustar el valor de la variable eTS para que indique el nombre correcto que debe usarse en el contrato.

Entonces, vamos a entender lo siguiente: si el contrato es, por ejemplo, el WDO, el valor de la variable eTS será dos, o sea, un valor par. En este caso, se ejecutará la segunda parte del primer operador ternario. Cuando esta segunda parte sea ejecutada, un nuevo operador ternario hará una nueva prueba. Verificará si, en la llamada del procedimiento, queremos usar el contrato completo o el minicontrol.

Si queremos usar el contrato completo, este segundo operador ternario hará que la variable eTS sea incrementada en uno. Es decir, el valor que antes era dos pasará a ser tres, y, al observar la tercera posición en la enumeración, veremos que el dato es el DOL. Cuando MQL5 ejecute la función EnumToString, ese valor tres será transformado en la cadena "DOL", permitiendo así que el nombre del contrato sea el del contrato completo, incluso si, en el gráfico, el símbolo visualizado es el historial del contrato del mini dólar.

Lo mismo aplica a la inversa. Si tú estás en el gráfico del historial del contrato del dólar completo e informas al procedimiento que deseas operar el contrato del mini dólar, las pruebas harán que el primer operador ternario sea verdadero, haciendo que se ejecute la primera parte. Cuando esta primera parte sea ejecutada, el segundo operador ternario fallará, haciendo que el valor de eTS, que era impar (tres), sea restado en una unidad, convirtiéndose así en dos.

En resumen: Resumiendo: el valor encontrado por la línea 47 será reajustado en la línea 56, de modo que el nombre del contrato corresponda al esperado por el operador, ya sea que quiera operar el minicontrol o el contrato completo. El único requisito es que el gráfico sea del historial de uno de los contratos.

Muy bien, muy bueno, muy bonito. Pero supongamos que exista un caso en el que no tengamos un minicontrol, es decir, que solo tengamos el contrato futuro completo. ¿Cómo podemos lidiar con esta situación? En este caso, podrías pensar que existen dos soluciones posibles, ¿verdad? Sin embargo, la solución es solo una: si tú estás pensando en duplicar el valor en la enumeración para obtener un valor par e impar, el compilador no lo aceptará. Pero puedes hacer lo siguiente: colocar los contratos en un orden lógico en la enumeración, de modo que, al probar un determinado valor, en caso de que no existan minicontratos, la variable sz0 no necesite cambiar. Entonces, a partir de este valor debidamente definido y conocido, el cambio no ocurrirá. Esto te obligará a hacer una prueba extra, pero no es nada difícil ni complicado.

Con esto, solucionamos la primera parte del problema. Sin embargo, el problema no está totalmente resuelto. Para resolver un detalle más, necesitamos cambiar otra cosa en el archivo de encabezado C_Terminal.mqh. Se trata del constructor de la clase, pues es él el responsable de llamar al procedimiento mostrado arriba. El constructor original deberá ser modificado conforme a la versión mostrada a continuación.

72. //+------------------------------------------------------------------+      
73.       C_Terminal(const long id = 0, const uchar sub = 0, const bool bUsingFull = false)
74.          {
75.             m_Infos.ID = (id == 0 ? ChartID() : id);
76.             m_Mem.AccountLock = false;
77.             m_Infos.SubWin = (int) sub;
78.             CurrentSymbol(bUsingFull);
79.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
80.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
81.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
82.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
83.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
84.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
85.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
86.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
87.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
88.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
89.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
90.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
91.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
92.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
93.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
94.             ChartChange();
95.          }
96. //+------------------------------------------------------------------+

Fragmento de código del archivo C_Terminal.mqh

Observa que son dos cambios bastante simples. El primero es en la línea 73, donde agregamos un nuevo parámetro. Ese nuevo parámetro se utiliza en la línea 78, donde llamamos al procedimiento explicado anteriormente. Observa que, por defecto, estoy definiendo que daremos prioridad al uso de minicontratos. Sin embargo, queremos y deseamos que el operador haga la elección que mejor le convenga. Así, serán necesarios algunos pequeños ajustes en determinados puntos del código.

Ya que aún no estamos modificando el código del Asesor Experto, necesitamos y vamos a hacer el cambio en el código de Chart Trade. Pero, para separar mejor las cosas, vamos a ver esto en un nuevo tema.


Convertir Chart Trade en un sistema de órdenes cruzadas

Los cambios en Chart Trade para que pueda trabajar con el sistema de órdenes cruzadas son bastante simples. Aunque tú puedas desear agregar algún objeto que permita al operador cambiar directamente el sistema de órdenes cruzadas, no haré eso aquí. La idea es hacer el mínimo posible de cambios en el código. Agregar un objeto para que el operador pueda cambiar directamente el tipo de orden cruzada en Chart Trad nos obligaría a agregar mucho más código solo para cubrir dicha funcionalidad. Sin embargo, podemos permitir que el operador cambie el sistema de órdenes cruzadas al modificar la configuración del indicador. Además de ser muy simple, este cambio traerá poca modificación en el código ya existente. Lo primero que haremos está mostrado en el siguiente código.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.80"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12536"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
12. //+------------------------------------------------------------------+
13. #define def_ShortName "Indicator Chart Trade"
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. enum eTypeContract {MINI, FULL};
18. //+------------------------------------------------------------------+
19. input ushort         user01 = 1;         //Leverage
20. input double         user02 = 100.1;     //Finance Take
21. input double         user03 = 75.4;      //Finance Stop
22. input eTypeContract  user04 = MINI;      //Cross order in contract
23. //+------------------------------------------------------------------+
24. int OnInit()
25. {
26.    chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03, (user04 == FULL));
27.    
28.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
29. 
30.    return INIT_SUCCEEDED;
31. }
32. //+------------------------------------------------------------------+
33. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
34. {
35.    return rates_total;
36. }
37. //+------------------------------------------------------------------+
38. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
39. {
40.    if (_LastError < ERR_USER_ERROR_FIRST) 
41.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
42. }
43. //+------------------------------------------------------------------+
44. void OnDeinit(const int reason)
45. {
46.    switch (reason)
47.    {
48.       case REASON_INITFAILED:
49.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
50.          break;
51.       case REASON_CHARTCHANGE:
52.          (*chart).SaveState();
53.          break;
54.    }
55. 
56.    delete chart;
57. }
58. //+------------------------------------------------------------------+

Código fuente del Indicador Chart Trade

Observa que fue añadida la línea 17, donde hay una enumeración. Esta tiene por finalidad ayudar al operador o usuario a definir qué será utilizado. Nota que estamos haciendo uso de esta enumeración en la línea 22. Este es el punto en que el operador o usuario elegirá si el Asesor Experto usará el contrato completo o el minicontrol. Aquí hay una complicación, ya que lo ideal sería hacer la selección en el Asesor Experto, y no aquí en Chart Trade. Sin embargo, como por ahora Chart Trade y el Asesor Experto son dos entidades distintas y separadas, procederemos así.

Sin embargo, el problema no está aquí en Chart Trade o en el Asesor Experto, ya que, como viste en el artículo anterior, es posible controlarlo a través de Chart Trade. El problema está realmente en otra cosa que será desarrollada más adelante. Ahí es donde realmente tenemos el gran problema, ya que todo pasará por el Asesor Experto. Por lo tanto, lo ideal sería configurar las cosas en él. No obstante, por ahora, y solo para demostración, ya que Chart Trade y el Asesor Experto están separados, dejaremos las cosas así.

Ese valor configurado se utiliza en la línea 26. Pero observa que estamos pasando un valor booleano dentro del constructor, y no un valor numérico. ¿Por qué? Porque, para el usuario, un valor booleano puede no ser tan explicativo. Para nosotros, programadores, un valor booleano es bastante expresivo, ya que solo estamos probando dos condiciones: tú, usuario, ¿vas a usar un contrato completo o un minicontrol? Por eso, el valor booleano es adecuado para nosotros, programadores. Así pues, podemos pasarlo al constructor de la clase para entender cómo esto se reflejará en el código, ya que necesitaremos alterar únicamente el constructor. Vamos a ver esto en un fragmento que puede mostrarse a continuación.

213. //+------------------------------------------------------------------+
214.       C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop, const bool bUsingFull)
215.          :C_Terminal(0, 0, bUsingFull)
216.          {
217.             m_Mouse = MousePtr;
218.             m_Info.IsSaveState = false;
219.             if (!IndicatorCheckPass(szShortName)) return;
220.             if (!RestoreState())
221.             {
222.                m_Info.Leverage = Leverage;
223.                m_Info.IsDayTrade = true;
224.                m_Info.FinanceTake = FinanceTake;
225.                m_Info.FinanceStop = FinanceStop;
226.                m_Info.IsMaximized = true;
227.                m_Info.minx = m_Info.x = 115;
228.                m_Info.miny = m_Info.y = 64;
229.             }
230.             CreateWindowRAD(170, 210);
231.             AdjustTemplate(true);
232.          }
233. //+------------------------------------------------------------------+

Fragmento del archivo C_ChartFloatingRAD.mqh

Aquí, la cosa es tan simple como en el constructor de la clase C_Terminal. Solo añadimos un nuevo parámetro que será recibido por el constructor, en la línea 214, y lo pasamos al constructor de la clase C_Terminal, en la línea 215. Más simple que eso, imposible. Tanto que ni necesito explicar los detalles involucrados.

Aunque esto sea muy bueno, necesitamos hacer un pequeño cambio más. En realidad, necesitamos añadir un código a la clase C_ChartFloatingRAD. Esto para que Chart Trade consiga decirle al Asesor Experto lo que está ocurriendo, o mejor dicho, lo que el usuario estará esperando operar. Tal cambio puede verse en el fragmento a continuación.

330.       case MSG_BUY_MARKET:
331.          ev = evChartTradeBuy;
332.       case MSG_SELL_MARKET:
333.          ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
334.       case MSG_CLOSE_POSITION:
335.          if ((m_Info.IsMaximized) && (sz < 0))
336.          {
337.             string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'),
338.                                         m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
339.             PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
340.             EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp);
341.          }
342.       break;

Fragmento del archivo C_ChartFloatingRAD.mqh

En este fragmento, el cambio es tan sutil que puede pasar desapercibido. Está en la línea 337, donde añadimos un nuevo valor que será transmitido al Asesor Experto. Tal valor tiene por objetivo informar al Asesor Experto el nombre del símbolo o contrato visualizado en Chart Trade. Ya aviso de entrada que este cambio forzará otro cambio en el Asesor Experto. Pero eso será visto después.


Consideraciones finales

Aunque este artículo muestra lo que es posible hacer dentro de MQL5, hay otros problemas que deben ser resueltos, precisamente por lo que se hizo aquí. Este tipo de cosa está lejos de ser simple y de resolverse completamente y con facilidad. Esto porque el uso de un sistema de órdenes cruzadas, que permite al usuario de Chart Trade informar al Asesor Experto que el gráfico del símbolo no representa el símbolo que se va a operar, causa enormes problemas. Muchos de esos problemas no se deben exactamente a Chart Trade o al Asesor Experto. Eso ya lo puedo dejar claro.

El problema de hecho ocurre cuando hacemos uso de algo que tú, querido lector, aún no has visto, ya que todavía no mencioné ninguna parte del código que deberá ser creada. No obstante, debo informar que permitir al usuario definir, en Chart Trade, qué tipo de contrato será utilizado no es la mejor solución. Al menos no lo veo así en este momento.

Tal vez lo que necesita ser mostrado aún sufrirá cambios que harán que la definición en Chart Trade sea algo interesante y sostenible. Todavía estoy pensando en una forma simple de mostrar las cosas. Implementar una solución personal es simple y rápido, pero implementar algo que deberá ser explicado y comprendido es complicado y laborioso. Así que, esperen futuros cambios en este sistema Chart Trade – Asesor Experto, porque con seguridad ocurrirán.

En el video abajo, puedes ver cómo es el proceso en el gráfico.


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

Archivos adjuntos |
Anexo.zip (490.53 KB)
Simulación de mercado (Parte 02): Orden cruzada (II) Simulación de mercado (Parte 02): Orden cruzada (II)
A diferencia de lo que se vio en el artículo anterior, aquí vamos a hacer el control de selección en el Asesor Experto. Aunque esta no es aún una solución definitiva, nos servirá por ahora. Así que acompaña el artículo para entender cómo implementar una de las soluciones posibles.
Creación de un Asesor Experto MQL5 basado en la estrategia PIRANHA utilizando las Bandas de Bollinger Creación de un Asesor Experto MQL5 basado en la estrategia PIRANHA utilizando las Bandas de Bollinger
En este artículo, creamos un Asesor Experto (Expert Advisor, EA) en MQL5 basado en la estrategia PIRANHA, utilizando Bandas de Bollinger para mejorar la efectividad comercial. Discutimos los principios clave de la estrategia, la implementación de la codificación y los métodos de prueba y optimización. Este conocimiento le permitirá implementar el EA en sus escenarios comerciales de manera efectiva.
Codificación ordinal para variables nominales Codificación ordinal para variables nominales
En este artículo, analizamos y demostramos cómo convertir predictores nominales en formatos numéricos adecuados para algoritmos de aprendizaje automático, utilizando tanto Python como MQL5.
Del básico al intermedio: Sobrecarga Del básico al intermedio: Sobrecarga
Este tal vez será el artículo más confuso para ti, principiante. Ya que aquí mostraré que no siempre tendremos, en un mismo código, todas las funciones y procedimientos con nombres exclusivos. Podemos, sí, tener funciones y procedimientos con un mismo nombre, y esto se conoce como sobrecarga. El contenido expuesto aquí tiene un propósito puramente didáctico. En ningún caso debe considerarse una aplicación cuya finalidad no sea el aprendizaje y el estudio de los conceptos mostrados.