市场模拟(第 24 部分):仓位视图(二)
引言
大家好,欢迎阅读本系列关于如何创建回放/模拟系统的又一篇文章。
在上一篇文章“市场模拟:仓位视图(一)”中,我们开始探讨创建并实现一个指标的可能性,该指标能让我们直接在交易品种图表上对未平仓位进行可视化展示。尽管许多人认为这没有必要,因为 MetaTrader 5 已经提供了这样的功能,但当我们使用交叉订单执行模型时,甚至在我们特定的回放/模拟系统的情况下,这种方法并不奏效。
如前所述,当我们开始处理图表上大量对象时,会出现一些问题,尤其是当这些对象以某种方式相互连接时,这会使整体实现变得更加复杂。话虽如此,我们在此的目的并非详述所面临的问题,而是展示我们决定实施的解决方案。如前所述,我们即将探讨的既不是最终解决方案,也不是唯一可能的方案。亲爱的读者,我建议你在开始编写越来越多的代码之前,先停下来思考一下,看看是否有明确的需求。
由于我们即将讨论的主题相当广泛,在此引言部分我们不会深入细节。我的目的是,在本文结束时,我们将创造出比之前发表的内容略为优秀的东西。那么,让我们开始工作吧。
为实施做好准备
好的,我们在上一篇文章中研究的指标显示的是关于单个未平仓位的数据。不过,它是在终端中以文本形式显示这些数据的。这并不太吸引人,因为包括我在内的许多人希望信息能直接显示在图表上。这将是本文的目标。我们只需创建一个以价格为纵坐标的水平线对象,并将该坐标设置为我们上一篇文章中获得的价格值。非常简单易懂。然而,可能会出现一些问题。但我们会一步步解决。
首先,我们将实现这些线。是的,我们需要三条线来显示仓位指标。一条线用于显示开仓价格,另外两条线:一条用于显示止损水平,另一条用于显示止盈水平。但是,为了创建这些线,我们将使用回放/模拟代码中已经实现的内容。
所以,亲爱的读者,既然你可能想用其他方式来组织这些内容,那么让我们从创建一个优先级列表开始。该列表可以在以下代码中看到:
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, //Tick-tock event 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 sell event 42. evChartTradeCloseAll, //Event to close positions 43. evChartTrade_At_EA, //Event for communication with the EA 44. evEA_At_ChartTrade, //Event for communication with Chart Trade 45. evChatWriteSocket, //Event to Mini Chat 46. evChatReadSocket //Event To Mini Chat 47. }; 48. //+------------------------------------------------------------------+ 49. enum EnumPriority { //Priority list on objects 50. ePriorityNull = -1, 51. ePriorityDefault = 0, 52. ePriorityChartTrade = 5, 53. ePriorityOrders = 10 54. }; 55. //+------------------------------------------------------------------+
Defines.mqh 文件的源代码
请注意我们对 Defines.mqh 文件所做的更改。它包括在第 49 行添加枚举。但是为什么呢?原因很简单:正如我在上一篇文章中提到的,图形对象最终可能会相互重叠。最糟糕的情况是两个线型对象完全重叠。在这种情况下,没有快速简便的方法来选择其中一个对象。然而,如果我们告诉 MetaTrader 5,即使是同一类型的对象也会有不同的优先级,那么 MetaTrader 5 在选择重叠对象时就会考虑到这一点,并会优先考虑具有更高值的对象。即便如此,在这样做时也要小心。我不希望你养成以牺牲其他对象为代价,为自己的对象分配越来越高的优先级值的习惯。
请注意,我们定义了四个优先级。第一级是分配给我们的应用程序创建的所有不需要高优先级的对象。其值为 -1。另一方面,第 51 行表示当用户向图表添加对象时,MetaTrader 5 通常使用的值。换句话说,这类对象的值设为零。由于某些对象可能具有一定的透明度,并且在图表重绘时可能会位于列表顶部,因此它们可能会部分遮挡 “Chart Trade” 面板。但是,请务必小心,因为即使 “Chart Trade” 面板完全隐藏,即位于某个对象后面,当您点击它所在区域时,MetaTrader 5 也会优先处理它,而不是前景中的对象。
这是通过为其分配优先级 5 来实现的。第 53 行也是如此。请注意,Chart Trade 与我们稍后将创建的对象之间在优先级上有所不同。这种优先级之间的间隔使我们在有需要时还能插入其他优先级层级。因此,根据刚才展示的模型,将成为订单和持仓可视化系统一部分的对象将拥有最高的优先级。您可以根据需要进行更改,但请记住我刚才提到的内容。
好,完成这些后,我们还需要对现有代码进行另外两处修改。但无需担心或为此感到惊慌。这些修改非常简单,只是为了使用这个列表。现在让我们修改 C_Terminal 类的代码。需要修改的地方见以下片段。
201. //+------------------------------------------------------------------+ 202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const EnumPriority zOrder = ePriorityNull) const 203. { 204. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false); 205. ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0); 206.
C_Terminal 类中的片段
第 202 行应更新为此处所示的版本。我们在此所做的只是让这个函数使用我们刚刚定义的优先级列表。另一个需要更改的片段也可以在以下片段中看到。它位于 C_ChartFloatingRAD 类中。
164. //+------------------------------------------------------------------+ 165. template <typename T > 166. void CreateObjectEditable(eObjectsIDE arg, T value) 167. { 168. DeleteObjectEdit(); 169. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, ePriorityChartTrade); 170. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 171. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
C_ChartFloatingRAD 类中的片段
必须在第 169 行进行修改。我们需要用之前片段中显示的行替换原始行。这意味着,如果我们更改了优先级列表并重新编译应用程序,那么如果 Chart Trade 指标自身的优先级发生了变化,它将自动切换为使用新的优先级。因此,我们无需再对现有代码进行任何其他更改,并且可以开始开发新的指标。
开始实现新对象
在继续讨论更复杂的代码之前,让我们先放慢节奏,以便你能跟上我的思路。因此,我们将从下面的代码开始,它只是对上一篇文章中我们考察过的代码的一个改进。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName) 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. } 30. //+------------------------------------------------------------------+ 31. int OnInit() 32. { 33. ZeroMemory(glVariables); 34. Terminal = new C_Terminal(); 35. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 36. glVariables.id = (*Terminal).GetInfoTerminal().ID; 37. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 38. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice); 39. 40. return INIT_SUCCEEDED; 41. } 42. //+------------------------------------------------------------------+ 43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 44. { 45. return rates_total; 46. } 47. //+------------------------------------------------------------------+ 48. void OnDeinit(const int reason) 49. { 50. delete Terminal; 51. if (glVariables.id > 0) 52. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 53. } 54. //+------------------------------------------------------------------+
仓位指标的源代码
非常好,看着之前的代码,你可能已经在想:“好吧,现在事情真的开始变得复杂了。”不过并非如此,我们仍然处于最基础的阶段。如果指标所安装的图表对应的交易品种存在未平仓位,以上代码只会在图表上创建一条线。这段代码非常简单、紧凑,并且不包含任何检查。程序只是创建一个 OBJ_HLINE 水平线对象,并使用用户指定的颜色在图表上显示它。非常简单。但如果你习惯于使用 MQL5 在图表上放置对象,你可能会惊讶地发现,这里没有出现你习惯看到的任何调用。所以问题来了:这段代码真的有效吗?如果真的有效,它是如何运作的?
让我们从第 08 行开始。这里有一个定义,就像接下来的两行一样,我们定义了将要创建的线对象的后缀。这样做的原因稍后会揭晓。在第 14 行到第 16 行中,我们允许用户更改线使用的颜色。到目前为止,还没有什么太复杂的。然而,事情从这里开始变得有趣起来。在第 19 行,我们开始定义一个稍后会消失的结构。不过,现在我们先这样处理,因为亲爱的读者,你需要理解我的思路。这样,你就能明白我为什么这样表述了。
让我们暂时跳过第 25 行的过程,继续看第 31 行。这是 MetaTrader 5 中指标开始工作的地方。请注意,我们做的第一件事是将 glVariables 结构的内存清零。为什么要这样做?正如我们之前所说,原因是这个结构以后会消失。但即使它保持不变,我们也需要确保结构中所有的变量初始值都是零。使用所示的调用语句来执行此操作,比逐个声明变量要容易得多。而且,如果我碰巧忘记了一个变量,它肯定会保持零值。
好的,第 34 行和第 35 行已经在上一篇文章中解释过了,所以我们可以跳过它们。另一方面,第 36 行只是保存存储在 C_Terminal 类中的值以供以后使用。现在我们来看主要代码行。在第一行,即第 37 行,我们定义了一个值,该值将作为所创建对象(在本例中为 HLINE 对象)名称的前缀。该前缀的值正是用于标识持仓 ticket 的数值。这样可以确保图表上只会有一个具有此名称的对象。
但是,由于我们稍后会重复使用这个相同的值,因此我们需要避免 MetaTrader 5 把接下来要创建的这些对象误判为同一个对象,并误认为该对象已经存在,而实际上我们是在创建一个新的对象。现在你开始理解第 08 行到第 10 行的定义了。但让我们回到代码,因为我们现在到了第 38 行,在这里我们调用第 25 行的过程,目的是:在图表上创建一条线,并将其放置在开仓价格处。
接下来,让我们转到第 25 行的过程来理解一些内容。在这个阶段,代码仍然相当简单且易于理解。此过程接收将分配给 HLINE 对象的名称。但是我们在哪里创建这个对象呢?我们通过 C_Terminal 类来实现,具体来说是在第 27 行。现在请注意以下事项:在第 27 行的第四个参数中,我们使用了一个非常低的优先级,实际上是列表中的最低优先级。这是为什么?
你可能会想:“但我们现在创建的这条线难道不是持仓和订单指标的一部分吗?为什么这个对象被分配的优先级比其他对象低?这说不通啊。在我看来,优先级应该是我们为持仓和订单指标创建的对象所列出的优先级。”不,亲爱的读者,这样想,尤其是在这个具体情况下,是错误的,原因很简单。这条水平线,且必须明确指出,仅仅是这条线,必须具有尽可能低的优先级。原因很简单:它不会被移动,也不应接收事件,因为它代表的是交易服务器指示开仓的价格或点位。现在你明白为什么给它分配这个优先级了吗?
但你仍然不能放松。我们只是创建了这条线,并将其定位在图表上。这个定位操作发生在之前代码的第 28 行。仅此而已。如果用户在图表上删除、移动甚至选择这条线,他们将能够移动它。这完全违背了我们希望通过这个指标实现的原则。为了解决这个问题,我们需要实现更多的代码。但是,由于我们的目标是尽可能清晰地解释问题,我们将逐步进行,以便你能理解为什么这个对象(在这种情况下是价格线)不能被移动。
总之,当从图表中移除指标时,将执行第 51 行的检查。如果条件满足,第 52 行会删除所有名称以本代码中用于创建并在图表上显示它们的前缀开头的对象。我希望这部分内容已经讲清楚了,因为添加指示止损和止盈位置的水平线时,使用的是相同的原理。然而,还是存在一些差异,如下面的片段所示:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName) 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. } 30. //+------------------------------------------------------------------+ 31. void CreateLineStopAndTake(const string szObjName, const double price, const color cor) 32. { 33. if (price <= 0) return; 34. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders); 35. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 36. } 37. //+------------------------------------------------------------------+ 38. int OnInit() 39. { 40. ZeroMemory(glVariables); 41. Terminal = new C_Terminal(); 42. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 43. glVariables.id = (*Terminal).GetInfoTerminal().ID; 44. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 45. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice); 46. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01); 47. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02); 48. 49. return INIT_SUCCEEDED; 50. } 51. //+------------------------------------------------------------------+ 52. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 53. { 54. return rates_total; 55. } 56. //+------------------------------------------------------------------+ 57. void OnDeinit(const int reason) 58. { 59. delete Terminal; 60. if (glVariables.id > 0) 61. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 62. } 63. //+------------------------------------------------------------------+
仓位指标的源代码
请注意,这里添加的代码很少。但是,现在,如果你在图表上使用这个应用程序,且该图表上该交易品种只有一个未平仓持仓,你将能够看到开仓价、止盈价和止损价的位置。那么,让我们来看看为了实现这一可视化效果,代码中增加了哪些内容。
现在,在第 31 行,我们有一个新的过程。此过程将创建水平止盈线和止损线。请注意,它与创建开仓价格线的过程几乎相同。但我为什么要把它们分开呢?我这样做是为了让你明白,开仓价格线必须与其他线(在本例中为止盈线和止损线)有不同的优先级。还有一个细节:在第 33 行,我们检查价格值是否不等于零也不小于零,因为那样就没有任何意义了。在这种情况下,将不会创建水平线对象。
但是,即使在指标中增加了在图表上再画两条线的能力,当移除指标时,由它创建的所有线条也会被移除。因此,我们只需在代码中添加第 46 行和第 47 行,就能完成所有操作,至少是最基本的操作。因此,当指标显示在图表上且有仓位开仓时,您将能够看到以下图像。

但在进行更复杂的操作之前,让我们添加一些文字,以便我们知道图表上的每一条线代表什么,因为如果没有文字,我们可能会想要在 MetaTrader 5 对象窗口中手动删除它。下图可以看到这个窗口:

在这里,我们可以看到图表上的所有对象,无论其是否处于隐藏状态。然而,只要对象以任何方式出现在图表上,它就会出现在这个列表中。我们不希望用户仅仅因为不了解某个对象的用途就删除它。因此,我们将对代码进行小幅修改。这种修改可以在两个地方进行:这里,在指标中,或者在 C_Terminal 类中。然而,说实话,我不想在没有充分理由的情况下,通过引入可能的不稳定性来不断更改一个已经完全稳定的类。因此,由于我们需要更改新创建对象的一些属性的状态,让我们在这里本地进行更改。
继续改进代码
我们可以添加一项内容,即对象描述,以便用户了解给定对象是什么以及它的用途。这很容易做到。我们只需将上面的代码替换为下面的代码:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrRoyalBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName, const string szDescription = "\n") 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 30. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 31. } 32. //+------------------------------------------------------------------+ 33. void CreateLineStopAndTake(const string szObjName, const double price, const color cor, const string szDescription = "\n") 34. { 35. if (price <= 0) return; 36. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders); 37. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 38. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 39. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 40. } 41. //+------------------------------------------------------------------+ 42. int OnInit() 43. { 44. ZeroMemory(glVariables); 45. Terminal = new C_Terminal(); 46. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 47. glVariables.id = (*Terminal).GetInfoTerminal().ID; 48. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 49. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price."); 50. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 51. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 52. 53. return INIT_SUCCEEDED; 54. } 55. //+------------------------------------------------------------------+ 56. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 57. { 58. return rates_total; 59. } 60. //+------------------------------------------------------------------+ 61. void OnDeinit(const int reason) 62. { 63. delete Terminal; 64. if (glVariables.id > 0) 65. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 66. } 67. //+------------------------------------------------------------------+
仓位指标的源代码
再次强调,只需添加极少量的代码,即可获得下图所示的结果。

同样在对象列表中,现在出现了以下内容:

记住:如上图所示,要在图表上显示文本,必须在图表设置中启用以下选项:

该属性也可以通过 MQL5 代码启用。为此,您只需在程序中添加一行代码:
41. //+------------------------------------------------------------------+ 42. int OnInit() 43. { 44. ZeroMemory(glVariables); 45. Terminal = new C_Terminal(); 46. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 47. glVariables.id = (*Terminal).GetInfoTerminal().ID; 48. ChartSetInteger(glVariables.id, CHART_SHOW_OBJECT_DESCR, true); 49. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 50. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price."); 51. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 52. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 53. 54. return INIT_SUCCEEDED; 55. } 56. //+------------------------------------------------------------------+
主代码片段
请注意,添加的行是第 48 行。这启用了上图所示的相同选项。然而,由于这增加了一个元素,代码就需要额外处理这一项设置,以避免更改用户设置,而作为程序员的你可能并不希望更改这些设置,因此我不会在代码的最终版本中使用它。但如果你想这样做,也没有问题。同样,您也可以更改文本的显示颜色。这在下图所示的下一个项目中完成:

我们还可以使用 MQL5 代码更改此属性。但同样,我不想干扰或更改用户已经习惯的设置。我想尽可能少地付出努力来实现预期目标。但是,亲爱的读者,回到主代码,你需要注意一点:在 CreateLinePriceOpen 过程和 CreateLineStopAndTake 过程的声明中,都为 szDescription 值分配了一个特定值。如果未为对象指定描述,它将收到一个值为“\n”的描述。MQL5 文档中对此有明确说明,内容如下:
“工具提示”文本。如果该属性未定义,则将显示由终端自动生成的工具提示。 通过为工具提示赋值“\n”(换行符),可以禁用该工具提示。
因此,szDescription 收到的这个默认值实际上不会影响第 29 行和第 38 行 OBJPROP_TEXT 属性中放置的信息。但是,这将阻止 MetaTrader 5 终端为第 30 行和第 39 行声明的 OBJPROP_TOOLTIP 属性生成值。
“我们如何访问 OBJPROP_TOOLTIP 属性?它和 OBJPROP_TEXT 一样吗?”不,它们不一样。OBJPROP_TEXT 属性正是当对象允许添加文本时我们所看到的属性。允许添加文本的对象之一是水平线。然而,并非所有对象都具有此属性。
但是 OBJPROP_TOOLTIP 属性将存在于所有图表对象中,并且这两个属性的描述文本不一定需要匹配。它们可能会有所不同,因为 OBJPROP_TOOLTIP 仅在光标悬停在对象上一段时间后才会显示其文本。此时,会出现一个小窗口,显示 OBJPROP_TOOLTIP 属性的内容。
如果您之前没有注意到这一点,下面的动画展示了这个小窗口的用途,以及如何查看 OBJPROP_TOOLTIP 属性的内容。

请注意,此功能在内容、交互模式和目的方面均作为辅助工具。它通常简单、直接且适用于多种情况。所有对象都可以通过此属性接收一些信息。如果您想帮助用户了解您的应用程序如何工作,尤其是当它包含许多通常以图标或图像形式出现的按钮时,请使用此属性为用户提供必要的帮助。他们会非常感激,因为这将改善他们使用您应用程序的体验。请牢记这一点。
好了,我们快讲完这篇文章了,但在结束之前,不如将创建线条的步骤整合到一个过程中?这样将来我们使用其他方法移动这些线条时,会节省时间和精力。因此,下一篇文章中将使用的最终代码如下所示:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrRoyalBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLineInfos(const string szObjName, const double price, const color cor, const string szDescription = "\n") 26. { 27. if (price <= 0) return; 28. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == user00 ? ePriorityNull : ePriorityOrders)) 29. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 30. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 31. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 32. ObjectSetInteger(glVariables.id, szObjName, OBJPROP_SELECTABLE, cor != user00); 33. } 34. //+------------------------------------------------------------------+ 35. int OnInit() 36. { 37. ZeroMemory(glVariables); 38. Terminal = new C_Terminal(); 39. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 40. glVariables.id = (*Terminal).GetInfoTerminal().ID; 41. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 42. CreateLineInfos(glVariables.szPrefixName + def_SufixLinePrice, PositionGetDouble(POSITION_PRICE_OPEN), user00, "Position opening price."); 43. CreateLineInfos(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 44. CreateLineInfos(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 45. 46. return INIT_SUCCEEDED; 47. } 48. //+------------------------------------------------------------------+ 49. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 50. { 51. return rates_total; 52. } 53. //+------------------------------------------------------------------+ 54. void OnDeinit(const int reason) 55. { 56. delete Terminal; 57. if (glVariables.id > 0) 58. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 59. } 60. //+------------------------------------------------------------------+
仓位指标的源代码
这是我们将在下一篇文章中修改的代码。由于没有实质性的变化,我认为,只需阅读本文并稍作思考,你就能轻松理解其工作原理。唯一需要注意的是,我必须提醒你:不要对所有三条线使用相同的颜色。你甚至可以对止损线和止盈线使用相同的颜色,但不要对开仓价线使用相同的颜色。这是因为在第 28 行和第 32 行,我们使用颜色来区分不同的内容。但这并非什么大问题。它可能只会让你感到困惑,妨碍你理解为什么事情没有像你预期的那样发展。
最后的思考
在本文中,我尝试尽可能简单明了地展示如何使用指标来监控交易服务器上的未平仓位。我们逐步采用这种方式,是为了说明并非所有内容都必须纳入 EA 中。你们中的许多人可能因为这样或那样的原因已经习惯了这样做。但事实上,这种做法并不合理,因为随着实现的发展,我们会越来越清楚地看到,可以创建或实现不同类型的指标来跟踪服务器上注册的仓位或订单,或者在我们的情况下,跟踪回放/模拟系统中的仓位或订单,并与之交互。
然而,尽管这里的主要想法是在回放/模拟系统中使用该指标,但我想提醒你,我们尚未将其应用于模拟交易品种或运行回放的情况。我正在真实交易品种和真实仓位上使用和测试该指标。因此,仅在回放/模拟系统中使用该指标是没有意义的。在使用该指标时,我想提醒你注意以下事项:它只会显示一个仓位。对于锁仓账户而言,仅持有两个或更多未平仓仓位并期望该指标能发挥作用是不够的,因为它目前尚不具备这样的能力。
很快我们就会看到使用这个指标的正确方法,该方法允许可视化多个未平仓位。如果是单边账户,就不会出现这个问题,因为它们不允许您同时买卖同一交易品种。但是,锁仓账户允许这样做。在这种情况下,我们需要对这个指标的使用方式做一个小小的调整。但实施成本非常低,说实话,值得考虑将这项工作从 EA 中剥离出来。这是因为我还没有展示出这个指标的所有功能。因此,在下一篇文章中,我们将对这个指标进行一些改进,因为它在这里有一个小缺点。如果你想知道问题出在哪里,请阅读本系列的下一篇文章。
| 文件 | 描述 |
|---|---|
| Experts\Expert Advisor.mq5 | 演示 Chart Trade 与 EA 之间的交互(交互需要 Mouse Study)。 |
| Indicators\Chart Trade.mq5 | 创建一个窗口,用于在发送订单之前配置订单(需要 Mouse Study 才能进行交互)。 |
| Indicators\Market Replay.mq5 | 创建用于与回放/模拟服务交互的控件(交互需要 Mouse Study)。 |
| Indicators\Mouse Study.mq5 | 提供图形控件与用户之间的交互(回放/模拟系统和真实市场都需要)。 |
| Indicators\Order Indicator.mq5 | 负责显示市价单,并支持与其交互并进行管理。 |
| Indicators\Position View.mq5 | 负责展示市场仓位,并允许与之互动和管理。 |
| Services\Market Replay.mq5 | 创建并维护市场回放/模拟服务(整个系统的主文件)。 |
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/13137
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
交易中的神经网络:一种跨领域时间序列预测框架(TimeFound)
新手在交易中的10个基本错误