English Русский Español Deutsch 日本語 Português
preview
市场模拟(第三部分):性能问题

市场模拟(第三部分):性能问题

MetaTrader 5示例 |
202 0
Daniel Jose
Daniel Jose

概述

在上一篇文章市场模拟(第二部分):跨期订单(二)中,我演示了如何控制我们在 Chart Trade 中观察到的资产或合约。这适用于我们不直接访问交易合约的系统。相反,我们使用另一种资产来执行此类操作。事实上,该资产是合约的历史数据,上一篇文章解释了这样做的原因。

也就是说,尽管我们现在在开发整个系统方面处于更高级的阶段,但始终有必要确保事情保持在适当的安全性、可靠性和性能水平内。实现这一目标绝非易事。相反,事情远没有那么简单。迟早,我们不可避免地会遇到某种类型的失败或问题。正是在这些时刻,对整个系统如何实施的完美理解变得至关重要。如果你不知道或不理解每个部分实际上在做什么,你可能会发现自己陷入了死胡同,事情会很快恶化。

几周来,我们一直在开发一些我们实际需要的应用程序。然而,当我开始实施一个即将推出的新应用程序时,问题开始出现。与之前观察到的相比,系统性能显著下降。由于我不打算将系统整体构建,即作为一个单独的块,因此有必要分析和确定这种性能下降的原因。

在研究流程图时,我注意到了一些差距。这些差距需要弥合,因为它们迟早会产生问题。因此,解决这些问题并防止其再次发生也是最终应用程序开发的一部分。除了这些将在整篇文章中解释的细节外,在使用鼠标指标时还可以观察到一个小缺陷。这个缺陷没有被注意到,但经过仔细检查,它被发现并得到了纠正,亲爱的读者,你将能够确切地看到它的含义。

这种细节往往被他人忽略或不做解释,可能会导致有抱负的专业程序员错误地认为程序员从不犯错,或者认为代码天生完美,开发时没有错误。事实上,无论多么精心策划,任何代码都不可能没有错误。我希望这篇文章除了解释之外,还能证明以下几点:代码永远不会真正完成。总有一些东西需要改进或纠正。

话虽如此,让我们从一个新话题开始,继续讨论文章本身。


改进封装

影响正在开发的软件的最严重问题之一是信息泄露。这种泄漏可能有多种形式。然而,这里的问题非常具体。该代码旨在广泛使用类,从而实现面向对象的编程。当一个类或使用特定类的应用程序访问了它不应该访问的东西时,就会发生泄漏。这些缺陷通常是由于封装不良或函数或过程存在于不属于它们的类中造成的。在我们的特定情况下,有一个过程不应该被其他代码访问,除了它所属的特定代码。然而,由于我直到现在才注意到这个问题,缺陷仍然存在。

有问题的缺陷是 C_Mouse 类中的 SetBuffer 函数。但为什么这个过程被认为是一个缺陷呢?它在 C_Mouse 类中是否存在固有的问题?我不会说这是错的。然而,经过仔细检查,我意识到这并不完全正确。问题不在于另一个程序(无论是指标还是 EA 交易)可以写入鼠标指标缓冲区。由于 MQL5 固有的安全功能,这种情况不会发生。问题是,当使用它的唯一进程是鼠标指标本身时,该过程存在于 C_Mouse 类中是没有意义的。通过从 C_Mouse 类中删除此过程,我们改进了它的封装,同时确保只有鼠标指标可以修改其缓冲区。

从类中删除该过程要求它继续像以前一样运行。这保证了与任何依赖于鼠标指标数据的现有或以前开发的代码的兼容性。在展示更新的 C_Mouse 类(除了这个之外还经历了其他变化)之前,让我们回顾一下鼠标指标代码。完整代码如下所示:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.82"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/12580"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. double GL_PriceClose;
14. datetime GL_TimeAdjust;
15. //+------------------------------------------------------------------+
16. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
17. //+------------------------------------------------------------------+
18. C_Study *Study       = NULL;
19. //+------------------------------------------------------------------+
20. input color user01   = clrBlack;                   //Price Line
21. input color user02   = clrPaleGreen;               //Positive Study
22. input color user03   = clrLightCoral;              //Negative Study
23. //+------------------------------------------------------------------+
24. C_Study::eStatusMarket m_Status;
25. int m_posBuff = 0;
26. double m_Buff[];
27. //+------------------------------------------------------------------+
28. int OnInit()
29. {
30.    Study = new C_Study(0, "Indicator Mouse Study", user01, user02, user03);
31.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
32.    MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
33.    OnBookEvent((*Study).GetInfoTerminal().szSymbol);
34.    m_Status = C_Study::eCloseMarket;
35.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
36.    ArrayInitialize(m_Buff, EMPTY_VALUE);
37.    
38.    return INIT_SUCCEEDED;
39. }
40. //+------------------------------------------------------------------+
41. int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], 
42.                 const double& high[], const double& low[], const double& close[], const long& tick_volume[], 
43.                 const long& volume[], const int& spread[]) 
44. {
45.    GL_PriceClose = close[rates_total - 1];
46.    if (_Symbol == def_SymbolReplay)
47.       GL_TimeAdjust = spread[rates_total - 1] & (~def_MaskTimeService);
48.    m_posBuff = rates_total;
49.    (*Study).Update(m_Status);   
50.    
51.    return rates_total;
52. }
53. //+------------------------------------------------------------------+
54. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
55. {
56.     uCast_Double info;
57.     C_Mouse::st_Mouse sMouse;
58.     
59.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
60.    sMouse = (*Study).GetInfoMouse();            
61.    info._8b[0] = (uchar)(sMouse.ExecStudy == C_Mouse::eStudyNull ? sMouse.ButtonStatus : 0);
62.    info._16b[1] = (ushort)sMouse.Position.X_Graphics;
63.    info._16b[2] = (ushort)sMouse.Position.Y_Graphics;
64.    if (m_posBuff > 0) m_Buff[m_posBuff - 1] = info.dValue;
65. 
66.    ChartRedraw((*Study).GetInfoTerminal().ID);
67. }
68. //+------------------------------------------------------------------+
69. void OnBookEvent(const string &symbol)
70. {
71.    MqlBookInfo book[];
72.    C_Study::eStatusMarket loc = m_Status;
73.    
74.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
75.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
76.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : (symbol == def_SymbolReplay ? C_Study::eInReplay : C_Study::eInTrading));
77.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
78.       if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
79.    if (loc != m_Status) (*Study).Update(m_Status);
80. }
81. //+------------------------------------------------------------------+
82. void OnDeinit(const int reason)
83. {
84.    MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
85. 
86.    delete Study;
87. }
88. //+------------------------------------------------------------------+

鼠标指标源代码

请注意,与原始代码相比几乎没有变化。您可以参考前面文章中的原始代码。但是,如果您一直密切关注代码,您会注意到 OnChartEvent 过程中的差异。以前,在 SetBuffer 函数的 C_Mouse类 中处理了相同的操作。现在该过程已被删除,我们确保缓冲区仍然在 OnChartEvent 中正确写入。

为什么不在 OnCalculate 函数中执行此操作,这似乎更自然?原因是我们正在处理与鼠标相关的事件。OnCalculate 专为价格相关的计算而设计。由于鼠标指标的目的是提供与鼠标相关的数据,因此我们必须使用更合适的函数。

因此,SetBuffer 中存在的几乎相同的代码现在位于第 61 行和第 64 行之间。然而,在第 64 行,必须执行之前未执行的额外测试。该测试解决了与鼠标指标相关的回放/模拟器系统实验期间观察到的特殊故障。该测试在第 64 行运行,以避免这些故障。实施此测试消除了这些故障,并在正确的时间解决了另一个问题。

除了对鼠标指标代码的改进之外,我还想强调另外两个变化。虽然它们尚未投入使用,但由于我正在将其他应用程序集成到回放/模拟器系统中,因此它们已经在实施中。我可能会在未来的文章中讨论这些更改,但即使我没有讨论,将它们放在适当的位置也可以避免反复删除它们。它们在某些情况下可能被证明是有用的,如果你打算实现类似的东西,你已经可以访问它们了。

第一个是包含 macros.mqh 的头文件。完整文件如下所示。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define macroRemoveSec(A)            (A - (A % 60))
05. #define macroGetDate(A)              (A - (A % 86400))
06. #define macroGetSec(A)               (A - (A - (A % 60)))
07. #define macroGetTime(A)              (A % 86400)
08. #define macroGetMin(A)               (int)((A - (A - ((A % 3600) - (A % 60)))) / 60)
09. #define macroGetHour(A)              (A - (A - ((A % 86400) - (A % 3600))))
10. #define macroHourBiggerOrEqual(A, B) ((A * 3600) < (B - (B - ((B % 86400) - (B % 3600)))))
11. #define macroMinusMinutes(A, B)      (B - ((A * 60) + (B % 60)))
12. #define macroMinusHours(A, B)        (B - (A * 3600))
13. #define macroAddHours(A, B)          (B + (A * 3600))
14. #define macroAddMin(A, B)            (B + (A * 60))
15. #define macroSetHours(A, B)          ((A * 3600) + (B - ((B % 86400))))
16. #define macroSetMin(A, B)            ((A * 60) + (B - (B % 3600)))
17. #define macroSetTime(A, B, C)        ((A * 3600) + (B * 60) + (C - (C % 86400)))
18. //+------------------------------------------------------------------+
19. #define macroColorRGBA(A, B) ((uint)((B << 24) | (A & 0x00FF00) | ((A & 0xFF0000) >> 16) | ((A & 0x0000FF) << 16)))
20. #define macroTransparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0)
21. //+------------------------------------------------------------------+

头文件 Macros.mqh

尽管这个文件的大部分内容都集中在操纵时间上 —— 无论是使用 datetime 类型还是 ulong 来表示日期和时间 —— 但这些函数对于与此类操纵相关的各种任务来说都是非常有价值的。这是因为对于处理器来说,直接执行计算比调用 MQL5 库函数来实现相同的结果更快。您可能不会立即注意到这些计算的有用性,但无论如何,macros.mqh 文件现在都将包含此内容。

另一个也发生变化的头文件是 defines.mqh。然而,变化很小:只添加了一行。即便如此,完整的文件如下所示。

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.                   };
46. //+------------------------------------------------------------------+

头文件 Defines.mqh

问题中的行是第 30 行。在枚举的开头添加它会改变一切。这个事件,即所谓的 Tic-Tac,是一个同步事件。目前,它实际上并未在回放/模拟器系统代码中使用。但是,当需要跨多个组件进行同步时,此事件的需求将很高。它的添加是由于我正在开发一些个人使用的应用程序,如前所述,我正在将所有项目集成到回放/模拟器系统中。

因此,Tic-Tac 事件是目前亲爱的读者可能无法完全理解的回放/模拟器代码的一部分。别担心;也许在将来,我将展示利用此事件的应用程序。

至此,我们结束本节并进入下一部分,主要重点是防止系统性能下降。这是一个已经开始显现的问题。为了清楚地划分主题,让我们进入一个新的部分。


减少性能下降

长期以来,该系统保持了足够的性能。然而,当我进入一个新阶段,更广泛地使用某些功能时,我发现回放/模拟器系统的整体性能下降了。即使为其开发的程序在真实或模拟账户上使用,也会发生这种情况。这种退化的原因非常具体,我将解释原因并提出一个合适的解决方案,至少在这个初始阶段是这样。

造成整体性能下降的主要原因是鼠标指标。为什么呢?并不是鼠标指标消耗的资源比预期的多。而是当指标的缓冲区被更密集地读取时,系统开始变慢。

这是由 C_Mouse 类中的一个过程引起的。具体来说,当读取指标缓冲区时,小的延迟开始累积。这些延迟是由于系统将图形坐标转换为价格时间坐标造成的。如果这种转换发生得太频繁,与鼠标指标相关的价格线的位置就会出现明显的滞后。

解决方案是隔离这种影响。这可以通过降低读取缓冲区的频率或使用另一种技术来实现。由于整个系统是图形化的,我们无法消除鼠标的使用。然而,无需采取极端措施,还是有解决办法的。

关键在于鼠标指标必须区分进行研究的时期和未进行研究的时期。这是其产生的根本原因。在进行研究时,图表上的应用程序必须忽略任何点击。

为了确定鼠标在做什么 —— 是处于活动状态还是研究模式 —— 我们使用了一项测试。它是通过 CheckClick 函数执行的。如果函数返回 true,则可以使用鼠标位置。如果返回 false,则鼠标处于研究模式。在这种情况下,只要应用程序知道鼠标状态,它就应该忽略访问鼠标的任何位置。

这要求图表上存在鼠标指标。解决所有这些问题需要进行一些更改,为了避免使这个讨论过于冗长和乏味(因为大多数更改涉及将功能从一个类移动到另一个类),我不会深入探讨细节。

您需要了解的是,以前,所有工作都是在 C_Mouse 类中执行的,以便任何使用鼠标指标的应用程序都可以访问必要的数据。现在,这项工作由 C_Mouse 和 C_Terminal 负责分担。但是,如果图表上没有鼠标指标,依赖于它的应用程序将无法完全运行。新的类层次结构反映了迄今为止开发的应用程序,如下图所示。

图片

此图展示了更新后的类结构。所以,这就是我们现在的基本结构。每个矩形代表不同的应用程序。尽管 C_ChartFloatingRAD 继承自 C_Mouse,但如果图表上没有鼠标指标,Chart Trade 指标将无法运行。

这似乎有悖常理:C_ChartFloatingRAD 继承 C_Mouse 这一事实可能表明 Chart Trade 将独立运行。然而,由于这些指标的设计方式,Chart Trade 依赖于鼠标指标。如果不存在,用户将无法访问 Chart Trade。

这种行为是故意的,并且被认为是适当的。键盘和鼠标交互都链接到 C_Terminal 和 C_Mouse。这些类旨在最大限度地减少整体性能下降。尽管如此,如果有必要,鼠标指标的缓冲区包含可由 Chart Trade 指标或任何其他应用程序访问的数据。

这个想法是图形终端内的任何组件都应该尽快访问 C_Mouse 数据。如果其他应用程序或服务需要此数据,则可以从外部读取缓冲区,从而转移计算负载并保持整体系统性能。

此外,还进行了一些更改以减少函数调用的数量。通常,如果值在应用程序的生命周期中保持不变,则可以将其存储在私有类变量中。使用变量比重复执行函数来检索相同的值更可取。

因此,许多检索图表 ID 的调用已被对类构造函数中初始化的存储变量的引用所取代。图表打开时该值不会改变。虽然这只节省了几个处理器周期,但这种优化可以产生显著的影响,尤其是在函数调用数量很高时。

单独来看,这些变化可能不会带来显著的绩效提升,但随着时间的推移,它们会变得显著。

最后,让我们检查一下新代码的结构。这些文章的目的不是提供源代码,而是传授知识。我将保持目前的做法:对每一个变化都进行解释。由于应用程序使用如图所示链接的多个文件,因此直接在文章中发布代码可以防止读者在不了解如何编译系统的情况下简单地复制和粘贴它。

对于初学者,我对这种方法表示歉意。我的目标不是鼓励 CTRL+C/CTRL+V 编程,这常常导致代码损坏。相反,我希望你能理解其背后的逻辑。那些有一些编程知识的人可以很容易地访问这些源代码。我们现在从 C_Terminal 类开始。完整的新类如下所示:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "Macros.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. class C_Terminal
008. {
009. //+------------------------------------------------------------------+      
010.    public   :
011. //+------------------------------------------------------------------+      
012.       struct st_Mouse
013.       {
014.          struct st00
015.          {
016.             short    X_Adjusted,
017.                      Y_Adjusted,
018.                      X_Graphics,
019.                      Y_Graphics;
020.             double   Price;
021.             datetime dt;
022.          }Position;
023.          uchar      ButtonStatus;
024.          bool       ExecStudy;
025.       };
026. //+------------------------------------------------------------------+
027.    protected:
028.       enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
029. //+------------------------------------------------------------------+
030.       struct st_Terminal
031.       {
032.          ENUM_SYMBOL_CHART_MODE   ChartMode;
033.          ENUM_ACCOUNT_MARGIN_MODE TypeAccount;
034.          long           ID;
035.          string         szSymbol;
036.          int            Width,
037.                         Height,
038.                         nDigits,
039.                         SubWin,
040.                         HeightBar;
041.          double         PointPerTick,
042.                         ValuePerPoint,
043.                         VolumeMinimal,
044.                         AdjustToTrade;
045.       };
046. //+------------------------------------------------------------------+
047.       void CurrentSymbol(bool bUsingFull)
048.          {
049.             MqlDateTime mdt1;
050.             string sz0, sz1;
051.             datetime dt = macroGetDate(TimeCurrent(mdt1));
052.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
053.       
054.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
055.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
056.             switch (eTS)
057.             {
058.                case DOL   :
059.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
060.                case IND   :
061.                case WIN   : sz1 = "GJMQVZ";       break;
062.                default    : return;
063.             }
064.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
065.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
066.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
067.          }
068. //+------------------------------------------------------------------+
069. inline void DecodeMousePosition(int xi, int yi)
070.          {
071.             int w = 0;
072.             
073.             xi = (xi > 0 ? xi : 0);
074.             yi = (yi > 0 ? yi : 0);
075.             ChartXYToTimePrice(m_Infos.ID, m_Mouse.Position.X_Graphics = (short)xi, m_Mouse.Position.Y_Graphics = (short)yi, w, m_Mouse.Position.dt, m_Mouse.Position.Price);
076.             m_Mouse.Position.dt = AdjustTime(m_Mouse.Position.dt);
077.             m_Mouse.Position.Price = AdjustPrice(m_Mouse.Position.Price);
078.             ChartTimePriceToXY(m_Infos.ID, w, m_Mouse.Position.dt, m_Mouse.Position.Price, xi, yi);
079.             yi -= (int)ChartGetInteger(m_Infos.ID, CHART_WINDOW_YDISTANCE, m_Infos.SubWin);
080.             m_Mouse.Position.X_Adjusted = (short) xi;
081.             m_Mouse.Position.Y_Adjusted = (short) yi;
082.          }
083. //+------------------------------------------------------------------+
084.    private   :
085.       st_Terminal m_Infos;
086.       st_Mouse    m_Mouse;
087.       struct mem
088.       {
089.          long    Show_Descr,
090.                  Show_Date;
091.          bool    AccountLock;
092.       }m_Mem;
093. //+------------------------------------------------------------------+
094. inline void ChartChange(void)
095.          {
096.             int x, y, t;
097.             
098.             m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
099.             m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
100.             ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t);
101.             ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y);
102.             m_Infos.HeightBar = (int)(t - y) / 100;
103.          }
104. //+------------------------------------------------------------------+
105.    public   :
106. //+------------------------------------------------------------------+      
107.       C_Terminal(const long id = 0, const uchar sub = 0)
108.          {
109.             m_Infos.ID = (id == 0 ? ChartID() : id);
110.             m_Mem.AccountLock = false;
111.             m_Infos.SubWin = (int) sub;
112.             CurrentSymbol(false);
113.             ZeroMemory(m_Mouse);
114.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
115.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
116.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
117.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
118.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
119.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
120.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
121.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
122.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
123.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
124.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
125.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
126.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
127.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
128.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
129.             ChartChange();
130.          }
131. //+------------------------------------------------------------------+
132.       ~C_Terminal()
133.          {
134.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
135.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
136.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false);
137.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false);
138.          }
139. //+------------------------------------------------------------------+
140. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg)
141.          {
142.             if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true;
143.             m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING);
144.          }
145. //+------------------------------------------------------------------+
146. inline const st_Terminal GetInfoTerminal(void) const
147.          {
148.             return m_Infos;
149.          }
150. //+------------------------------------------------------------------+
151. inline const st_Mouse GetPositionsMouse(void) const
152.          {
153.             return m_Mouse;
154.          }
155. //+------------------------------------------------------------------+
156. const double AdjustPrice(const double arg) const
157.          {
158.             return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits);
159.          }
160. //+------------------------------------------------------------------+
161. inline datetime AdjustTime(const datetime arg)
162.          {
163.             int nSeconds= PeriodSeconds();
164.             datetime   dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0);
165.             
166.             return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt)));
167.          }
168. //+------------------------------------------------------------------+
169. inline double FinanceToPoints(const double Finance, const uint Leverage)
170.          {
171.             double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1));
172.             
173.             return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
174.          };
175. //+------------------------------------------------------------------+
176.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
177.          {
178.             static string st_str = "";
179.             
180.             switch (id)
181.             {
182.                case CHARTEVENT_CHART_CHANGE:
183.                   m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
184.                   m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
185.                   ChartChange();
186.                   break;
187.                case CHARTEVENT_MOUSE_MOVE:
188.                   DecodeMousePosition((int)lparam, (int)dparam);
189.                   break;
190.                case CHARTEVENT_OBJECT_CLICK:
191.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
192.                   if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
193.                      ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
194.                   break;
195.                case CHARTEVENT_OBJECT_CREATE:
196.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
197.                   st_str = sparam;
198.                   break;
199.             }
200.          }
201. //+------------------------------------------------------------------+
202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) 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.             ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
207.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
208.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
209.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
210.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
211.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
212.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
213.          }
214. //+------------------------------------------------------------------+
215.       bool IndicatorCheckPass(const string szShortName)
216.          {
217.             string szTmp = szShortName + "_TMP";
218.             
219.             IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
220.             m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin);
221.             if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE)
222.             {
223.                ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
224.                Print("Only one instance is allowed...");
225.                SetUserError(C_Terminal::ERR_NoMoreInstance);
226.                
227.                return false;
228.             }
229.             IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
230.    
231.             return true;
232.          }
233. //+------------------------------------------------------------------+
234. };

头文件 C_Terminal.mqh

那些第一次看到这段代码的人可能会认为它庞大而复杂。然而,大部分仍保持完好。也就是说,进行了更改,因为它现在协助 C_Mouse 类执行其任务。这就是为什么我们现在在 DispatchMessage 过程中看到 CHARTEVENT_MOUSE_MOVE 事件。大部分工作将在这里处理。

请注意,在第 12 行,我们现在有一个提供鼠标数据的结构。但是,第 69 行的 DecodeMousePosition 过程不会填充与鼠标按钮相关的数据。这个责任现在属于 C_Mouse 类。因此,下一个要审查的项目是 C_Mouse 类本身,如下所示。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_MousePrefixName "MouseBase" + (string)GetInfoTerminal().SubWin + "_"
007. #define macro_NameObjectStudy (def_MousePrefixName + "T" + (string)ObjectsTotal(0))
008. //+------------------------------------------------------------------+
009. class C_Mouse : public C_Terminal
010. {
011.    public   :
012.       enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
013.       enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
014. //+------------------------------------------------------------------+
015.    protected:
016. //+------------------------------------------------------------------+
017.       void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const
018.          {
019.             if (!m_Mem.IsInitOk) return;
020.             CreateObjectGraphics(szName, OBJ_BUTTON);
021.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_STATE, true);
022.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_BORDER_COLOR, clrBlack);
023.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_COLOR, clrBlack);
024.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_BGCOLOR, backColor);
025.             ObjectSetString(m_Mem.id, szName, OBJPROP_FONT, "Lucida Console");
026.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_FONTSIZE, 10);
027.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
028.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_XDISTANCE, x);
029.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
030.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_XSIZE, w); 
031.             ObjectSetInteger(m_Mem.id, szName, OBJPROP_YSIZE, 18);
032.          }
033. //+------------------------------------------------------------------+
034.    private   :
035.       enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
036.       struct st01
037.       {
038.          st_Mouse Data;
039.          color    corLineH,
040.                   corTrendP,
041.                   corTrendN;
042.          eStudy   Study;
043.       }m_Info;
044.       struct st_Mem
045.       {
046.          bool     CrossHair,
047.                   IsFull,
048.                   IsInitOk;
049.          datetime dt;
050.          string   szShortName,
051.                   szLineH,
052.                   szLineV,
053.                   szLineT,
054.                   szBtnS;
055.          long     id;
056.       }m_Mem;
057. //+------------------------------------------------------------------+
058.       void GetDimensionText(const string szArg, int &w, int &h)
059.          {
060.             TextSetFont("Lucida Console", -100, FW_NORMAL);
061.             TextGetSize(szArg, w, h);
062.             h += 5;
063.             w += 5;
064.          }
065. //+------------------------------------------------------------------+
066.       void CreateStudy(void)
067.          {
068.             if (m_Mem.IsFull)
069.             {
070.                CreateObjectGraphics(m_Mem.szLineV = macro_NameObjectStudy, OBJ_VLINE, m_Info.corLineH);
071.                CreateObjectGraphics(m_Mem.szLineT = macro_NameObjectStudy, OBJ_TREND, m_Info.corLineH);
072.                ObjectSetInteger(m_Mem.id, m_Mem.szLineT, OBJPROP_WIDTH, 2);
073.                CreateObjToStudy(0, 0, m_Mem.szBtnS = macro_NameObjectStudy);
074.             }
075.             m_Info.Study = eStudyCreate;
076.          }
077. //+------------------------------------------------------------------+
078.       void ExecuteStudy(const double memPrice)
079.          {
080.             double v1 = GetPositionsMouse().Position.Price - memPrice;
081.             int w, h;
082.             
083.             if (!CheckClick(eClickLeft))
084.             {
085.                m_Info.Study = eStudyNull;
086.                ChartSetInteger(m_Mem.id, CHART_MOUSE_SCROLL, true);
087.                if (m_Mem.IsFull)   ObjectsDeleteAll(m_Mem.id, def_MousePrefixName + "T");
088.             }else if (m_Mem.IsFull)
089.             {
090.                string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ",
091.                                          MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetPositionsMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0));
092.                GetDimensionText(sz1, w, h);
093.                ObjectSetString(m_Mem.id, m_Mem.szBtnS, OBJPROP_TEXT, sz1);                                                
094.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
095.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_XSIZE, w);
096.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_YSIZE, h);
097.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_XDISTANCE, GetPositionsMouse().Position.X_Adjusted - w);
098.                ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h));            
099.                ObjectMove(m_Mem.id, m_Mem.szLineT, 1, GetPositionsMouse().Position.dt, GetPositionsMouse().Position.Price);
100.                ObjectSetInteger(m_Mem.id, m_Mem.szLineT, OBJPROP_COLOR, (memPrice > GetPositionsMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
101.             }
102.             m_Info.Data.ButtonStatus = eKeyNull;
103.          }
104. //+------------------------------------------------------------------+
105.    public   :
106. //+------------------------------------------------------------------+
107.       C_Mouse(const long id, const string szShortName)
108.          :C_Terminal(id)
109.          {
110.             m_Mem.IsInitOk = false;
111.             m_Mem.id = GetInfoTerminal().ID;
112.             m_Mem.szShortName = szShortName;
113.          }
114. //+------------------------------------------------------------------+
115.       C_Mouse(const long id, const string szShortName, color corH, color corP, color corN)
116.          :C_Terminal(id)
117.          {
118.             m_Mem.id = GetInfoTerminal().ID;
119.             if (!(m_Mem.IsInitOk = IndicatorCheckPass(m_Mem.szShortName = szShortName))) return;
120.             m_Mem.CrossHair = (bool)ChartGetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL);
121.             ChartSetInteger(m_Mem.id, CHART_EVENT_MOUSE_MOVE, true);
122.             ChartSetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL, false);
123.             ZeroMemory(m_Info);
124.             m_Info.corLineH  = corH;
125.             m_Info.corTrendP = corP;
126.             m_Info.corTrendN = corN;
127.             m_Info.Study = eStudyNull;
128.             if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
129.                CreateObjectGraphics(m_Mem.szLineH = (def_MousePrefixName + (string)ObjectsTotal(0)), OBJ_HLINE, m_Info.corLineH);
130.             ChartRedraw(m_Mem.id);
131.          }
132. //+------------------------------------------------------------------+
133.       ~C_Mouse()
134.          {
135.             if (!m_Mem.IsInitOk) return;
136.             ChartSetInteger(m_Mem.id, CHART_EVENT_OBJECT_DELETE, false);
137.             ChartSetInteger(m_Mem.id, CHART_EVENT_MOUSE_MOVE, ChartWindowFind(m_Mem.id, m_Mem.szShortName) != -1);
138.             ChartSetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
139.             ObjectsDeleteAll(m_Mem.id, def_MousePrefixName);
140.          }
141. //+------------------------------------------------------------------+
142. inline bool CheckClick(const eBtnMouse value) 
143.          {
144.             return (m_Info.Data.ButtonStatus & value) == value;
145.          }
146. //+------------------------------------------------------------------+
147. inline const st_Mouse GetInfoMouse(void)
148.          {
149.             if (!m_Mem.IsInitOk)
150.             {
151.                double Buff[];
152.                uCast_Double loc;
153.                int handle = ChartIndicatorGet(m_Mem.id, 0, m_Mem.szShortName);
154. 
155.                ZeroMemory(m_Info.Data);
156.                if (CopyBuffer(handle, 0, 0, 1, Buff) == 1)
157.                {
158.                   loc.dValue = Buff[0];
159.                   DecodeMousePosition((int)loc._16b[1], (int)loc._16b[2]);
160.                   m_Info.Data = GetPositionsMouse();
161.                   m_Info.Data.ButtonStatus = loc._8b[0];
162.                }
163.                IndicatorRelease(handle);
164.             }
165.             return m_Info.Data;
166.          }
167. //+------------------------------------------------------------------+*/
168.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
169.          {
170.             int w = 0;
171.             static double memPrice = 0;
172.       
173.             C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
174.             switch (id)
175.             {
176.                case (CHARTEVENT_CUSTOM + evHideMouse):
177.                   if (m_Mem.IsFull)   ObjectSetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR, clrNONE);
178.                   break;
179.                case (CHARTEVENT_CUSTOM + evShowMouse):
180.                   if (m_Mem.IsFull) ObjectSetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR, m_Info.corLineH);
181.                   break;
182.                case CHARTEVENT_MOUSE_MOVE:
183.                   m_Info.Data = GetPositionsMouse();
184.                   if (m_Mem.IsFull) ObjectMove(m_Mem.id, m_Mem.szLineH, 0, 0, m_Info.Data.Position.Price);
185.                   if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(m_Mem.id, m_Mem.szLineV, 0, m_Info.Data.Position.dt, 0);
186.                   m_Info.Data.ButtonStatus = (uchar) sparam;
187.                   if (CheckClick(eClickMiddle) && (m_Info.Study != eStudyCreate))
188.                      if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
189.                   if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
190.                   {
191.                      ChartSetInteger(m_Mem.id, CHART_MOUSE_SCROLL, false);
192.                      if (m_Mem.IsFull)   ObjectMove(m_Mem.id, m_Mem.szLineT, 0, m_Mem.dt = m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price);
193.                      m_Info.Study = eStudyExecute;
194.                   }
195.                   if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
196.                   m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
197.                   break;
198.                case CHARTEVENT_OBJECT_DELETE:
199.                   if ((m_Mem.IsFull) && (sparam == m_Mem.szLineH))
200.                      CreateObjectGraphics(m_Mem.szLineH, OBJ_HLINE, m_Info.corLineH);
201.                   break;
202.             }
203.          }
204. //+------------------------------------------------------------------+
205. };
206. //+------------------------------------------------------------------+
207. #undef macro_NameObjectStudy
208. //+------------------------------------------------------------------+

头文件 C_Mouse.mqh

虽然这个类乍一看似乎没有变化,但它已经经历了几次修改。这些变化旨在使鼠标指标的使用更加明确。您可以看到存在一些差异。我将其作为爱好者和有抱负的专业程序员的练习,将此代码与 C_Mouse 类的先前版本进行比较。相信我,这将是非常有益和愉快的,揭示了类继承的许多有趣方面。这里有一个提示来激发你的好奇心。看看第 187 行,问问自己:为什么在创建研究的过程中,我要检查研究是否已经在进行中?

不出所料,C_Study 类代码也被修改了。这些更改的唯一目的是为了防止通过函数调用重复检索常量值(在本例中为图表 ID)。更新后的 C_Study 类代码完整内容如下。

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_"
007. //+------------------------------------------------------------------+
008. class C_Study : public C_Mouse
009. {
010.    private   :
011. //+------------------------------------------------------------------+
012.       struct st00
013.       {
014.          eStatusMarket  Status;
015.          MqlRates       Rate;
016.          string         szInfo,
017.                         szBtn1,
018.                         szBtn2,
019.                         szBtn3;
020.          color          corP,
021.                         corN;
022.          int            HeightText;
023.          bool           bvT, bvD, bvP;
024.          long           id;
025.       }m_Info;
026. //+------------------------------------------------------------------+
027.       void Draw(void)
028.          {
029.             double v1;
030.             
031.             if (m_Info.bvT)
032.             {
033.                ObjectSetInteger(m_Info.id, m_Info.szBtn1, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 18);
034.                ObjectSetString(m_Info.id, m_Info.szBtn1, OBJPROP_TEXT, m_Info.szInfo);
035.             }
036.             if (m_Info.bvD)
037.             {
038.                v1 = NormalizeDouble((((GetPositionsMouse().Position.Price - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
039.                ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1);
040.                ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
041.                ObjectSetString(m_Info.id, m_Info.szBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
042.             }
043.             if (m_Info.bvP)
044.             {
045.                v1 = NormalizeDouble((((GL_PriceClose - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
046.                ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1);
047.                ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
048.                ObjectSetString(m_Info.id, m_Info.szBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
049.             }
050.          }
051. //+------------------------------------------------------------------+
052. inline void CreateObjInfo(EnumEvents arg)
053.          {
054.             switch (arg)
055.             {
056.                case evShowBarTime:
057.                   C_Mouse::CreateObjToStudy(2, 110, m_Info.szBtn1 = (def_ExpansionPrefix + (string)ObjectsTotal(0)), clrPaleTurquoise);
058.                   m_Info.bvT = true;
059.                   break;
060.                case evShowDailyVar:
061.                   C_Mouse::CreateObjToStudy(2, 53, m_Info.szBtn2 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
062.                   m_Info.bvD = true;
063.                   break;
064.                case evShowPriceVar:
065.                   C_Mouse::CreateObjToStudy(58, 53, m_Info.szBtn3 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
066.                   m_Info.bvP = true;
067.                   break;
068.             }
069.          }
070. //+------------------------------------------------------------------+
071. inline void RemoveObjInfo(EnumEvents arg)
072.          {
073.             string sz;
074.             
075.             switch (arg)
076.             {
077.                case evHideBarTime:
078.                   sz = m_Info.szBtn1;
079.                   m_Info.bvT = false;
080.                   break;
081.                case evHideDailyVar:
082.                   sz = m_Info.szBtn2;
083.                   m_Info.bvD   = false;
084.                   break;
085.                case evHidePriceVar:
086.                   sz = m_Info.szBtn3;
087.                   m_Info.bvP = false;
088.                   break;
089.             }
090.             ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, false);
091.             ObjectDelete(m_Info.id, sz);
092.             ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, true);
093.          }
094. //+------------------------------------------------------------------+
095.    public   :
096. //+------------------------------------------------------------------+
097.       C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
098.          :C_Mouse(IdParam, szShortName, corH, corP, corN)
099.          {
100.             ZeroMemory(m_Info);
101.             m_Info.id = GetInfoTerminal().ID;
102.             if (_LastError >= ERR_USER_ERROR_FIRST) return;
103.             m_Info.corP = corP;
104.             m_Info.corN = corN;
105.             CreateObjInfo(evShowBarTime);
106.             CreateObjInfo(evShowDailyVar);
107.             CreateObjInfo(evShowPriceVar);
108.             ResetLastError();
109.          }
110. //+------------------------------------------------------------------+
111.       void Update(const eStatusMarket arg)
112.          {
113.             int i0;
114.             datetime dt;
115.                
116.             if (m_Info.Rate.close == 0)
117.                m_Info.Rate.close = iClose(NULL, PERIOD_D1, ((_Symbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(NULL, PERIOD_D1, 0))) ? 0 : 1));
118.             switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
119.             {
120.                case eCloseMarket   :
121.                   m_Info.szInfo = "Closed Market";
122.                   break;
123.                case eInReplay      :
124.                case eInTrading     :
125.                   i0 = PeriodSeconds();
126.                   dt = (m_Info.Status == eInReplay ? (datetime) GL_TimeAdjust : TimeCurrent());
127.                   m_Info.Rate.time = (m_Info.Rate.time <= dt ? (datetime)(((ulong) dt / i0) * i0) + i0 : m_Info.Rate.time);
128.                   if (dt > 0) m_Info.szInfo = TimeToString((datetime)m_Info.Rate.time - dt, TIME_SECONDS);
129.                   break;
130.                case eAuction      :
131.                   m_Info.szInfo = "Auction";
132.                   break;
133.                default            :
134.                   m_Info.szInfo = "ERROR";
135.             }
136.             Draw();
137.          }
138. //+------------------------------------------------------------------+
139. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
140.          {
141.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
142.             switch (id)
143.             {
144.                case CHARTEVENT_CUSTOM + evHideBarTime:
145.                   RemoveObjInfo(evHideBarTime);
146.                   break;
147.                case CHARTEVENT_CUSTOM + evShowBarTime:
148.                   CreateObjInfo(evShowBarTime);
149.                   break;
150.                case CHARTEVENT_CUSTOM + evHideDailyVar:
151.                   RemoveObjInfo(evHideDailyVar);
152.                   break;
153.                case CHARTEVENT_CUSTOM + evShowDailyVar:
154.                   CreateObjInfo(evShowDailyVar);
155.                   break;
156.                case CHARTEVENT_CUSTOM + evHidePriceVar:
157.                   RemoveObjInfo(evHidePriceVar);
158.                   break;
159.                case CHARTEVENT_CUSTOM + evShowPriceVar:
160.                   CreateObjInfo(evShowPriceVar);
161.                   break;
162.                case CHARTEVENT_MOUSE_MOVE:
163.                   Draw();
164.                   break;
165.             }
166.             ChartRedraw(m_Info.id);
167.          }
168. //+------------------------------------------------------------------+
169. };
170. //+------------------------------------------------------------------+
171. #undef def_ExpansionPrefix
172. #undef def_MousePrefixName
173. //+------------------------------------------------------------------+

头文件 C_Study.mqh

如果您使用到目前为止审查过的所有源代码,您将能够创建鼠标指标。然而,还需要两个文件才能完成本文。我不想把它们推迟到以后的文章中,因为下一篇文章将涵盖不同的主题。这样,一切都会准备好,我们在一段时间内避免再次讨论这个话题。现在让我们检查最后两个源文件,从 C_ChartFloatingRAD 类开始,完整显示如下:

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

头文件 C_ChartFloatingRAD.mqh

尽管此代码可能看起来很广泛,但任何关注此回放/模拟系列的人都会注意到其中大部分保持不变。对于研究代码的人来说,修改是很少的。但是,您会注意到许多对 GetInfoTerminal 的调用已被 m_Init.id 取代。之所以做出此更改,是因为访问变量比调用函数快得多。

在第 224 行,您可以看到捕获图表 ID 的位置,以便随后在整个类中使用。然而,关键点是第 306 行的 CHARTEVENT_MOUSE_MOVE 事件。请注意,尽管第 307 行调用了 C_Mouse 类,但如果鼠标指标不存在,它将返回 false。当指标存在时,它将返回 true。

请注意第 309 行,其中 CheckMousePosition 过程不使用 lparam 和 dparam 数据,而这些数据通常包含鼠标位置值。查看第 140 行的该过程后,您将看到第 145 行检索位置数据。它由 C_Terminal 类解释。第 145 行和 146 行指定了我们要使用的值。在这种情况下,我们使用绝对值,但也可以使用相对值。

这些值来自 C_Terminal 的解释,而不是鼠标指标缓冲区。这种方法提高了执行效率和速度。尽管如此,相同的值也存储在鼠标指标缓冲区中。

最后,我们来到最后一个源文件,由于 C_ChartFloatingRAD 构造函数的修改,该文件也发生了变化。这是 Chart Trade 指标的源代码。如下所示:

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

Chart Trade 指标源代码

请注意,唯一的变化发生在第 23 行。对于那些没有看过以前版本的人,提供了完整的代码以供参考。


最后的探讨

在本文中,我介绍了对代码所做的所有更改,以增强其可持续性和可维护性。在下一篇文章中,我们将探讨一个不同的主题。本文附带了鼠标指标和 Chart Trade 指标的编译应用程序,使您可以在交易中使用最新版本。我们将在下一篇文章中继续。

File 03 描述
Experts\Expert Advisor.mq5
演示 Chart Trade 和 EA 交易之间的交互(交互需要鼠标研究)
Indicadores\Chart Trade.mq5 创建用于配置要发送的订单的窗口(需要进行鼠标研究才能进行交互)
Indicadores\Market Replay.mq5 创建与回放/模拟器服务交互的控件(交互需要鼠标研究)
Indicadores\Mouse Study.mq5 实现图形控件和用户之间的交互(操作回放模拟器和实时市场交易所需)
Servicios\Market Replay.mq5 创建并维护市场回放和模拟服务(整个系统的主文件)


本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/12580

附加的文件 |
Anexo.zip (490.53 KB)
MQL5自动化交易策略(第十一部分):开发多层级网格交易系统 MQL5自动化交易策略(第十一部分):开发多层级网格交易系统
在本文中,我们将使用MQL5开发一款多层级网格交易系统EA,重点探讨网格交易策略背后的架构与算法设计。我们将研究多层网格逻辑的实现方式以及应对不同市场状况的风险管理技术。最后,我们将提供详尽的解释和实用技巧,指导您完成自动化交易系统的构建、测试与优化。
具有强化学习和灭绝失败个体的进化交易算法(ETARE) 具有强化学习和灭绝失败个体的进化交易算法(ETARE)
在本文中,我介绍了一种创新的交易算法,其针对外汇交易结合了进化算法与深度强化学习。该算法利用低效个体灭绝机制来优化交易策略。
价格行为分析工具包开发(第十八部分):四分位理论(3)——四分位看板 价格行为分析工具包开发(第十八部分):四分位理论(3)——四分位看板
本文中,我们在原有四分位脚本的基础上新增 "四分位看板"(Quarters Board) 工具,该工具让您无需返回代码即可直接在图表上切换四分位水平。您可以轻松启用或禁用特定水平,EA还会提供趋势方向注释,帮助您更好地理解市场走势。
价格行为分析工具包开发(第 17 部分):TrendLoom EA 工具 价格行为分析工具包开发(第 17 部分):TrendLoom EA 工具
作为一名价格行为的观察者和交易者,我注意到当一个趋势得到多个时间周期的确认时,它通常会朝着该方向延续。可能不同的是趋势持续的时间,而这取决于您是哪种类型的交易者,无论是长期持仓还是从事剥头皮交易。您为确认所选的时间周期起着至关重要的作用。读这篇文章,了解一个快速、自动化的系统,只需点击一下按钮或通过定期更新,就能帮助您分析不同时间周期的整体趋势。