Simulación de mercado: Position View (II)
Introducción
Hola a todos y bienvenidos a un nuevo artículo de la serie sobre cómo construir un sistema de repetición/simulación.
En el artículo anterior, Simulación de mercado: Position View (I), empezamos a hablar sobre la posibilidad de construir e implementar un indicador que nos permita visualizar posiciones abiertas directamente en el gráfico del símbolo. Aunque muchos imaginan que no es algo necesario, ya que podemos contar con la ayuda de MetaTrader 5 para ello, esto no se aplica cuando usamos un modelo operativo de orden cruzada o incluso, como será nuestro caso específico, un sistema de repetición/simulador.
Como se mencionó, existen algunos problemas cuando empezamos a trabajar con muchos objetos en el gráfico, especialmente cuando estos, de alguna forma, tendrán alguna relación entre sí, lo que hace que todo el trabajo resulte bastante complicado de implementar y desarrollar. Muy bien, pero aquí nuestro foco no es exponer los problemas que encontramos, sino mostrar alguna solución que decidimos implementar. Lo que empezaremos a ver, como vengo diciendo desde hace ya algún tiempo, no será una solución definitiva ni la única posible. Lo que propongo es que tú, querido lector, te detengas a pensar un poco antes de empezar a crear códigos y más códigos sin un motivo aparente.
Como el tema que vamos a tratar es bastante amplio, no pretendo extenderme mucho en esta introducción. Mi intención es que, al final de este artículo, hayamos producido algo un poco mejor de lo que se vio en el artículo anterior. Entonces, manos a la obra.
Preparando el camino para la implementación
Muy bien, el indicador que vimos en el artículo anterior solo nos informaba los datos de una posición abierta. Sin embargo, esto se hacía en el terminal, en formato de texto. Eso no parece tan motivador, ya que lo que muchos, incluido yo, desean es que la información se muestre directamente en el gráfico. Ese será el objetivo de este artículo. Todo lo que necesitamos hacer es crear un objeto de línea horizontal, cuya coordenada sea el precio, y aplicar en esa coordenada el valor que obtuvimos en el artículo anterior. Algo muy simple y directo. Sin embargo, surgirán algunos problemas al hacer esto. Pero lo veremos con calma.
Primero, entonces, vamos a implementar estos conjuntos de líneas. Y sí, necesitamos tres líneas que el indicador de posición deberá mostrar. Una línea para el precio de apertura de la posición y otras dos líneas extra: una para el nivel en el que se encuentra el stop y una última para el nivel del take. Pero, para crear esas líneas, usaremos lo que ya tenemos implementado en nuestro código de repetición/simulador.
Muy bien, como tú, querido lector, podrías querer organizar esto de una manera distinta de la que voy a presentar, empecemos creando una lista de prioridades. Esta puede verse en el siguiente código.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_VERSION_DEBUG 05. //+------------------------------------------------------------------+ 06. #ifdef def_VERSION_DEBUG 07. #define macro_DEBUG_MODE(A) \ 08. Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A)); 09. #else 10. #define macro_DEBUG_MODE(A) 11. #endif 12. //+------------------------------------------------------------------+ 13. #define def_SymbolReplay "RePlay" 14. #define def_MaxPosSlider 400 15. #define def_MaskTimeService 0xFED00000 16. #define def_IndicatorTimeFrame (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96)))) 17. #define def_IndexTimeFrame 4 18. //+------------------------------------------------------------------+ 19. union uCast_Double 20. { 21. double dValue; 22. long _long; // 1 Information 23. datetime _datetime; // 1 Information 24. uint _32b[sizeof(double) / sizeof(uint)]; // 2 Informations 25. ushort _16b[sizeof(double) / sizeof(ushort)]; // 4 Informations 26. uchar _8b [sizeof(double) / sizeof(uchar)]; // 8 Informations 27. }; 28. //+------------------------------------------------------------------+ 29. enum EnumEvents { 30. evTicTac, //Event of tic-tac 31. evHideMouse, //Hide mouse price line 32. evShowMouse, //Show mouse price line 33. evHideBarTime, //Hide bar time 34. evShowBarTime, //Show bar time 35. evHideDailyVar, //Hide daily variation 36. evShowDailyVar, //Show daily variation 37. evHidePriceVar, //Hide instantaneous variation 38. evShowPriceVar, //Show instantaneous variation 39. evCtrlReplayInit, //Initialize replay control 40. evChartTradeBuy, //Market buy event 41. evChartTradeSell, //Market sales event 42. evChartTradeCloseAll, //Event to close positions 43. evChartTrade_At_EA, //Event to communication 44. evEA_At_ChartTrade, //Event to communication 45. evChatWriteSocket, //Event to Mini Chat 46. evChatReadSocket //Event To Mini Chat 47. }; 48. //+------------------------------------------------------------------+ 49. enum EnumPriority { //Priority list on objects 50. ePriorityNull = -1, 51. ePriorityDefault = 0, 52. ePriorityChartTrade = 5, 53. ePriorityOrders = 10 54. }; 55. //+------------------------------------------------------------------+
Código fuente del archivo Defines.mqh
Observa este cambio que hicimos aquí en el archivo Defines.mqh. Consiste en añadir una enumeración en la línea 49. ¿Pero por qué? El motivo es simple. Como comenté en el artículo anterior, los objetos gráficos pueden terminar quedando unos sobre otros. El peor de los casos es cuando dos objetos de tipo línea quedan superpuestos. En ese caso, no hay manera de hacer una selección simple y rápida de uno de los objetos. Sin embargo, si le indicas a MetaTrader 5 que incluso los objetos del mismo tipo tendrán distinta prioridad, MetaTrader 5 lo tendrá en cuenta cuando vayas a seleccionar objetos superpuestos y priorizará el que tenga el valor más alto. Aún así, ten cuidado al hacer esto. No quiero que entres en la costumbre de asignar valores cada vez mayores para dar prioridad a tus objetos en detrimento de otros.
Fíjate en que estoy creando 4 niveles. El primer nivel es el que recibirán todos los objetos creados por nuestras aplicaciones y que no necesitan tanta prioridad. Es decir, el valor -1. En la línea 51, en cambio, se indica el valor que MetaTrader 5 suele utilizar cuando el usuario añade un objeto al gráfico. Es decir, reciben el valor cero. Como algunos objetos pueden tener cierto nivel de transparencia y, durante un repintado del gráfico, quedar en la parte superior de la lista, pueden ocultar parcialmente Chart Trade. Sin embargo, ten cuidado, porque, aunque Chart Trade esté totalmente oculto, es decir, detrás de algún objeto, si haces clic en la región donde se encuentra, MetaTrader 5 le dará prioridad a él y no al objeto que esté en primer plano.
Esto se consigue porque le estamos definiendo una prioridad de cinco. Lo mismo ocurre en la línea 53. Observa que, entre la prioridad de Chart Trade y la de los objetos que crearemos en breve, hay un margen. Este margen nos permite añadir otros niveles de prioridad, si llega a ser necesario. Entonces, según el modelo que acabo de presentar, los objetos que formarán parte del sistema de visualización de órdenes y posiciones tendrán la prioridad más alta de todas. Puedes modificar esto, si lo deseas. Pero ten muy presente lo que acabo de comentar.
Muy bien, hecho esto, tendremos que hacer otros dos cambios en el código existente. Pero no hace falta que te preocupes ni que sientas recelo. Son cambios muy simples, solo para que esta enumeración se use. Así que vamos a modificar el código de la clase C_Terminal. El punto que debe modificarse puede verse en el siguiente fragmento
201. //+------------------------------------------------------------------+ 202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const EnumPriority zOrder = ePriorityNull) const 203. { 204. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false); 205. ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0); 206.
Fragmento de la clase C_Terminal
Debes sustituir el código original de la línea 202 por este mismo código, que también se muestra en la línea 202. Lo que estamos haciendo aquí es declarar que, a partir de ahora, usaremos la lista de prioridades que acabamos de definir. El otro fragmento que debe modificarse también puede verse en el siguiente fragmento. Solo que este se encuentra en la clase C_ChartFloatingRAD.
164. //+------------------------------------------------------------------+ 165. template <typename T > 166. void CreateObjectEditable(eObjectsIDE arg, T value) 167. { 168. DeleteObjectEdit(); 169. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, ePriorityChartTrade); 170. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 171. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
Fragmento de la clase C_ChartFloatingRAD
El cambio deberá hacerse en la línea 169. Deberás sustituir la línea original por esta línea que aparece en el fragmento anterior. Con esto, si en algún momento llegas a cambiar la lista de prioridades y vuelves a compilar las aplicaciones, el indicador Chart Trade pasará automáticamente a usar la nueva prioridad si se le ha modificado la prioridad. Así, ya no necesitamos cambiar absolutamente nada más en el código existente y podemos empezar a trabajar en el nuevo indicador.
Iniciando la implementación de los nuevos objetos
Antes de pasar al código más avanzado, vayamos un poco más despacio, para que puedas seguir la línea de razonamiento que voy a aplicar. De esta manera, comenzaremos con el código que aparece a continuación, que no es más que una mejora del código que vimos en el artículo anterior.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName) 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. } 30. //+------------------------------------------------------------------+ 31. int OnInit() 32. { 33. ZeroMemory(glVariables); 34. Terminal = new C_Terminal(); 35. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 36. glVariables.id = (*Terminal).GetInfoTerminal().ID; 37. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 38. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice); 39. 40. return INIT_SUCCEEDED; 41. } 42. //+------------------------------------------------------------------+ 43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 44. { 45. return rates_total; 46. } 47. //+------------------------------------------------------------------+ 48. void OnDeinit(const int reason) 49. { 50. delete Terminal; 51. if (glVariables.id > 0) 52. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 53. } 54. //+------------------------------------------------------------------+
Código fuente del Indicador de posición
Muy bien. Tú, al mirar este código anterior, ya debes de estar imaginando: ahora sí que esto empezó a complicarse. Pero no, todavía seguimos en lo básico. Este código anterior solo creará una única línea en el gráfico, siempre que exista una posición en el símbolo en el que se coloque el indicador. Este código sigue siendo muy simple, compacto y sin ninguna comprobación. Solo creará un objeto H_LINE y lo mostrará en el gráfico, según el color que el usuario podrá definir. Así de simple. Pero, si estás acostumbrado a colocar objetos en el gráfico usando MQL5, debes de estar extrañado, ya que no aparece ninguna de las llamadas que normalmente estás acostumbrado a usar. Así que surge la duda: ¿realmente funciona este código? Y, si funciona, ¿cómo sucede esto?
Empecemos por la línea ocho. Allí tenemos una definición, al igual que en las dos líneas siguientes, donde definimos un sufijo para los objetos de tipo línea que vamos a crear. El motivo por el que hago esto se entenderá mejor más adelante. Ya entre las líneas 14 y 16, permitimos al usuario modificar los colores que se usarán en las líneas. Hasta aquí, nada demasiado complicado. Sin embargo, ahora empieza la parte interesante. Esto ocurre en la línea 19, donde empezamos a definir una estructura que después desaparecerá. Pero, por ahora, vamos a hacer las cosas de esta manera, ya que tú, querido lector, necesitas entender mi línea de razonamiento. Así podrás comprender por qué lo estoy planteando así.
Por ahora, vamos a saltarnos el procedimiento de la línea 25 y vayamos a la línea 31. Allí el indicador empezará a ejecutarse en MetaTrader 5. Observa que lo primero que hago es poner a cero la memoria de la estructura glVariables. ¿Por qué hacer esto? El motivo, como ya dije, es que esta estructura desaparecerá más adelante. Pero, aunque se mantenga, quiero garantizar que todas las variables presentes en la estructura tengan cero como valor inicial. Hacer esto mediante la llamada mostrada es más simple que declarar variable por variable. Y, si llego a olvidar alguna, con toda seguridad quedará con el valor cero.
Magnífico. Ahora, las líneas 34 y 35 ya se explicaron en el artículo anterior. Así que podemos saltárnoslas. La línea 36, en cambio, solo sirve para conservar, de cara a un uso futuro, el valor almacenado en la clase C_Terminal. Con esto, llegamos a las líneas principales. En la primera, que es la línea 37, definimos el valor que será el prefijo del nombre del objeto que vamos a crear, en este caso, un objeto de tipo HLINE. El valor de este prefijo es precisamente el valor numérico que define el ticket de la posición. Esto garantiza que solo tendremos un único objeto con ese nombre en el gráfico.
Pero, como vamos a reutilizar este mismo valor más adelante, necesitamos hacer algunas cosas para que MetaTrader 5 no confunda los objetos que vamos a poner en el gráfico, creyendo que el objeto ya existe cuando, en realidad, estamos creando uno nuevo. Ahora empiezas a entender las definiciones de las líneas ocho a diez. Pero volvamos al código, porque ahora tenemos la línea 38, que es donde hacemos una llamada al procedimiento de la línea 25 con el siguiente objetivo: crear una línea en el gráfico y posicionarla en el precio de apertura de la posición.
Entonces, pasemos al procedimiento de la línea 25 para entender algunas cosas. Porque, en este momento, el código sigue siendo bastante simple y fácil de entender. Este procedimiento recibe el nombre que deberá tener el objeto de tipo HLINE. Pero ¿dónde estamos creando ese objeto? Lo estamos haciendo usando la clase C_Terminal, concretamente en la línea 27. Ahora, observa algo aquí. Y quiero que prestes mucha atención a esto. Fíjate en que, en el cuarto parámetro de la línea 27, estamos usando una prioridad muy baja, en realidad, la más baja que existe en la lista. ¿Por qué?
Puedes pensar lo siguiente: pero esta línea que estamos creando en este punto, ¿no es un objeto que forma parte del indicador de posición y órdenes? ¿Por qué está recibiendo una prioridad de eventos inferior a la de otros objetos? Esto no tiene sentido. La prioridad, en mi opinión, debería ser la que pusimos en la lista como la prioridad de los objetos que se crearán en el indicador de posiciones y órdenes. No, querido lector, pensar así, y sobre todo en este caso concreto, no es lo correcto. Y el motivo es simple. Esta línea horizontal, y que quede muy claro, solo esta línea, deberá tener la prioridad más baja posible. Y el motivo es simple: no se moverá ni deberá recibir eventos, ya que representa el precio, o punto, en el que el servidor de trading indica una posición abierta. ¿Entiendes ahora por qué recibe este valor de prioridad?
Pero todavía no debes confiarte. Pues lo único que hicimos fue crear la línea y posicionarla en el gráfico. Este posicionamiento se produce en la línea 28 del código anterior. Eso fue todo lo que se hizo. Si el usuario elimina, mueve o incluso selecciona esta línea en el gráfico, podrá moverla. Eso viola por completo el principio que queremos implementar con este indicador. Resolver esto implica implementar todavía más código. Pero, como el objetivo aquí es ser lo más didáctico posible, haremos esto poco a poco, para que puedas comprender por qué no puedes mover un objeto que, en este caso, es la línea del precio, mientras ques objetos similares.
De todos modos, cuando el indicador se elimine del gráfico, tendremos, en la línea 51, una comprobación. Si la comprobación tiene éxito, en la línea 52 eliminaremos todos los objetos que lleven el prefijo que usamos en este código para crearlos y visualizarlos en el gráfico. Espero que hayas comprendido esta parte, porque, para añadir las líneas horizontales que indican dónde se encuentran el stop loss y el take profit, se sigue el mismo principio. Pero hay unas pocas diferencias, como puedes ver en el siguiente fragmentofragmento.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName) 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. } 30. //+------------------------------------------------------------------+ 31. void CreateLineStopAndTake(const string szObjName, const double price, const color cor) 32. { 33. if (price <= 0) return; 34. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders); 35. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 36. } 37. //+------------------------------------------------------------------+ 38. int OnInit() 39. { 40. ZeroMemory(glVariables); 41. Terminal = new C_Terminal(); 42. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 43. glVariables.id = (*Terminal).GetInfoTerminal().ID; 44. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 45. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice); 46. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01); 47. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02); 48. 49. return INIT_SUCCEEDED; 50. } 51. //+------------------------------------------------------------------+ 52. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 53. { 54. return rates_total; 55. } 56. //+------------------------------------------------------------------+ 57. void OnDeinit(const int reason) 58. { 59. delete Terminal; 60. if (glVariables.id > 0) 61. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 62. } 63. //+------------------------------------------------------------------+
Código fuente del Indicador de posición
Ten en cuenta que se añadió muy poco código. Sin embargo, ahora, si usas esta aplicación en un gráfico donde el símbolo solo tiene una posición abierta, podrás ver dónde están los precios de apertura, take profit y stop loss. Entonces, veamos qué se agregó al código para que esta visualización sea posible.
Ahora, en la línea 31, tenemos un nuevo procedimiento. Este es el que creará las líneas horizontales de take profit y stop loss. Observa que es prácticamente igual al procedimiento para crear la línea del precio de apertura. Pero, ¿por qué los dejé separados? El motivo es que entiendas que la línea del precio de apertura deberá tener una prioridad distinta de las demás líneas, que, en este caso, son las de take profit y stop loss. Otro detalle es que, en la línea 33, comprobamos si el valor del precio es cero o negativo, lo que no tiene sentido. Si ese es el caso, el objeto de línea horizontal no se creará.
Pero, incluso al añadir la posibilidad de que el indicador coloque dos líneas más en el gráfico, en cuanto se elimine, también se eliminarán todas las líneas que haya creado. Así, solo fue necesario añadir las líneas 46 y 47 al código para que todo quedara concluido, al menos en la forma más básica de todo esto. Como resultado, podrás ver la siguiente imagen cuando el indicador esté en el gráfico y haya una posición abierta.

Pero, antes de pasar a algo todavía más complejo, vamos a añadir algún texto para que podamos saber de qué se trata esa línea presente en el gráfico, ya que, sin eso, podríamos sentirnos tentados a eliminarlas manualmente a través de la ventana de objetos de MetaTrader 5. Dicha ventana puede verse en la siguiente imagen.

Aquí podemos ver todos los objetos presentes en el gráfico, estén ocultos o no. Pero, si el objeto está de alguna forma en el gráfico, aparecerá en esta lista. Y no queremos que el usuario elimine uno de estos objetos solo porque no sabe cuál es su propósito. Así que haremos un pequeño cambio en el código. Este cambio puede hacerse en dos lugares: aquí, en el indicador, o en la clase C_Terminal. Sin embargo, y sinceramente, no quiero seguir modificando una clase que ya es totalmente estable, añadiéndole posibles inestabilidades sin un buen motivo. Entonces, como necesitaremos cambiar el estado de algunas de las propiedades de los objetos que acabamos de crear, vamos a hacerlo aquí, localmente.
Mejorando aún más las cosas
Una de las cosas que podemos añadir, para que el usuario sepa qué representa ese objeto específico y por qué está ahí, es una descripción para el objeto. Hacer esto es muy simple. Basta con que modifiques el código anterior por el que se muestra a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrRoyalBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName, const string szDescription = "\n") 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 30. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 31. } 32. //+------------------------------------------------------------------+ 33. void CreateLineStopAndTake(const string szObjName, const double price, const color cor, const string szDescription = "\n") 34. { 35. if (price <= 0) return; 36. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders); 37. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 38. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 39. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 40. } 41. //+------------------------------------------------------------------+ 42. int OnInit() 43. { 44. ZeroMemory(glVariables); 45. Terminal = new C_Terminal(); 46. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 47. glVariables.id = (*Terminal).GetInfoTerminal().ID; 48. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 49. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price."); 50. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 51. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 52. 53. return INIT_SUCCEEDED; 54. } 55. //+------------------------------------------------------------------+ 56. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 57. { 58. return rates_total; 59. } 60. //+------------------------------------------------------------------+ 61. void OnDeinit(const int reason) 62. { 63. delete Terminal; 64. if (glVariables.id > 0) 65. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 66. } 67. //+------------------------------------------------------------------+
Código fuente del Indicador de posición
Observa de nuevo que fue necesario añadir muy poco código para que el resultado obtenido pudiera verse en la siguiente imagen.

Y también en la lista de objetos, donde ahora aparece lo siguiente.

Recuerda lo siguiente: para que el texto aparezca en el gráfico, como se ve en la imagen superior, es necesario que, en la configuración del gráfico, la siguiente propiedad esté marcada, como se muestra a continuación.

Incluso es posible marcar esta propiedad mediante código MQL5. Para ello, bastaría con que añadieras una línea de código al programa, como se muestra en el siguiente fragmento.
41. //+------------------------------------------------------------------+ 42. int OnInit() 43. { 44. ZeroMemory(glVariables); 45. Terminal = new C_Terminal(); 46. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 47. glVariables.id = (*Terminal).GetInfoTerminal().ID; 48. ChartSetInteger(glVariables.id, CHART_SHOW_OBJECT_DESCR, true); 49. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 50. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price."); 51. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 52. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 53. 54. return INIT_SUCCEEDED; 55. } 56. //+------------------------------------------------------------------+
Fragmento del código principal
Observa que la línea añadida es precisamente la línea 48. Esto activaría la misma opción que se ve en la imagen superior. Pero, como esto añade un elemento más que el código debe analizar, a fin de evitar que el usuario cambie configuraciones que tú, como programador, tal vez no quieras que se modifiquen, no voy a usarlo en el código final. Pero, si deseas hacerlo, puedes hacerlo sin problema. Del mismo modo, el color con el que se muestra el texto también puede modificarse. Esto se hace en el siguiente punto, marcado en la imagen inferior:

También podemos cambiar esta propiedad mediante código MQL5. Pero, nuevamente, no quiero preocuparme ni cambiar configuraciones que el usuario ya debe de estar acostumbrado a usar en el día a día. Quiero hacer el mínimo esfuerzo posible para cumplir el objetivo propuesto. Pero, volviendo al código principal, quiero llamar tu atención, querido lector, sobre algo. Observa que, tanto en la declaración del procedimiento CreateLinePriceOpen como en la declaración de CreateLineStopAndTake, el valor szDescription tiene asignado un valor. Si no especificas una descripción para el objeto, este recibirá una descripción cuyo valor será "\n". Esto aparece en la documentación de MQL5, que dice lo siguiente:
El texto de un "tooltip" (ayuda emergente). Si la propiedad no está definida, entonces se mostrará el "tooltip" generado automáticamente por el terminal. Un "tooltip" puede deshabilitarse asignándole el valor "\n" (salto de línea).
Así, este valor por defecto que recibe szDescription, en realidad, no influirá en la información que se colocará en la propiedad OBJPROP_TEXT, en las líneas 29 y 38. Pero impedirá que el terminal de MetaTrader 5 genere un valor para la propiedad OBJPROP_TOOLTIP, que se declara en las líneas 30 y 39.
¿Cómo tendremos acceso a esta propiedad OBJPROP_TOOLTIP? ¿No es lo mismo que OBJPROP_TEXT? No, no son lo mismo. La propiedad OBJPROP_TEXT es precisamente la que ves cuando algún objeto te permite añadirle un texto. Un objeto que permite esto es la línea horizontal. Pero esta propiedad no está presente en todos los objetos.
En cambio, la propiedad OBJPROP_TOOLTIP sí estará presente en todos los objetos del gráfico. Pero el texto de ambas propiedades no tiene por qué ser necesariamente el mismo. Pueden ser diferentes, ya que la propiedad OBJPROP_TOOLTIP solo muestra su texto cuando colocas el cursor sobre un objeto durante un tiempo. En ese momento, aparecerá una pequeña ventana con el contenido presente en la propiedad OBJPROP_TOOLTIP.
Si nunca lo has notado, en la animación que aparece a continuación puedes ver de qué se trata esa pequeña ventana, así como la forma de visualizar lo que contiene la propiedad OBJPROP_TOOLTIP.

Observa que el contenido, la forma de interacción y también el objetivo de esta propiedad funcionan como una ayuda emergente. Algo simple, directo y práctico para muchas situaciones en general. Todos los objetos pueden recibir alguna información en esta propiedad. Siempre que quieras ayudar a un usuario a entender alguna de tus aplicaciones, en las que hay muchos botones, y muchas veces esos botones son iconos o imágenes, usa esta propiedad para darle una ayuda al usuario. Te lo agradecerá mucho, ya que esto mejora su experiencia al usar tu aplicación. Queda el consejo.
Muy bien, estamos llegando al final de este artículo. Pero, antes de terminar, ¿qué te parece reunir la creación de las líneas en una única rutina? Así tendrás menos trabajo en el futuro, ya que usaremos otros medios para hacer que estas líneas se muevan. Entonces, el código final, que se usará en el próximo artículo, se muestra a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrRoyalBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLineInfos(const string szObjName, const double price, const color cor, const string szDescription = "\n") 26. { 27. if (price <= 0) return; 28. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == user00 ? ePriorityNull : ePriorityOrders)) 29. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 30. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 31. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 32. ObjectSetInteger(glVariables.id, szObjName, OBJPROP_SELECTABLE, cor != user00); 33. } 34. //+------------------------------------------------------------------+ 35. int OnInit() 36. { 37. ZeroMemory(glVariables); 38. Terminal = new C_Terminal(); 39. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 40. glVariables.id = (*Terminal).GetInfoTerminal().ID; 41. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 42. CreateLineInfos(glVariables.szPrefixName + def_SufixLinePrice, PositionGetDouble(POSITION_PRICE_OPEN), user00, "Position opening price."); 43. CreateLineInfos(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 44. CreateLineInfos(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 45. 46. return INIT_SUCCEEDED; 47. } 48. //+------------------------------------------------------------------+ 49. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 50. { 51. return rates_total; 52. } 53. //+------------------------------------------------------------------+ 54. void OnDeinit(const int reason) 55. { 56. delete Terminal; 57. if (glVariables.id > 0) 58. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 59. } 60. //+------------------------------------------------------------------+
Código fuente del Indicador de posición
Este será el código que modificaremos en el próximo artículo. Como prácticamente no cambió, creo que, con solo leer este artículo y pensar un poco, podrás entender fácilmente cómo funciona. El único detalle, y aquí debo aconsejarte que no lo hagas, es usar el mismo color para las tres líneas. Incluso puedes usar el mismo color para las líneas de stop loss y take profit. Pero no uses el mismo color para la línea del precio de apertura. Esto se debe a que, en la línea 28, así como en la línea 32, usamos el color como una forma de distinguir las cosas. Pero esto no es tan grave. Solo puede confundirte y hacer que no entiendas por qué las cosas no están funcionando como esperas.
Consideraciones finales
En este artículo mostraré, de la forma más simple y práctica posible, cómo podrás usar un indicador para observar posiciones abiertas en el servidor de trading. Estoy haciendo esto así, y poco a poco, precisamente para mostrarte que no necesitas incorporar necesariamente todo esto en un Asesor Experto. Muchos de ustedes ya deben de estar bastante acostumbrados a hacer esto, por un motivo u otro. Pero la verdad es que eso es una tontería, ya que, a medida que avancemos en esta implementación, quedará claro que podrás crear o implementar diversos tipos de indicadores para observar e interactuar con posiciones u órdenes registradas en el servidor o, en nuestro caso, en el sistema de repetición/simulador.
Sin embargo, aunque la propuesta principal aquí sea usar el indicador en el sistema de repetición/simulador, quiero que veas que todavía no lo he aplicado a un símbolo que se esté simulando ni a una situación en la que estemos ejecutando un replay. Estoy usando y probando el indicador en símbolos reales, con posiciones reales. Así que no tiene sentido usarlo solo en el sistema de repetición/simulador. Solo quiero que tengas el siguiente cuidado al usarlo: solo mostrará una posición. No basta con que tengas dos o más posiciones abiertas, en el caso de cuentas de tipo HEDGING, y esperes que el indicador funcione, porque todavía no es capaz de hacerlo.
Pronto veremos cuál es la forma correcta de usar este indicador para que podamos visualizar más de una posición abierta. En el caso de las cuentas de tipo NETTING, no tendrás esa preocupación, ya que no te permiten estar comprado y vendido al mismo tiempo sobre el mismo símbolo. Pero las cuentas de tipo HEDGING sí lo permiten. Y es justamente para ese caso específico para el que necesitaremos hacer un pequeño ajuste en la forma de usar este indicador. Pero el costo de esto es tan bajo que, sinceramente, vale la pena que pienses en sacar esa carga del Asesor Experto. Esto se debe a que todavía no he mostrado todo lo que este indicador puede ofrecernos. Entonces, en el próximo artículo, mejoraremos un poco más este indicador, ya que aquí hay una pequeña falla. Y, si quieres saber qué falla es esa, mira el próximo artículo de esta misma secuencia.
| Archivo | Descripción |
|---|---|
| Experts\Expert Advisor.mq5 | Demuestra la interacción entre Chart Trade y el Asesor Experto (es necesario el Mouse Study para la interacción) |
| Indicators\Chart Trade.mq5 | Crea la ventana para configurar la orden a ser enviada (es necesario el Mouse Study para la interacción) |
| Indicators\Market Replay.mq5 | Crea los controles para la interacción con el servicio de repetición/simulador (es necesario el Mouse Study para la interacción) |
| Indicators\Mouse Study.mq5 | Permite la interacción entre los controles gráficos y el usuario (necesario tanto para operar el sistema de repetición/simulador como en el mercado real) |
| Indicators\Order Indicator.mq5 | Responsable de la indicación de órdenes de mercado, permitiendo interactuar con ellas y controlarlas |
| Indicators\Position View.mq5 | Responsable de la indicación de posiciones de mercado, permitiendo interactuar con ellas y controlarlas |
| Services\Market Replay.mq5 | Crea y mantiene el servicio de repetición y simulación de mercado (archivo principal de todo el sistema) |
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/13137
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Del básico al intermedio: SandBox y MetaTrader
Simulación de mercado: Position View (I)
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 22): Panel de correlación
Del básico al intermedio: Eventos en Objetos (IV)
- 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