Português
preview
Desarrollo de un sistema de repetición (Parte 47): Proyecto Chart Trade (VI)

Desarrollo de un sistema de repetición (Parte 47): Proyecto Chart Trade (VI)

MetaTrader 5Ejemplos | 18 julio 2024, 12:40
22 0
Daniel Jose
Daniel Jose

Introducción

En el artículo anterior Desarrollo de un sistema de repetición (Parte 46): Proyecto Chart Trade (V), mostré cómo puedes agregar datos al ejecutable para que no sea necesario transportarlos de forma independiente. Ese conocimiento es muy importante para algo que se hará pronto. Pero, por ahora, seguiremos desarrollando las cosas que deben implementarse antes.

En este artículo finalizaremos el indicador Chart Trade, haciéndolo funcional hasta el punto de poder usarlo junto con algún EA. Lo haremos de una forma que nos permitirá acceder y trabajar con el indicador como si realmente estuviera vinculado al EA. Pero lo haremos de una manera mucho más interesante que en el artículo anterior: Desarrollo de un EA de trading desde cero (Parte 30): ¿CHART TRADE ahora como indicador? En ese artículo, usamos el Chart Trade como indicador indirecto. Aquí será un indicador puro. 

Para ello, haremos que funcione de una forma muy específica, de la misma manera que se trabajaría con cualquier otro tipo de indicador. Es decir, crearemos el buffer de datos correspondiente. Este proceso ya ha sido explicado en otros artículos, puedes echar un vistazo en los siguientes:

Estos 3 artículos contienen la base de lo que haremos de hecho. Si no los leíste, te aconsejo hacerlo. De lo contrario, quedarás a la deriva en este artículo, ya que la comprensión no será adecuada debido a la falta de un conocimiento más profundo de las bases involucradas aquí. Después, lee los artículos mencionados y comprende bien su contenido.

Antes de comenzar a realizar los cambios, es necesario hacer algunas pequeñas modificaciones. Esto se ha incluido en el código ya existente de la clase C_ChartFloatingRAD con el fin de proporcionarnos un acceso fácil y adecuado a los datos que necesitaremos.  Entonces, comencemos la última etapa del indicador Chart Trade, pero esto se verá en el próximo tema.


Pequeños cambios, grandes resultados

Los cambios que se van a hacer no son muchos ni complicados. Esto es para quienes están siguiendo esta serie de artículos. Como se está haciendo en esta segunda etapa de artículos sobre el sistema de repetición/simulación. Justo abajo, tienes acceso al código completo del indicador Chart Trade. Es importante ver primero este código, para simplificar la explicación del código de la clase.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade base indicator."
04. #property description "This version communicates via buffer with the EA."
05. #property description "See the articles for more details."
06. #property version   "1.47"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/es/articles/11760"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. input int      user01 = 1;       //Leverage
18. input double   user02 = 100.1;   //Finance Take
19. input double   user03 = 75.4;    //Finance Stop
20. //+------------------------------------------------------------------+
21. double m_Buff[];
22. //+------------------------------------------------------------------+
23. int OnInit()
24. {
25.     bool bErr;
26.             
27.     chart = new C_ChartFloatingRAD("Indicator Chart Trade", new C_Mouse("Indicator Mouse Study"), user01, user02, user03);
28.     
29.     if (bErr = (_LastError != ERR_SUCCESS)) Print(__FILE__, " - [Error]: ", _LastError);
30. 
31.     SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
32.     ArrayInitialize(m_Buff, EMPTY_VALUE);
33.     
34.     return (bErr ? INIT_FAILED : INIT_SUCCEEDED);
35. }
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.     (*chart).MountBuffer(m_Buff, rates_total);
40.     
41.     return rates_total;
42. }
43. //+------------------------------------------------------------------+
44. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
45. {
46.     (*chart).DispatchMessage(id, lparam, dparam, sparam);
47.     (*chart).MountBuffer(m_Buff);
48.     
49.     ChartRedraw();
50. }
51. //+------------------------------------------------------------------+
52. void OnDeinit(const int reason)
53. {
54.     if (reason == REASON_CHARTCHANGE) (*chart).SaveState();
55. 
56.     delete chart;
57. }
58. //+------------------------------------------------------------------+

Código fuente del Indicador Chart Trade

Observen que todo este código de arriba contiene todo lo necesario para hacer que el indicador Chart Trade funcione. Pero, por motivos de practicidad, gran parte del código se ha transferido a la clase, que se verá más adelante. No obstante, puede que estés pensando: ¿cómo funciona este código? ¿Cómo nos permite enviar órdenes para que el EA ejecute las operaciones? Calma. Primero entendamos lo que está ocurriendo aquí, en el código del indicador.

En la línea 11 tenemos la primera de las etapas que necesitamos. En esta línea definimos e informamos que utilizaremos 1 buffer. Podríamos utilizar más buffers, pero usar 1 ya será suficiente.

El buffer que utilizaremos se declara en la línea 21. Pero solo definimos la forma de usarlo en la línea 31. Como no queremos que el buffer se llene de "basura", lo iniciamos en la línea 32 de forma que contendrá solo y únicamente valores cero.

Básicamente, como puedes ver, el código del indicador no sufrió grandes cambios en comparación con el visto en los artículos anteriores. Pero aparecieron dos nuevas líneas. Las líneas 39 y 47. Ambas líneas realizan una llamada a la misma rutina dentro de la clase, que veremos enseguida. Pero puedes estar imaginando que son funciones diferentes debido a que el número y los parámetros son distintos. Pero pronto te darás cuenta de que ambas son iguales. Para entenderlo, es necesario ver el código de la clase. A continuación, puedes ver el código completo de la clase.  

001.//+------------------------------------------------------------------+
002.#property copyright "Daniel Jose"
003.//+------------------------------------------------------------------+
004.#include "../Auxiliar/C_Mouse.mqh"
005.#include "../Auxiliar/Interprocess.mqh"
006.#include "C_AdjustTemplate.mqh"
007.//+------------------------------------------------------------------+
008.#define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A)
009.//+------------------------------------------------------------------+
010.class C_ChartFloatingRAD : private C_Terminal
011.{
012.    public          :
013.            enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
014.            struct stData
015.            {
016.                    int             Leverage;
017.                    double          PointsTake,
018.                                    PointsStop;
019.                    bool            IsDayTrade;
020.                    union u01
021.                    {
022.                            ulong   TickCount;
023.                            double  dValue;
024.                    }uCount;
025.                    eObjectsIDE Msg;
026.            };
027.    private :
028.            struct st00
029.            {
030.                    int     x, y, minx, miny;
031.                    string  szObj_Chart,
032.                            szObj_Editable,
033.                            szFileNameTemplate;
034.                    long    WinHandle;
035.                    double  FinanceTake,
036.                            FinanceStop;
037.                    bool    IsMaximized;
038.                    stData  ConfigChartTrade;
039.                    struct st01
040.                    {
041.                            int    x, y, w, h;
042.                            color  bgcolor;
043.                            int    FontSize;
044.                            string FontName;
045.                    }Regions[MSG_NULL];
046.            }m_Info;
047.//+------------------------------------------------------------------+
048.            C_Mouse   *m_Mouse;
049.            string    m_szShortName;
050.//+------------------------------------------------------------------+
051.            void CreateWindowRAD(int w, int h)
052.                    {
053.                            m_Info.szObj_Chart = "Chart Trade IDE";
054.                            m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit";
055.                            ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
056.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x);
057.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y);
058.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
059.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
060.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
061.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
062.                            m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID);
063.                    };
064.//+------------------------------------------------------------------+
065.            void AdjustEditabled(C_AdjustTemplate &Template, bool bArg)
066.                    {
067.                            for (eObjectsIDE c0 = 0; c0 <= MSG_STOP_VALUE; c0++)
068.                                    if (bArg)
069.                                    {
070.                                            Template.Add(EnumToString(c0), "bgcolor", NULL);
071.                                            Template.Add(EnumToString(c0), "fontsz", NULL);
072.                                            Template.Add(EnumToString(c0), "fontnm", NULL);
073.                                    }
074.                                    else
075.                                    {
076.                                            m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor"));
077.                                            m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz"));
078.                                            m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm");
079.                                    }
080.                    }
081.//+------------------------------------------------------------------+
082.inline void AdjustTemplate(const bool bFirst = false)
083.                    {
084.#define macro_AddAdjust(A) {                         \
085.             (*Template).Add(A, "size_x", NULL);     \
086.             (*Template).Add(A, "size_y", NULL);     \
087.             (*Template).Add(A, "pos_x", NULL);      \
088.             (*Template).Add(A, "pos_y", NULL);      \
089.                           }
090.#define macro_GetAdjust(A) {                                                                                                                                                        \
091.             m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x"));   \
092.             m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y"));   \
093.             m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x"));  \
094.             m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y"));  \
095.                           }
096.#define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.ConfigChartTrade.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
097.                            
098.                            C_AdjustTemplate  *Template;
099.                            
100.                            if (bFirst)
101.                            {
102.                                    Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true);
103.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0));
104.                                    AdjustEditabled(Template, true);
105.                            }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
106.                            m_Info.ConfigChartTrade.Leverage = (m_Info.ConfigChartTrade.Leverage <= 0 ? 1 : m_Info.ConfigChartTrade.Leverage);
107.                            m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.ConfigChartTrade.Leverage));
108.                            m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.ConfigChartTrade.Leverage));
109.                            m_Info.ConfigChartTrade.PointsTake = FinanceToPoints(m_Info.FinanceTake, m_Info.ConfigChartTrade.Leverage);
110.                            m_Info.ConfigChartTrade.PointsStop = FinanceToPoints(m_Info.FinanceStop, m_Info.ConfigChartTrade.Leverage);
111.                            (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
112.                            (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.ConfigChartTrade.Leverage));
113.                            (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
114.                            (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
115.                            (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.ConfigChartTrade.IsDayTrade ? "1" : "0"));
116.                            (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
117.                            (*Template).Execute();
118.                            if (bFirst)
119.                            {
120.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0);
121.                                    m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
122.                                    AdjustEditabled(Template, false);
123.                            };
124.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6));
125.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
126.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
127.
128.                            delete Template;
129.                            
130.                            ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
131.                            ChartRedraw(m_Info.WinHandle);
132.
133.#undef macro_PointsToFinance
134.#undef macro_GetAdjust
135.#undef macro_AddAdjust
136.                    }
137.//+------------------------------------------------------------------+
138.            eObjectsIDE CheckMousePosition(const int x, const int y)
139.                    {
140.                            int xi, yi, xf, yf;
141.                            
142.                            for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++)
143.                            {
144.                                    xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x;
145.                                    yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y;
146.                                    xf = xi + m_Info.Regions[c0].w;
147.                                    yf = yi + m_Info.Regions[c0].h;
148.                                    if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0;
149.                            }
150.                            return MSG_NULL;
151.                    }
152.//+------------------------------------------------------------------+
153.inline void DeleteObjectEdit(void)
154.                    {
155.                            ChartRedraw();
156.                            ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable);
157.                            m_Info.ConfigChartTrade.Msg = MSG_NULL;
158.                    }
159.//+------------------------------------------------------------------+
160.            template <typename T >
161.            void CreateObjectEditable(eObjectsIDE arg, T value)
162.                    {
163.                            long id = GetInfoTerminal().ID;
164.                            
165.                            DeleteObjectEdit();
166.                            CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0);
167.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3);
168.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
169.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w);
170.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h);
171.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor);
172.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER);
173.                            ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1);
174.                            ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName);
175.                            ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value));
176.                            ChartRedraw();
177.                            m_Info.ConfigChartTrade.Msg = MSG_NULL;
178.                    }
179.//+------------------------------------------------------------------+
180.            bool RestoreState(void)
181.                    {
182.                            uCast_Double info;
183.                            bool bRet;
184.                            
185.                            if (bRet = GlobalVariableGet(macro_NameGlobalVariable("P"), info.dValue))
186.                            {
187.                                    m_Info.x = info._int[0];
188.                                    m_Info.y = info._int[1];
189.                            }
190.                            if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("M"), info.dValue) : bRet))
191.                            {
192.                                    m_Info.minx = info._int[0];
193.                                    m_Info.miny = info._int[1];
194.                            }
195.                            if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("B"), info.dValue) : bRet))
196.                            {
197.                                    m_Info.ConfigChartTrade.IsDayTrade = info._char[0];
198.                                    m_Info.IsMaximized = info._char[1];
199.                                    m_Info.ConfigChartTrade.Msg = (eObjectsIDE)info._char[2];
200.                            }
201.                            if (bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("L"), info.dValue) : bRet))
202.                                    m_Info.ConfigChartTrade.Leverage = info._int[0];
203.                            bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("Y"), m_Info.ConfigChartTrade.uCount.dValue) : bRet);
204.                            bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("T"), m_Info.FinanceTake) : bRet);
205.                            bRet = (bRet ? GlobalVariableGet(macro_NameGlobalVariable("S"), m_Info.FinanceStop) : bRet);
206.                            
207.                            
208.                            GlobalVariablesDeleteAll(macro_NameGlobalVariable(""));
209.                            
210.                            return bRet;
211.                    }
212.//+------------------------------------------------------------------+
213.    public  :
214.//+------------------------------------------------------------------+
215.            C_ChartFloatingRAD(const string szShortName)
216.                    :m_Mouse(NULL),
217.                     m_szShortName(szShortName)
218.                    {
219.                    }
220.//+------------------------------------------------------------------+
221.            C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const int Leverage, const double FinanceTake, const double FinanceStop)
222.                    :C_Terminal(),
223.                     m_szShortName(NULL)
224.                    {
225.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
226.                            m_Mouse = MousePtr;
227.                            if (!RestoreState())
228.                            {
229.                                    m_Info.ConfigChartTrade.Leverage = Leverage;
230.                                    m_Info.FinanceTake = FinanceTake;
231.                                    m_Info.FinanceStop = FinanceStop;
232.                                    m_Info.ConfigChartTrade.IsDayTrade = true;
233.                                    m_Info.ConfigChartTrade.uCount.TickCount = 0;
234.                                    m_Info.ConfigChartTrade.Msg = MSG_NULL;
235.                                    m_Info.IsMaximized = true;
236.                                    m_Info.minx = m_Info.x = 115;
237.                                    m_Info.miny = m_Info.y = 64;
238.                            }
239.                            CreateWindowRAD(170, 210);
240.                            AdjustTemplate(true);
241.                    }
242.//+------------------------------------------------------------------+
243.            ~C_ChartFloatingRAD()
244.                    {
245.                            if (m_Mouse == NULL) return;
246.                            ChartRedraw();
247.                            ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart);
248.                            FileDelete(m_Info.szFileNameTemplate);
249.                                                            
250.                            delete m_Mouse;
251.                    }
252.//+------------------------------------------------------------------+
253.            void SaveState(void)
254.                    {
255.#define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B);
256.                            
257.                            uCast_Double info;
258.                            
259.                            if (m_Mouse == NULL) return;
260.                            info._int[0] = m_Info.x;
261.                            info._int[1] = m_Info.y;
262.                            macro_GlobalVariable(macro_NameGlobalVariable("P"), info.dValue);
263.                            info._int[0] = m_Info.minx;
264.                            info._int[1] = m_Info.miny;
265.                            macro_GlobalVariable(macro_NameGlobalVariable("M"), info.dValue);
266.                            info._char[0] = m_Info.ConfigChartTrade.IsDayTrade;
267.                            info._char[1] = m_Info.IsMaximized;
268.                            info._char[2] = (char)m_Info.ConfigChartTrade.Msg;
269.                            macro_GlobalVariable(macro_NameGlobalVariable("B"), info.dValue);
270.                            info._int[0] = m_Info.ConfigChartTrade.Leverage;
271.                            macro_GlobalVariable(macro_NameGlobalVariable("L"), info.dValue);
272.                            macro_GlobalVariable(macro_NameGlobalVariable("T"), m_Info.FinanceTake);
273.                            macro_GlobalVariable(macro_NameGlobalVariable("S"), m_Info.FinanceStop);
274.                            macro_GlobalVariable(macro_NameGlobalVariable("Y"), m_Info.ConfigChartTrade.uCount.dValue);
275.                            
276.#undef macro_GlobalVariable
277.                    }
278.//+------------------------------------------------------------------+
279.inline void MountBuffer(double &Buff[], const int iPos = -1)
280.                    {
281.                            static int posBuff = 0;
282.                            uCast_Double info;
283.
284.                            if ((m_szShortName != NULL) || (m_Info.ConfigChartTrade.Msg == MSG_NULL)) return;
285.                            posBuff = (iPos > 5 ? iPos - 5 : posBuff);
286.                            Buff[posBuff + 0] = m_Info.ConfigChartTrade.uCount.dValue;
287.                            info._char[0] = (char)m_Info.ConfigChartTrade.IsDayTrade;
288.                            info._char[1] = (char)m_Info.ConfigChartTrade.Msg;
289.                            Buff[posBuff + 1] = info.dValue;
290.                            info._int[0] = m_Info.ConfigChartTrade.Leverage;
291.                            Buff[posBuff + 2] = info.dValue;
292.                            Buff[posBuff + 3] = m_Info.ConfigChartTrade.PointsTake;
293.                            Buff[posBuff + 4] = m_Info.ConfigChartTrade.PointsStop;
294.                    }
295.//+------------------------------------------------------------------+
296.inline const stData GetDataBuffer(void)
297.                    {
298.                            double Buff[];
299.                            int handle;
300.                            uCast_Double info;
301.                            stData data;
302.                            
303.                            ZeroMemory(data);
304.                            if (m_szShortName == NULL) return data;
305.                            if ((handle = ChartIndicatorGet(ChartID(), 0, m_szShortName)) == INVALID_HANDLE) return data;
306.                            if (CopyBuffer(handle, 0, 0, 5, Buff) == 5)
307.                            {
308.                                    data.uCount.dValue = Buff[0];
309.                                    info.dValue = Buff[1];
310.                                    data.IsDayTrade = (bool)info._char[0];
311.                                    data.Msg = (C_ChartFloatingRAD::eObjectsIDE) info._char[1];
312.                                    info.dValue = Buff[2];
313.                                    data.Leverage = info._int[0];
314.                                    data.PointsTake = Buff[3];
315.                                    data.PointsStop = Buff[4];
316.                            }
317.                            if (handle != INVALID_HANDLE) IndicatorRelease(handle);
318.                            
319.                            return data;
320.                    };
321.//+------------------------------------------------------------------+
322.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
323.                    {
324.#define macro_AdjustMinX(A, B)  {                            \
325.             B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x;  \
326.             mx = x - m_Info.Regions[MSG_TITLE_IDE].w;       \
327.             A = (B ? (mx > 0 ? mx : 0) : A);                \
328.                                }
329.#define macro_AdjustMinY(A, B)  {                            \
330.             B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y;  \
331.             my = y - m_Info.Regions[MSG_TITLE_IDE].h;       \
332.             A = (B ? (my > 0 ? my : 0) : A);                \
333.                                }
334.                                                                  
335.                            static int sx = -1, sy = -1;
336.                            int x, y, mx, my;
337.                            static eObjectsIDE obj = MSG_NULL;
338.                            double dvalue;
339.                            bool b1, b2, b3, b4;
340.                            eObjectsIDE tmp;
341.    
342.                            if (m_szShortName == NULL) switch (id)
343.                            {
344.                                    case CHARTEVENT_CHART_CHANGE:
345.                                            x = (int)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS);
346.                                            y = (int)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS);
347.                                            macro_AdjustMinX(m_Info.x, b1);
348.                                            macro_AdjustMinY(m_Info.y, b2);
349.                                            macro_AdjustMinX(m_Info.minx, b3);
350.                                            macro_AdjustMinY(m_Info.miny, b4);
351.                                            if (b1 || b2 || b3 || b4) AdjustTemplate();
352.                                            break;
353.                                    case CHARTEVENT_MOUSE_MOVE:
354.                                            if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (tmp = CheckMousePosition(x = (int)lparam, y = (int)dparam))
355.                                            {
356.                                                    case MSG_TITLE_IDE:
357.                                                            if (sx < 0)
358.                                                            {
359.                                                                    DeleteObjectEdit();
360.                                                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
361.                                                                    sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
362.                                                                    sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
363.                                                            }
364.                                                            if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
365.                                                            if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
366.                                                            if (m_Info.IsMaximized)
367.                                                            {
368.                                                                    m_Info.x = (mx > 0 ? mx : m_Info.x);
369.                                                                    m_Info.y = (my > 0 ? my : m_Info.y);
370.                                                            }else
371.                                                            {
372.                                                                    m_Info.minx = (mx > 0 ? mx : m_Info.minx);
373.                                                                    m_Info.miny = (my > 0 ? my : m_Info.miny);
374.                                                            }
375.                                                            break;
376.                                                    case MSG_BUY_MARKET:
377.                                                    case MSG_SELL_MARKET:
378.                                                    case MSG_CLOSE_POSITION:
379.                                                            DeleteObjectEdit();
380.                                                            m_Info.ConfigChartTrade.Msg = tmp;
381.                                                            m_Info.ConfigChartTrade.uCount.TickCount = GetTickCount64();
382.                                                            break;
383.                                            }else if (sx > 0)
384.                                            {
385.                                                    ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                                                
386.                                                    sx = sy = -1;
387.                                            }
388.                                            break;
389.                                    case CHARTEVENT_OBJECT_ENDEDIT:
390.                                            switch (obj)
391.                                            {
392.                                                    case MSG_LEVERAGE_VALUE:
393.                                                    case MSG_TAKE_VALUE:
394.                                                    case MSG_STOP_VALUE:
395.                                                            dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT));
396.                                                            if (obj == MSG_TAKE_VALUE)
397.                                                                    m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
398.                                                            else if (obj == MSG_STOP_VALUE)
399.                                                                    m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
400.                                                            else
401.                                                                    m_Info.ConfigChartTrade.Leverage = (dvalue <= 0 ? m_Info.ConfigChartTrade.Leverage : (int)MathFloor(dvalue));
402.                                                            AdjustTemplate();
403.                                                            obj = MSG_NULL;
404.                                                            ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable);
405.                                                            break;
406.                                            }
407.                                            break;
408.                                    case CHARTEVENT_OBJECT_CLICK:
409.                                            if (sparam == m_Info.szObj_Chart) switch (obj = CheckMousePosition(x = (int)lparam, y = (int)dparam))
410.                                            {
411.                                                    case MSG_DAY_TRADE:
412.                                                            m_Info.ConfigChartTrade.IsDayTrade = (m_Info.ConfigChartTrade.IsDayTrade ? false : true);
413.                                                            DeleteObjectEdit();
414.                                                            break;
415.                                                    case MSG_MAX_MIN:
416.                                                            m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
417.                                                            DeleteObjectEdit();
418.                                                            break;
419.                                                    case MSG_LEVERAGE_VALUE:
420.                                                            CreateObjectEditable(obj, m_Info.ConfigChartTrade.Leverage);
421.                                                            break;
422.                                                    case MSG_TAKE_VALUE:
423.                                                            CreateObjectEditable(obj, m_Info.FinanceTake);
424.                                                            break;
425.                                                    case MSG_STOP_VALUE:
426.                                                            CreateObjectEditable(obj, m_Info.FinanceStop);
427.                                                            break;
428.                                            }
429.                                            if (obj != MSG_NULL) AdjustTemplate();
430.                                            break;
431.                            }
432.                    }
433.//+------------------------------------------------------------------+
434.};
435.//+------------------------------------------------------------------+
436.#undef macro_NameGlobalVariable
437.//+------------------------------------------------------------------+

Código fuente de la clase C_ChartFloatingRAD

Quizás pienses que estoy bromeando al poner el código fuente en el artículo. Pero esto está lejos de ser una broma. De hecho, quiero explicar con el máximo detalle lo que está ocurriendo. Esto es para que no uses realmente este código, sino para que puedas entender y crear algo MEJOR.

Entonces, entendamos lo que está ocurriendo aquí. Pues si no logras entender el funcionamiento de esta clase, con toda certeza no entenderás lo que se desarrollará después.

Justo al inicio tenemos ya algunos cambios. En la línea 12 hacemos una declaración de una clausura pública. De hecho, esta cláusula está ahí porque necesitamos que los datos puedan accederse fuera de la clase. Puedes pensar que podríamos colocar estos mismos datos en otro lugar. Así, mantendríamos solo un tipo de cláusula. Pero aquí estoy declarando solo una estructura y una enumeración. La enumeración ya es conocida desde hace mucho tiempo. Sin embargo, la estructura es nueva. Entre las líneas 14 y 26 encontramos la declaración de la estructura que se utiliza para organizar los datos necesarios.

Gran parte de esta estructura es bastante fácil de entender, casi no hace falta explicarla. Sin embargo, hay algunas cosas aquí que resultan bastante extrañas. ¿Por qué está presente esta unión entre las líneas 20 y 24? ¿Y por qué la línea 25? Muy bien, el hecho es que la unión nos permitirá transferir las cosas más fácilmente. Además, la línea 25 se usará en un momento muy concreto. Llegaremos allí. Ten calma.

Esta misma estructura se referencia en la línea 38. Pero recuerda lo siguiente: Los datos presentes en la clase no pueden ser accedidos directamente fuera de la clase. Por ello, necesitamos algún medio para acceder a ellos. En realidad, no los accederemos de forma directa. No en el indicador, como puedes ver en el código fuente del indicador. Después, tenemos la línea 49. Esta tiene una función interesante. Servirá como un tipo de selector. Pero esto se comprenderá mejor más adelante.

En la línea 106, montamos y adecuamos la primera de las variables que fueron declaradas en la estructura encontrada en la línea 38. Noten que básicamente es lo mismo que hacíamos antes. Pero ahora estamos trabajando con un modelado un poco diferente.

Entonces, en la línea 109, ajustamos una de las variables. Esta es la variable del número de puntos del valor de take. Atención a esto, estoy hablando en términos de valor financiero, estoy hablando en términos de PONTOS. No confundas las dos cosas. Otra cosa es que tampoco estoy mencionando el número de puntos basado en el precio actual negociado. Estoy hablando en términos de puntos generales. No importa el precio, lo que importa es cuántos puntos de desplazamiento tendremos. Es importante que notes también que este valor en puntos ya estará ajustado en términos financieros. Eso se debe a la línea 107, que hace el ajuste por nosotros.

En la línea 110, tenemos algo equivalente, solo que esta vez lo estamos haciendo para el valor de stop. De la misma manera que se hacía en la línea 107 para ajustar lo financiero, antes de asumir la puntuación en la línea 109. En la línea 108, ajustamos lo financiero del stop antes de asumir la puntuación del stop. Es muy importante que entiendas lo que está pasando en este punto. Si no logras comprenderlo, tendrás dificultades en entender el ajuste que se hará después, en el momento en que introducimos las órdenes en el activo.

No obstante, para facilitar un poco la vida, en el anexo tendrás acceso a los indicadores de Mouse y Chart Trade, así como también a un EA bastante simple, para que puedas entender lo que está pasando. Por motivos de seguridad, el EA presente en el anexo no pondrá órdenes. Solo imprimirá los valores que están siendo introducidos. Así quedará más claro para que puedas entender cómo funciona el sistema de hecho.

Ahora en las líneas 157 y 177, por motivos de practicidad, ajustamos el valor de otra variable. Hasta ahora no se ha hecho ningún tipo de acceso. Todo lo que estamos haciendo es ajustando y configurando los valores de las variables. Pero al observar la línea 203, vemos algo que parece extraño. ¿Por qué estamos almacenando este valor en una variable global del terminal? ¿Realmente necesitamos hacer esto? ¿O sería pura pérdida de tiempo? De hecho, necesitamos hacerlo. El motivo es que cuando se elimina y se vuelve a cargar en el gráfico el indicador Chart Trade, cualquier valor de la memoria se pierde. Y este valor que estamos almacenando es muy importante para nosotros.

Sin embargo, aquí solo estamos recuperando los valores que se almacenaron. Entonces, este mismo valor que estamos recuperando en la línea 203 se almacenó realmente en la línea 274. Debe de haber notado que he saltado algunas funciones en esta explicación. Pero ahora vamos a volver a ellas. Comenzaremos por el constructor. Sí, ahora no tenemos solo uno, sino dos constructores para la clase. El motivo de esto es el mismo que el del artículo sobre el indicador de mouse. Necesitamos un método para poder traducir los datos del buffer.

Bueno, en el fondo, bien en el fondo, no necesitaríamos esto. Podríamos hacer la traducción directamente en el EA, o en el indicador. Sin embargo, para facilitar el mantenimiento, prefiero ponerlo todo junto. Así, si más adelante es necesario hacer algún cambio, todo lo que necesitaré hacer es trabajar en la clase C_ChartFloatingRAD. De este modo, no tendré todo ese trabajo de ajuste de programa por programa, con el fin de estandarizar los módulos. Bueno, pero volviendo a los constructores, tenemos el antiguo constructor, que está montado a partir de la línea 221. Básicamente, recibió la línea 223, donde inicializamos el nombre del indicador. Detalle: Este nombre no se usa para grabar en el buffer, sino para leer el buffer. Además, tenemos las líneas entre 232 y 234, donde introducimos las nuevas variables. No todas, pues las demás se ajustan al procedimiento de ajuste de la plantilla.

El segundo constructor es bastante simple y comienza en la línea 215. Allí básicamente asignamos los valores predeterminados. Esto ocurre durante la fase de traducción. Igual que sucedió con el indicador de mouse, aquí también tenemos dos fases. Una en la que grabamos en el buffer y otra en la que leemos el buffer. Pero esto no es porque la programación se esté llevando mal. Si estás pensando esto, es porque aún no has entendido lo que estamos haciendo. De hecho, lo que hacemos al usar el indicador es grabar los datos en un buffer. Este se lee mediante CopyBuffer por algún otro programa, normalmente el EA. Por esto tenemos dos fases, y por el mismo motivo tenemos dos constructores.

A diferencia de los constructores, en el caso del destructor, solo podemos tener uno. Este comienza en la línea 243. Sin embargo, observen que la única adición que se hizo es justamente la línea 245. Por tanto, si estamos usando la clase en un indicador, cuando se llame al destructor, esta prueba de la línea 245 pasará, permitiendo que los objetos creados sean eliminados. Pero si la clase se está usando para leer el buffer del indicador, al hacer la prueba de la línea 245, esta fallará y evitará que se elimine cualquier objeto. Un mecanismo simple, pero funcional.

Muy bien, esta fue la parte fácil. Ahora llegamos a la parte crítica de nuestra explicación. Cómo se realiza la interacción y cómo se almacenan y leen los datos del buffer. Esta parte tal vez resulte un poco confusa para quienes están comenzando. Por eso es tan importante comprender los conceptos vistos en los artículos anteriores, de los cuales hablé al principio. Pero para facilitar un poco las cosas, vamos a echar un vistazo a la figura 01.

Figura 01

Figura 01 - Esquema de comunicación.

En la figura 01 podemos ver el sistema de comunicación para transferir información del indicador al Expert Advisor. Observen que el buffer no forma parte realmente del indicador. Aunque el búfer se declare en el indicador, no debes considerarlo como parte de la memoria del mismo. De hecho, es mantenido por MetaTrader 5 y, cuando eliminamos el indicador del gráfico, este último libera la memoria que se utilizaba para el buffer. Pero, y aquí está uno de los peligros. Los datos no se destruyen realmente. Solo se libera la memoria. Tal vez no comprendas el peligro real de esto. Pero intentaré explicarlo de forma sencilla.

Si se libera la memoria donde se encontraba el buffer en MetaTrader 5 debido a que el indicador se ha eliminado del gráfico, esto puede suceder después de que el buffer se haya escrito. Si el indicador se vuelve a colocar en el gráfico, es posible que el buffer aún contenga algún tipo de dato. Esto puede suceder con la última actualización. Por tanto, cuando el Expert Advisor lea estos datos mediante CopyBuffer, es posible que los lea de forma incorrecta.

El asunto es tan serio que debes tener el máximo cuidado al acceder al buffer. Para garantizar que exista alguna sincronización entre la escritura y la lectura, fue necesario modificar el procedimiento DispatchMessage, presente en la clase C_ChartFloatingRAD. Esta sincronización entre la escritura del buffer y su lectura es muy importante. Si no se hace de manera correcta, habrá un retraso entre lo que el usuario envía y lo que se hace en realidad. Es decir, el usuario puede enviar una orden de compra y que no se atienda, o enviar justo después una orden de venta y que la compra se ejecute. Sin embargo, al enviar justo después una orden de venta, la compra se ejecutará. Este tipo de fallo no es culpa de la plataforma MetaTrader 5, sino de un mal entendimiento sobre cómo desarrollar el código para garantizar la correcta sincronización entre los eventos.

De hecho, no existe ninguna conexión en términos de código entre el Expert Advisor y el indicador Chart Trade. Esta conexión se produce por el uso del buffer. Entonces el Expert Advisor no sabe lo que el Chart Trade está haciendo, así como el Chart Trade no sabe lo que el Expert Advisor está de hecho haciendo. No obstante, MetaTrader 5 sí lo sabe. Y, como el punto común entre el indicador y el Expert Advisor es precisamente MetaTrader 5, lo usamos de manera que nos permita hacer que las cosas sucedan. Más o menos como si se tratara de magia, el usuario tendrá la impresión de que el Expert Advisor y el indicador Chart Trade son un único programa.

Pero hay otra cosa igualmente importante. Debes saber que, si no tienes el indicador de mouse, que ya se mostró en artículos anteriores, el indicador Chart Trade NUNCA funcionará. Es necesario que tengas las 3 aplicaciones en el gráfico: el indicador de Mouse, el Indicador Chart Trade y el Expert Advisor, además de otras cosas que veremos en el futuro. Sin esto, todo el sistema no hará absolutamente nada.

¿Y quién garantiza esto? Quien de hecho lo garantiza es justamente el evento CHARTEVENT_MOUSE_MOVE, que se encuentra en la línea 353 de la clase C_ChartFloatingRAD. Si el indicador de mouse no está en el gráfico, podrás hacer clic, editar y modificar los valores del indicador Chart Trade. Esto es porque tales eventos no necesariamente están ligados al indicador de mouse. Sin embargo, no podrás enviar órdenes de compra, venta, cierre, ni mover el indicador Chart Trade, sin que el indicador de mouse esté en el gráfico.

Pero entonces espera un poco. ¿Necesito que el indicador de mouse esté en el gráfico para poder interactuar con Chart Trade? ¿Es esto? Sí. Es posible eliminar esta dependencia, pero esto te acarreará otros problemas más adelante. Si deseas usar los demás métodos que explicaré. Este tipo de cosas es un coste que tendrás que asumir si quieres trabajar de la misma manera que yo. Por eso es importante que comprendas muy bien cómo funciona el sistema, de lo contrario, tendrás un sistema en el que no podrás confiar de hecho.

Pero volvamos al código para comprender cómo conseguí sincronizar el sistema. Para comprenderlo completamente, es necesario que entiendas lo que hace el código fuente del indicador, pero si has leído los artículos que indiqué al inicio de este, no tendrás dificultades para comprender su funcionamiento. Así, daré por sentado que ya comprendes cómo funciona el código del indicador. De este modo, podré centrarme en el código de la clase.

Cada vez que haces clic o mueves el mouse, MetaTrader 5 genera algún evento. Los eventos de clic en general son tratados por CHARTEVENT_OBJECT_CLICK. Sin embargo, usar este controlador no nos permite mantener la sincronización. El motivo es algo complicado de explicar, pero tiene que ver con el orden de ejecución de las cosas. El motivo es algo complicado de explicar, pero tiene que ver con el orden de ejecución de las cosas. Por tanto, para que, al hacer clic en uno de los tres botones del Chart Trade (botón de compra, botón de venta y botón de cerrar posición), se genere un evento en el Expert Advisor en el mismo instante, hacemos las cosas de una forma un poco diferente.

Por eso, si comparas este código presente en el procedimiento DispatchMessage con el mismo código del artículo anterior, verás que son ligeramente diferentes. La diferencia está en el modo en que tratamos el clic en los botones mencionados. En la versión anterior, este clic era tratado en la llamada CHARTEVENT_OBJECT_CLICK, ahora lo trataremos en CHARTEVENT_MOUSE_MOVE. Y como el propio código nos informa del objeto que se ha hecho clic, hemos creado una variable nueva para trabajar de manera un poco más organizada. Esta variable se declara en la línea 340. Y su valor se ajusta en la línea 354.

Ahora presta mucha atención a lo que explicaré. Entre las líneas 376 y 378, colocamos los códigos de los botones. Así, cuando el indicador de mouse envíe los datos a nosotros, podremos enviar la orden para que el Expert Advisor la ejecute. Pero aquí hay un pequeño detalle. ¿Cómo hacer que el Expert Advisor sepa cuál fue el botón presionado? Sencillo. Enviamos el código del botón al Expert Advisor. Esto se hace en la línea 380. Ahora, en la línea 381, capturamos el número de ticks para generar un número exclusivo. Esto será necesario para el Expert Advisor. Pero no te preocupes, pronto entenderás cómo funciona esto de hecho.

Entonces, a cada evento que se genere, además de esta llamada a la función DispatchMessage, que preparará las cosas para enviarlas al buffer, tendremos de hecho otra llamada. Esta es la que realmente colocará los datos en el buffer. Puedes ver su código a partir de la línea 279 en la clase. Ahora, fíjate en la línea 281, donde encontramos una variable estática. Esta almacenará el valor que es transferido por el evento OnCalculate, es decir, tenemos que almacenar el valor rates_total, el motivo de esto ya fue explicado antes. Lee los artículos indicados al inicio para entender el motivo. Así, cuando la llamada efectuada por el controlador OnChartEvent ocurra, sabremos dónde colocar los datos en el buffer.

Atención al hecho de que en la línea 284, realizamos una prueba para garantizar que solo el indicador Chart Trade escribirá en la memoria. Además, solo se escribirá en la memoria cuando se pulse uno de los botones deseados. Todo muy lindo y muy bonito. Pero nada de esto tendrá realmente algún valor si el Expert Advisor no interpreta los datos que le envía el indicador Chart Trade. Para entender cómo el Expert Advisor conseguirá interpretar las cosas, necesitamos ver un código funcional, pero que sea lo más básico posible.


Uso de un Expert Advisor de prueba

Como todo necesita ser probado, debemos asegurarnos de que dichas pruebas se realicen de manera que quede claro lo que de hecho está ocurriendo. En este caso, para probar la interacción entre el Indicador Chart Trade, el Indicador de Mouse y el Expert Advisor. Necesitamos hacer uso de un sistema que sea lo más sencillo posible. Pero al mismo tiempo, necesitamos que este mismo sistema, además de simple, se base en algo que de hecho usaremos en el futuro.

Con base en este criterio, vamos a hacer uso del código que puedes ver a continuación:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Demo version between interaction of Chart Trade and EA"
04. #property version   "1.47"
05. #property link "https://www.mql5.com/es/articles/11760"
06. //+------------------------------------------------------------------+
07. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
08. //+------------------------------------------------------------------+
09. C_ChartFloatingRAD *chart = NULL;
10. //+------------------------------------------------------------------+
11. int OnInit()
12. {
13.     chart = new C_ChartFloatingRAD("Indicator Chart Trade");
14.     
15.     return (CheckPointer(chart) != POINTER_INVALID ? INIT_SUCCEEDED : INIT_FAILED);
16. }
17. //+------------------------------------------------------------------+
18. void OnDeinit(const int reason)
19. {
20.     delete chart;
21. }
22. //+------------------------------------------------------------------+
23. void OnTick() {}
24. //+------------------------------------------------------------------+
25. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
26. {
27.     static ulong st_uTime = 0;
28.     C_ChartFloatingRAD::stData info;
29.     
30.     switch (id)
31.     {
32.             case CHARTEVENT_OBJECT_CLICK:
33.                     info = (*chart).GetDataBuffer();
34.                     if (st_uTime != info.uCount.TickCount)
35.                     {
36.                             st_uTime = info.uCount.TickCount;
37.                             PrintFormat("%u -- %s [%s] %d : %f <> %f", info.uCount.TickCount, info.IsDayTrade ? "DT" : "SW", EnumToString(info.Msg), info.Leverage, info.PointsTake, info.PointsStop);
38.                     }else Print("IGNORADO...");
39.                     break;
40.     }
41. }
42. //+------------------------------------------------------------------+

Código fuente del Expert Advisor de prueba

Cabe destacar que este código es muy sencillo y compacto, y casi en su totalidad autoexplicativo. Sin embargo, puede que tú, sobre todo los principiantes, no consigas comprender realmente su funcionamiento. No te sientas mal por esto. Todo principiante tiene el mismo tipo de dificultad. Pero si te esfuerzas, estudias, te dedicas, tienes disciplina y siempre tratas de mejorar, podrás comprender su funcionamiento. En el futuro, terminarás convirtiéndote en un gran profesional. Pero rendirse no es una opción para quien desea profesionalizarse.

De acuerdo, pero revisemos rápidamente el código, ya que son pocas líneas y resultará sencillo de entender.

Todo aquel código, visto en el tema anterior, presente en la clase C_ChartFloatingRAD, se resume cuando se usa aquí, en el Expert Advisor. Aunque en la línea 7 estamos incluyendo la clase por completo, para el compilador no es así exactamente. Entonces, en la línea 9 declaramos de manera global un puntero. Este será usado para que podamos acceder a la clase C_ChartFloatingRAD. Esto quizá te confunda un poco. Pero tal confusión se debe a que imaginas que la clase se accederá de la misma manera que se accede al código del indicador.

De hecho, podrías incluso hacerlo así. Pero no es nada adecuado hacerlo. El motivo es que la clase no se diseñó para usarse sin un indicador. Lo mismo ocurre con la clase C_Mouse, que se utiliza en el indicador de mouse, y no debe emplearse en un código que no sea un indicador. Sé que muchos pueden sentirse tentados a hacerlo. Pero NO debe hacerse. Pues toda la seguridad, el modelado y el desempeño esperados no están diseñados para usar las clases en un código que no sea el original. Es decir, si colocas el mismo modo de codificación del indicador en el Expert Advisor, conseguirás que no sea necesario usar el indicador Chart Trade. Esto es un hecho.

Sin embargo, si haces esto, transferir el código del indicador al Expert Advisor. Podrás tener problemas con la estabilidad y seguridad de todo el sistema. Puesto que no se está creando de forma que ofrezca tal sostenibilidad. Entonces, cada cosa en su lugar. Así, cuando estemos creando el puntero en la línea 13, haciendo la llamada al constructor, fíjate en que estaré informando del mismo nombre que se ha informado como siendo el del indicador Chart Trade. De esta forma, el indicador hará su trabajo y el Expert Advisor, el suyo. Cada uno en su lugar. Si algo sale mal en alguno de ellos, todo lo que necesitarás hacer será volver a representar el gráfico.

Ahora, observa que prácticamente todo el código se resume solo a esto. Informar el nombre del indicador, y luego en el controlador de eventos OnChartEvent, capturar el evento CHARTEVENT_OBJECT_CLICK, y entonces analizar lo que ocurrió. Aquí hay una observación importante. Siempre que hagas clic, se generará un evento de clic en objeto, aunque imagines que no hiciste clic en nada importante. El motivo de esto es que el indicador de mouse deberá estar siempre en el gráfico. Este indicador cuenta con una línea horizontal, que es un objeto. Entonces, siempre que hagas clic, esta línea horizontal generará un evento.

Pero entonces, ¿cómo el sistema puede diferenciar entre un clic en esta línea y un clic en otro objeto? Esta es una cuestión bastante interesante que se tratará en otro artículo pronto. Pero debes ser consciente de ello cuando uses diversos objetos en el gráfico. Principalmente porque añadiremos otros indicadores en breve. Pero eso queda para el futuro.

La cuestión es que, en la línea 33, el evento de clic en objetos intentará leer el contenido del buffer del indicador Chart Trade. Si lo consigue, recibiremos los datos devueltos. Y entonces, en la línea 34, verificaremos si se trata de un evento diferente, proveniente de Chart Trade o de otra cosa que pueda ignorarse.

Si, de hecho, el evento se generó en el Chart Trade y el motivo fue que hicimos clic en uno de los botones de interacción con el sistema de órdenes, actualizaremos el valor de la variable estática, como se hace en la línea 36. Después, imprimiremos un mensaje en el terminal para poder analizar lo ocurrido. En caso de que el evento deba ser ignorado por cualquier motivo, ejecutaremos la línea 38.

 

Conclusión

En el video 01, puedes ver cómo el sistema funciona en la práctica. Independientemente de esto, nada sustituye al hecho de poder ver el sistema en funcionamiento. Entonces, en el anexo tendrás acceso al sistema en su estado actual.


Video 01 - Demostración



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

Archivos adjuntos |
Anexo_47.zip (175.7 KB)
Desarrollo y prueba de los sistemas comerciales Aroon Desarrollo y prueba de los sistemas comerciales Aroon
En este artículo, aprenderemos a construir un sistema comercial Aroon, aprendiendo asimilando los fundamentos de los indicadores y los pasos necesarios para crear un sistema comercial basado en el indicador Aroon. Una vez creado este sistema comercial, comprobaremos si puede ser rentable o necesita una mayor optimización.
Desarrollo de un sistema de repetición (Parte 46): Proyecto Chart Trade (V) Desarrollo de un sistema de repetición (Parte 46): Proyecto Chart Trade (V)
¿Cansado de perder tiempo buscando ese archivo que es necesario para que tu aplicación funcione? ¿Qué tal si incluimos todo en el ejecutable? Así nunca perderás tiempo buscando las cosas. Sé que muchos utilizan exactamente esa forma de distribuir y guardar las cosas. Pero existe una manera mucho más adecuada. Al menos en lo que respecta a la distribución de ejecutables y almacenamiento de los mismos. La forma que explicaré aquí, puede ser de gran ayuda. Ya que puedes usar el propio MetaTrader 5 como un gran ayudante, así como el MQL5. No es algo tan complejo ni difícil de entender.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
Algoritmos de optimización de la población: Algoritmo genético binario (Binary Genetic Algorithm, BGA). Parte II Algoritmos de optimización de la población: Algoritmo genético binario (Binary Genetic Algorithm, BGA). Parte II
En este artículo, analizaremos el algoritmo genético binario (BGA), que modela los procesos naturales que ocurren en el material genético de los seres vivos en la naturaleza.