
开发回放系统(第 43 部分):Chart Trade 项目(II)
概述
在上一篇文章开发回放系统(第 42 部分):Chart Trader 项目 (I) 中,我展示了如何安排鼠标指标与其他指标之间的互动。
我们开始编写代码,以确保 Chart Trade 指标与鼠标操作完美协调。不过,它与文章中描述的最初版本的 Chart Trade 指标不同:
- 从零开始开发交易 EA 交易(第 30 部分):以 CHART TRADE 作为指标?!
- 一张图表上的多个指标(第 06 部分):将 MetaTrader 5 转化为 RAD 系统(二)
- 一张图表上的多个指标(第 05 部分):将 MetaTrader 5 转化为 RAD 系统(一)
在这里,我们将做一些更高级因而也不同的事情。但无论如何,结果都与视频 01 中的一样。我建议你在开始阅读文章之前先观看这段视频,这样你就能知道我们到底要做什么。这不是阅读代码就能理解的。俗话说,一张图片胜过千言万语,因此请观看视频,以便更好地理解文章中的解释。
视频 01 - 演示视频
视频显示数据显示在 Chart Trade 窗口中。你可能已经注意到,我们所做的一切与前一篇文章中显示的一模一样,但信息更新时并没有实际使用对象。你可能会想:这怎么可能?
"这家伙一定在耍什么花招。我从没见过有人这样做。这完全不合理......"其他人可能认为我是某种巫师或魔术师,拥有超乎想象的力量。不,不是这样的。我只是简单地使用 MetaTrader 5 平台和 MQL5 语言,达到了许多人不努力理解的水平。他们总是继续说同样的话,做同样的事,而不去探索 MQL5 语言或 MetaTrader 5 平台的真正潜力和功能。
我希望大家都看了视频。因为在这篇文章中,我将告诉你如何做一件事,这件事将从根本上改变你对什么是可能、什么是不可能的看法。重要提示:我只解释视频中的内容,视频中未展示的内容将在后面讨论。
更新指标代码
需要做出的改变不会太大。但我们还是要循序渐进,否则大家都会不清楚发生了什么。
让我们先看看对指标代码所做的修改,修改内容如下代码所示。
Chart Trade 指标源代码
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Base version for Chart Trade (DEMO version)" 04. #property version "1.43" 05. #property icon "/Images/Market Replay/Icons/Indicators.ico" 06. #property link "https://www.mql5.com/es/articles/11664" 07. #property indicator_chart_window 08. #property indicator_plots 0 09. //+------------------------------------------------------------------+ 10. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh> 11. //+------------------------------------------------------------------+ 12. C_ChartFloatingRAD *chart = NULL; 13. //+------------------------------------------------------------------+ 14. input int user01 = 1; //Leverage 15. input double user02 = 100.1; //Finance Take 16. input double user03 = 75.4; //Finance Stop 17. //+------------------------------------------------------------------+ 18. int OnInit() 19. { 20. chart = new C_ChartFloatingRAD("Indicator Chart Trade", new C_Mouse("Indicator Mouse Study"), user01, user02, user03); 21. if (_LastError != ERR_SUCCESS) 22. { 23. Print("Error number:", _LastError); 24. return INIT_FAILED; 25. } 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. (*chart).DispatchMessage(id, lparam, dparam, sparam); 38. 39. ChartRedraw(); 40. } 41. //+------------------------------------------------------------------+ 42. void OnDeinit(const int reason) 43. { 44. delete chart; 45. 46. ChartRedraw(); 47. } 48. //+------------------------------------------------------------------+
如果将其与文章 开发回放系统(第 42 部分):Chart Trade 项目 (I) 中给出的指标源代码进行比较,可以看到它发生了一些变化。我们添加了一种方法,允许用户指定最初设置的指标的值。虽然我们在第 14 行和第 16 行之间有这种形式的互动,但这些点并不是完全必要的,尽管它们对测试很有意义。请记住,Chart Trade 是一个与用户互动的系统。
您只需在指标中输入默认值,而无需使用第 14 行至第 16 行。这可以在第 20 行完成,我们可以不使用从用户交互中获取的值,而是直接将这些值放入调用中。因此,我们的 Chart Trade 具有默认值,用户可以在指标出现在图表上后对其进行更改。
事实上,本文将不涉及这一点。原因是我们将采用稍有不同的过程来完成这项工作。但正如您在演示视频中看到的那样,用户报告的数值会传输到 Chart Trade 中,并清晰地显示出来。如前所述,这种交互作用的存在对测试目的很有意义。
我们已经看到,指标能够建立通信。现在,让我们来看看如何如视频所示,在不使用对象的情况下在指标中反映变化。从视频中可以看到,图上唯一的对象是OBJ_CHART。您知道我是怎么做到的吗?如何仅使用OBJ_CHART 更改数据和数值?
如果您不知道这是如何实现的,那您就必须研究一下 MetaTrader 5 平台和 MQL5 语言的实际工作原理。为了更清楚地解释,让我们开始一个新主题。
对象和目标
大多数想要或梦想学习编程的人实际上并不知道自己在做什么。他们的活动包括试图以某种方式创造事物。然而,编程并不是为用户量身定制合适的解决方案,这样做会带来更多的问题,而不是解决方案。
也许这一点并不完全清楚。好吧,我将尝试传达我多年来在各种语言、平台和系统中的编程经验。
每当我们开始使用一个系统时,无论它是什么,我们应该做的第一件事就是评估它的能力,了解系统为我们提供了什么。这是一般性的,也就是说,我们必须努力理解为什么一切都是以这种方式而不是其他方式创造的。了解了最基本功能的工作原理后,您就可以继续深入研究。
深入研究不是寻找改进这些功能的方法,也不是简单地使用它们进行编程。深度在于试图利用其他人尚未探索的资源。许多人限制自己只使用其他人通常使用的东西,这并不是一件坏事。但是,如果只是肤浅地了解系统的功能,又如何提出改进建议呢?这种思维方式毫无意义。这就像一个孩子想通过观察一条正在运行的装配线来提出一种新的生产方法。这种事情是没有前途的,注定会带来更多的问题,而不是解决办法。
也许我说得还是不够清楚。当看到 MetaTrader 5 时,有些人认为他们对该平台有足够的了解,可以说用它做什么是可能的或不可能的。然后他们转向 MQL5,试图解决他们的一些问题。这是一个错误。与其他语言一样,您不应将 MQL5 视为满足您需求或愿望的魔法。MQL5 不是一种魔法语言。该语言扩展了 MetaTrader 5 的功能。但是,它无法使平台以不同于预期的方式运行。
正确的做法是深入了解 MetaTrader 5 的工作原理,然后寻找适合自己的市场观察和分析方法。在探索 MetaTrader 5 的过程中,您将了解如何将其转化为更适合您对市场看法的工具。为此,您必须使用 MQL5,它将打开 MetaTrader 5 的大门,使您或其他交易者的生活更加轻松。
有鉴于此,我们可以考虑一个问题。许多用户由于不了解 MetaTrader 5 的基本操作细节而低估了它。其中一个细节是模板的概念,而且它们不仅限于 MetaTrader 5 中。它们让我们能够以简单实用的方式定制、调整和组织某些事物。从注释到简化在特定时刻以特定方式查看市场的过程。
模板可以包含各种各样的内容,我之前已经介绍过如何探索其中的一些内容。在一张图表上的多个指标(第 03 部分):开发自定义定义 一文中,我展示了如何将不同的指标放在一起,如图 01 所示。
图 01 - 在一个子窗口中使用多个指标
只有使用模板才能展示上述概念。无论您是多么优秀的程序员,也无论您对 MQL5 的知识有多么渊博,如果不了解 MetaTrader 5 的功能,就永远无法实现图 01 所示的结果。这是因为,并不是所有事情都需要编写代码。编程只会帮助创建一个没有其他选择的解决方案。然而,当你需要新东西时,它不应该是第一个尝试,而应该是达到预期效果的工具。
我为什么要这么说?原因正是本文开头的视频 01 所显示的。无论您的编程能力有多强,也无论您对 MQL5 的工作原理有多了解,如果不了解 MetaTrader 5 的功能,您都无法完成视频中的操作。
这个故事的重要意义在于,当我发表文章一张图表上的多个指标(第 06 部分):将 MetaTrader 5 转化为 RAD 系统 (II) 时 ,当时我并没有使用 MetaTrader 5 的某些概念。我仍然坚持一些想法和概念,但最终发现这些想法和概念并不适合我。我不是说它们错了,只是不太合适,还有更好的解决办法。这正是由于该文章以及上一篇文章 一张图表上的多个指标(第 05 部分):将 MetaTrader 5 转化为 RAD 系统 (I) 所展示的。
在这两篇文章中,我们都打开了模板并尝试对其进行操作。但所有这些工作都只是表面文章,因为你看到的大部分内容都是对相同模板的重新编程。这样做是为了模拟 RAD 系统,而 RAD 系统可以通过 MQL5 编程来实现。
虽然这样做成功了,但您会注意到,事实上,在 MetaTrader 5 中创建的模板后来在指标被放置到图表上时被指标本身重新创建了。因此,指标重新创建了模板中存在的所有对象。通过这种方式,MetaTrader 5 可以访问其中的对象,从而配置和更改每个对象中的值。
然而,随着时间的推移,这个想法显然可以改进。因此,我找到了一个更合适的 Chart Trade 模型,它使用的对象最少。除了本文所介绍的内容,我们还需要做更多的事情。然而,视频 01 显示,数值发生了变化。同时,屏幕上只有一个对象。
这正是编程让我们能够做到的。没有它,我们就只能使用 MetaTrader 5 中的功能。通过编程,我们可以扩展平台的功能。这可以通过使用 MetaTrader 5 中已有的功能来实现。我们创建一个模板,其中包含我们所需的内容,并将其放入 OBJ_CHART。但是,如果没有适当的编程,就不可能更改模板内对象的值。通过正确使用编程,我们解决了这一问题,从而扩展了 MetaTrader 5 的功能。
为了更清楚地说明这一点,请打开 Chart Trade 中使用的模板文件。全文如下。
Chart Trade 文件(模板):
001. <chart> 002. fore=0 003. grid=0 004. volume=0 005. ticker=0 006. ohlc=0 007. one_click=0 008. one_click_btn=0 009. bidline=0 010. askline=0 011. lastline=0 012. descriptions=0 013. tradelines=0 014. tradehistory=0 015. background_color=16777215 016. foreground_color=0 017. barup_color=16777215 018. bardown_color=16777215 019. bullcandle_color=16777215 020. bearcandle_color=16777215 021. chartline_color=16777215 022. volumes_color=16777215 023. grid_color=16777215 024. bidline_color=16777215 025. askline_color=16777215 026. lastline_color=16777215 027. stops_color=16777215 028. windows_total=1 029. 030. <window> 031. height=100.000000 032. objects=18 033. 034. <indicator> 035. name=Main 036. path= 037. apply=1 038. show_data=1 039. scale_inherit=0 040. scale_line=0 041. scale_line_percent=50 042. scale_line_value=0.000000 043. scale_fix_min=0 044. scale_fix_min_val=0.000000 045. scale_fix_max=0 046. scale_fix_max_val=0.000000 047. expertmode=0 048. fixed_height=-1 049. </indicator> 050. 051. <object> 052. type=110 053. name=MSG_NULL_000 054. color=0 055. pos_x=0 056. pos_y=0 057. size_x=170 058. size_y=210 059. bgcolor=16777215 060. refpoint=0 061. border_type=1 062. </object> 063. 064. <object> 065. type=110 066. name=MSG_NULL_001 067. color=0 068. pos_x=5 069. pos_y=30 070. size_x=152 071. size_y=26 072. bgcolor=12632256 073. refpoint=0 074. border_type=1 075. </object> 076. 077. <object> 078. type=110 079. name=MSG_NULL_002 080. color=0 081. pos_x=5 082. pos_y=58 083. size_x=152 084. size_y=26 085. bgcolor=15130800 086. refpoint=0 087. border_type=1 088. </object> 089. 090. <object> 091. type=110 092. name=MSG_NULL_003 093. color=0 094. pos_x=5 095. pos_y=86 096. size_x=152 097. size_y=26 098. bgcolor=10025880 099. refpoint=0 100. border_type=1 101. </object> 102. 103. <object> 104. type=110 105. name=MSG_NULL_004 106. color=0 107. pos_x=5 108. pos_y=114 109. size_x=152 110. size_y=26 111. bgcolor=8036607 112. refpoint=0 113. border_type=1 114. </object> 115. 116. <object> 117. type=102 118. name=MSG_NULL_007 119. descr=Lever ( x ) 120. color=0 121. style=1 122. angle=0 123. pos_x=10 124. pos_y=63 125. fontsz=16 126. fontnm=System 127. anchorpos=0 128. refpoint=0 129. </object> 130. 131. <object> 132. type=102 133. name=MSG_NULL_008 134. descr=Take ( $ ) 135. color=0 136. style=1 137. angle=0 138. pos_x=10 139. pos_y=91 140. fontsz=16 141. fontnm=System 142. anchorpos=0 143. refpoint=0 144. </object> 145. 146. <object> 147. type=102 148. name=MSG_NULL_009 149. descr=Stop ( $ ) 150. color=0 151. style=1 152. angle=0 153. pos_x=10 154. pos_y=119 155. fontsz=16 156. fontnm=System 157. anchorpos=0 158. refpoint=0 159. </object> 160. 161. <object> 162. type=107 163. name=MSG_TITLE_IDE 164. descr=Chart Trade 165. color=16777215 166. style=1 167. pos_x=0 168. pos_y=0 169. size_x=164 170. size_y=28 171. bgcolor=16748574 172. fontsz=16 173. fontnm=System 174. refpoint=0 175. readonly=1 176. align=0 177. </object> 178. 179. <object> 180. type=106 181. name=MSG_BUY_MARKET 182. size_x=70 183. size_y=25 184. offset_x=0 185. offset_y=0 186. pos_x=5 187. pos_y=145 188. bmpfile_on=\Images\Market Replay\Chart Trade\BUY.bmp 189. bmpfile_off=\Images\Market Replay\Chart Trade\BUY.bmp 190. state=0 191. refpoint=0 192. anchorpos=0 193. </object> 194. 195. <object> 196. type=106 197. name=MSG_SELL_MARKET 198. size_x=70 199. size_y=25 200. offset_x=0 201. offset_y=0 202. pos_x=85 203. pos_y=145 204. bmpfile_on=\Images\Market Replay\Chart Trade\SELL.bmp 205. bmpfile_off=\Images\Market Replay\Chart Trade\SELL.bmp 206. state=0 207. refpoint=0 208. anchorpos=0 209. </object> 210. 211. <object> 212. type=103 213. name=MSG_CLOSE_POSITION 214. descr=Close Position 215. color=0 216. style=1 217. pos_x=5 218. pos_y=173 219. fontsz=16 220. fontnm=System 221. state=0 222. size_x=152 223. size_y=26 224. bgcolor=1993170 225. frcolor=-1 226. refpoint=0 227. </object> 228. 229. <object> 230. type=107 231. name=MSG_NAME_SYMBOL 232. descr=? 233. color=0 234. style=1 235. pos_x=7 236. pos_y=34 237. size_x=116 238. size_y=20 239. bgcolor=12632256 240. fontsz=14 241. fontnm=Tahoma 242. refpoint=0 243. readonly=1 244. align=1 245. </object> 246. 247. <object> 248. type=107 249. name=MSG_LEVERAGE_VALUE 250. descr=? 251. color=0 252. style=1 253. pos_x=80 254. pos_y=61 255. size_x=70 256. size_y=18 257. bgcolor=15130800 258. fontsz=12 259. fontnm=Tahoma 260. refpoint=0 261. readonly=0 262. align=1 263. </object> 264. 265. <object> 266. type=107 267. name=MSG_TAKE_VALUE 268. descr=? 269. color=0 270. style=1 271. pos_x=80 272. pos_y=91 273. size_x=70 274. size_y=18 275. bgcolor=10025880 276. fontsz=12 277. fontnm=Tahoma 278. refpoint=0 279. readonly=0 280. align=1 281. </object> 282. 283. <object> 284. type=107 285. name=MSG_STOP_VALUE 286. descr=? 287. color=0 288. style=1 289. pos_x=80 290. pos_y=119 291. size_x=70 292. size_y=18 293. bgcolor=8036607 294. fontsz=12 295. fontnm=Tahoma 296. refpoint=0 297. readonly=0 298. align=1 299. </object> 300. 301. <object> 302. type=106 303. name=MSG_DAY_TRADE 304. size_x=32 305. size_y=22 306. offset_x=0 307. offset_y=0 308. pos_x=123 309. pos_y=33 310. bmpfile_on=\Images\Market Replay\Chart Trade\DT.bmp 311. bmpfile_off=\Images\Market Replay\Chart Trade\SW.bmp 312. state=0 313. refpoint=0 314. anchorpos=0 315. </object> 316. 317. <object> 318. type=106 319. name=MSG_MAX_MIN 320. size_x=20 321. size_y=20 322. offset_x=0 323. offset_y=0 324. pos_x=140 325. pos_y=3 326. bmpfile_on=\Images\Market Replay\Chart Trade\Maximize.bmp 327. bmpfile_off=\Images\Market Replay\Chart Trade\Minimize.bmp 328. state=1 329. refpoint=0 330. anchorpos=0 331. </object> 332. 333. </window> 334. </chart>
我知道它的内容似乎完全没有必要。请注意,附件中不会包含文件,整个系统将以这样一种方式发布,即可以通过将代码改写为相应文件的方式获取文件。我这样做是为了确保您阅读并理解文章。我不希望你们在不知道自己在做什么的情况下下载附件文件并使用系统。虽然这看起来会使文章明显变长,但实际上并没有,只是使解释更加详细而已。
现在让我们仔细看看 Chart Trade 模板文件。注意包含以下内容的行:
descr=?
它出现在模板文件的多个地方,而且是有意为之。为什么呢?虽然 descr 出现在不同的地方,但如上所述,这些地方主要指的是 107 型对象。什么是 107 型对象?
有关这些对象类型的描述首次出现在一张图表上的多个指标(第 05 部分):将 MetaTrader 5 转化为 RAD (I) 系统 一文中。为方便起见,我将提供本文中的表格如下:
TYPE 变量的值 | 引用的对象 |
---|---|
102 | OBJ_LABEL |
103 | OBJ_BUTTON |
106 | OBJ_BITMAP_LABEL |
107 | OBJ_EDIT |
110 | OBJ_RECTANGLE_LABEL |
因此,对象 107 就是 OBJ_EDIT。它们是用户可以输入一些信息的对象。
但要直接访问或向模板中的对象输入值是不可能的,这是事实。如何解决这个问题,使模板中的对象 107 可以从指标接收数值呢?要回答这个问题,您需要查看负责使用模板的类的代码。
更新 C_ChartFloatingRAD 类
要使 Chart Trade 显示用户输入的值,需要在图表上载入指标后立即执行一些基本操作。这些操作旨在解决一些不使用编程就无法解决的问题。因此,充分了解 MetaTrader 5 的工作原理非常重要。如果不了解平台运行原理,就无法创建必要的代码来克服这些没有编程就无法解决的障碍。
下面是 C_ChartFloatingRAD 类的完整代码。该代码目前的形式可使指标如视频 01 所示运行。你能理解这是怎么发生的吗?
C_ChartFloatingRAD 类源代码:
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "../Auxiliar/C_Terminal.mqh" 005. #include "../Auxiliar/C_Mouse.mqh" 006. //+------------------------------------------------------------------+ 007. class C_ChartFloatingRAD : private C_Terminal 008. { 009. private : 010. struct st00 011. { 012. int x, y, cx, cy; 013. string szObj_Chart, 014. szFileNameTemplate; 015. long WinHandle; 016. double FinanceTake, 017. FinanceStop; 018. int Leverage; 019. }m_Info; 020. //+------------------------------------------------------------------+ 021. C_Mouse *m_Mouse; 022. //+------------------------------------------------------------------+ 023. void CreateWindowRAD(int x, int y, int w, int h) 024. { 025. m_Info.szObj_Chart = (string)ObjectsTotal(GetInfoTerminal().ID); 026. ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0); 027. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x = x); 028. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y = y); 029. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w); 030. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h); 031. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false); 032. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false); 033. m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID); 034. m_Info.cx = w; 035. m_Info.cy = 26; 036. }; 037. //+------------------------------------------------------------------+ 038. void Level_1(int &fpIn, int &fpOut, const string szFind, const string szWhat, const string szValue) 039. { 040. string sz0 = ""; 041. int i0 = 0; 042. string res[]; 043. 044. while ((!FileIsEnding(fpIn)) && (sz0 != "</object>")) 045. { 046. sz0 = FileReadString(fpIn); 047. if (StringSplit(sz0, '=', res) > 1) 048. { 049. i0 = (res[1] == szFind ? 1 : i0); 050. if ((i0 == 1) && (res[0] == szWhat)) 051. { 052. FileWriteString(fpOut, szWhat + "=" + szValue + "\r\n"); 053. return; 054. } 055. } 056. FileWriteString(fpOut, sz0 + "\r\n"); 057. }; 058. } 059. //+------------------------------------------------------------------+ 060. void SwapValueInTemplate(const string szFind, const string szWhat, const string szValue) 061. { 062. int fpIn, fpOut; 063. string sz0; 064. 065. if (_LastError != ERR_SUCCESS) return; 066. if ((fpIn = FileOpen(m_Info.szFileNameTemplate, FILE_READ | FILE_TXT)) == INVALID_HANDLE) 067. { 068. SetUserError(C_Terminal::ERR_FileAcess); 069. return; 070. } 071. if ((fpOut = FileOpen(m_Info.szFileNameTemplate + "_T", FILE_WRITE | FILE_TXT)) == INVALID_HANDLE) 072. { 073. FileClose(fpIn); 074. SetUserError(C_Terminal::ERR_FileAcess); 075. return; 076. } 077. while (!FileIsEnding(fpIn)) 078. { 079. sz0 = FileReadString(fpIn); 080. FileWriteString(fpOut, sz0 + "\r\n"); 081. if (sz0 == "<object>") Level_1(fpIn, fpOut, szFind, szWhat, szValue); 082. }; 083. FileClose(fpIn); 084. FileClose(fpOut); 085. if (!FileMove(m_Info.szFileNameTemplate + "_T", 0, m_Info.szFileNameTemplate, FILE_REWRITE)) 086. { 087. FileDelete(m_Info.szFileNameTemplate + "_T"); 088. SetUserError(C_Terminal::ERR_FileAcess); 089. } 090. } 091. //+------------------------------------------------------------------+ 092. inline void UpdateChartTemplate(void) 093. { 094. ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate); 095. ChartRedraw(m_Info.WinHandle); 096. } 097. //+------------------------------------------------------------------+ 098. inline double PointsToFinance(const double Points) 099. { 100. return Points * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade; 101. }; 102. //+------------------------------------------------------------------+ 103. public : 104. //+------------------------------------------------------------------+ 105. C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const int Leverage, const double FinanceTake, const double FinanceStop) 106. :C_Terminal() 107. { 108. m_Mouse = MousePtr; 109. m_Info.Leverage = (Leverage < 0 ? 1 : Leverage); 110. m_Info.FinanceTake = PointsToFinance(FinanceToPoints(MathAbs(FinanceTake), m_Info.Leverage)); 111. m_Info.FinanceStop = PointsToFinance(FinanceToPoints(MathAbs(FinanceStop), m_Info.Leverage)); 112. if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown); 113. m_Info.szFileNameTemplate = StringFormat("Chart Trade/%u.tpl", GetInfoTerminal().ID); 114. if (!FileCopy("Chart Trade/IDE_RAD.tpl", 0, m_Info.szFileNameTemplate, FILE_REWRITE)) SetUserError(C_Terminal::ERR_FileAcess); 115. if (_LastError != ERR_SUCCESS) return; 116. SwapValueInTemplate("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol); 117. SwapValueInTemplate("MSG_LEVERAGE_VALUE", "descr", (string)m_Info.Leverage); 118. SwapValueInTemplate("MSG_TAKE_VALUE", "descr", (string)m_Info.FinanceTake); 119. SwapValueInTemplate("MSG_STOP_VALUE", "descr", (string)m_Info.FinanceStop); 120. if (_LastError != ERR_SUCCESS) return; 121. CreateWindowRAD(0, 0, 170, 210); 122. UpdateChartTemplate(); 123. } 124. //+------------------------------------------------------------------+ 125. ~C_ChartFloatingRAD() 126. { 127. ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Chart); 128. FileDelete(m_Info.szFileNameTemplate); 129. 130. delete m_Mouse; 131. } 132. //+------------------------------------------------------------------+ 133. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 134. { 135. static int sx = -1, sy = -1; 136. int x, y, mx, my; 137. 138. switch (id) 139. { 140. case CHARTEVENT_MOUSE_MOVE: 141. if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) 142. { 143. x = (int)lparam; 144. y = (int)dparam; 145. if ((x > m_Info.x) && (x < (m_Info.x + m_Info.cx)) && (y > m_Info.y) && (y < (m_Info.y + m_Info.cy))) 146. { 147. if (sx < 0) 148. { 149. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 150. sx = x - m_Info.x; 151. sy = y - m_Info.y; 152. } 153. if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x = mx); 154. if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y = my); 155. } 156. }else if (sx > 0) 157. { 158. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 159. sx = sy = -1; 160. } 161. break; 162. } 163. } 164. //+------------------------------------------------------------------+ 165. }; 166. //+------------------------------------------------------------------+
请注意,自前一篇文章以来,C_ChartFloatingRAD 文件几乎没有任何重要的新增内容。这是有意为之。如果我展示该文件的最终版本,您将无法理解 MetaTrader 5 如何以及为何能修改模板中的值。这样做的目的是为了在使用 CTRL+B 打开对象列表窗口时,只在列表中看到 OBJ_CHART,但数值和窗口仍会发生变化。
现在,让我们试着了解 MetaTrader 5 如何以及为何能通过模板通知我们正在发生的事情。请记住这一点:我们不会停止使用模板,它将应用于 OBJ_CHART 对象。当执行第 94 行,第 95 行告诉 MetaTrader 5 更新 OBJ_CHART 时,就会发生这种情况。如果不考虑这一事实,你可能会花很长时间去寻找代码中不存在的东西。例如,对于实际存在于模板文件中的内容。
在第 38 行和第 123 行之间可以看到添加的代码。它的结构使下一篇文章中将要执行的进一步步骤不需要很多改动,这是非常好的。因此,请注意本文的解释,你就会很容易理解下一篇文章了。你甚至可以从编程的角度预测下一篇文章的内容。
让我们从类的构造函数开始,因为这将使其他部分更容易理解。该构造函数始于第 105 行,但真正的变化是从 109 号线开始的。在这一行中,我们保证杠杆值始终大于或等于 1。为此,我们使用了三元运算符。因此,这里没有什么特别复杂或令人困惑的地方。
现在让我们转到第 110 行。这一行和第 111 行一样,对图表交易窗口中显示的数值进行调整。这样做的原因是,用户输入的值可能对运行 Chart Trade 指标的资产无效。例如,就美元期货而言,每份合约的变动总是每 5 美元发生一次。在这种情况下,指定止盈或止损值不是 5 的倍数是没有意义的,例如止盈=76.00 美元,因为这个值永远不会达到。
因此,110 行和 111 行进行了两次调用。前者将资产价值转换为点数,后者则相反,将点数价值转换为资产价值。您可能会认为,这样做会将数值恢复到原来的值。这是事实,但这里将使用数学方法来转换和调整数值。第一次调用将使用 C_Terminal 类中的函数。在第二行中,我们执行第 98 行中的函数。
该函数在 98 行处仅使用一行。第 100 行实际上是进行计算,将给定的点数转换成资产价值。
这是最简单的部分。现在,让我们来看看当前系统中最困难的部分。
当构造函数运行到第 113 行时,我们会发现一些非常有趣的现象。第 113 行将创建临时文件的名称。记住,该文件将是临时文件。它将出现在MQL5\Files 中定义的区域,以及专门为此行 113 创建的其他信息。
在第 113 行创建文件名后,我们就可以继续看第 114 行。在这一行中,我们将以新名称完全复制模板文件的内容(在上一个主题中可以看到)。如果我们成功了,我们将继续前进。但如果复制失败,我们会向调用者报告。
真正的神奇之处发生在第 116 行和第 119 行之间。在这里,我们让 Chart Trade 指标接收 MetaTrader 5 和/或用户提供的值。这些行将调用类代码的第 60 行。从现在起,我们将继续处理这部分代码,因为构造函数的其余部分已在前一篇文章中作了说明。不过,在我们最终进入第 60 行之前,请允许我提请大家注意代码中添加的另一行:第 128 行。它会删除构造函数中创建的文件。这就是为什么我强调创建的文件是临时文件。
让我们继续看第 60 行。我需要在这里重复一下(我还在解释基础知识,请耐心等待)。第一步是打开 "原始" 文件(注意单词中的引号),这在第 66 行完成。如果我们成功了,我们将继续。如果失败,我们将返回到调用者。
在第 71 行,我们将从一个临时文件创建一个临时文件(听起来很奇怪,但没有别的办法)。如果失败,我们将关闭打开的文件并返回到调用者。在第 77 行之前,我们所做的只是打开一个文件并创建另一个文件。如果执行到第 77 行,我们将进入一个循环,复制整个文件。
等等,复制整个文件?再来一次?是的,但这次我们要测试第 81 行的一个条件。此条件将检查在复制过程中是否发现模板文件中的对象。当发现的时候,我们将转到第 38 行,处理找到的对象。无论如何,如果正在分析的模式是前一个主题中的模式,我们将跳转到第 38 行。因此,即使您想使用自己的模板,只要正确定义对象的名称,也是可以的。
调用第 38 行的函数后,我们将继续执行,直到执行到第 44 行。在这里,我们将继续按照之前的方法复制文件。但是,当关闭对象的行出现在执行第 44 行时,我们将返回到调用对象,即第 81 行。让我们停下来,看一看第 38 行出现的函数。
在复制过程中,我们将读取第 46 行的源文件。之后,我们将扩展这一行的内容。如果执行分解的第 47 行报告我们有两条指令可用,就会生成一个新的执行线程。否则,将写入读取行,这发生在第 56 行。
现在请注意以下几点:在第 47 行的分解过程中,i0 变量的初始值为 0。请特别注意这一点。一旦找到对象名称,i0 变量将被设置为 1。这就是危险所在。如果手动编辑模板文件,必须确保对象名称出现在任何参数之前。唯一可以出现在名称之前的参数是对象的类型。名称之前不得有其他参数。如果出现这种情况,整个过程就会失败。
在第 50 行,我们将检查 i0 是否显示已找到所需的对象。在这种情况下,该变量的值为 1。但我们还有第二个条件,它与我们正在寻找的参数有关。在这种情况下,我们将始终搜索 descr.如果出现这两个条件,我们就会在复制过程中写入所需的值。这种修改发生在第 52 行。之后,我们返回调用者,即第 81 行。
让我们看看这一过程是如何发生的。我们复制整个文件,只有特定的一行除外。修改这一行后,模板中将出现新的数据或呈现出不同的外观。
当整个文件被复制和修改后,我们将关闭两个文件,这在第 83 和 84 行完成。之后,在第 85 行,我们将尝试把临时文件重命名为该类希望使用的文件。操作方法与视频 01 中显示的完全相同。
结论
在本资料中,我介绍了管理模板所需的第一步。这样,它就能适应我们的需要。这将使我们能够通过相当简单的编程更好地使用 MetaTrader 5 的功能。不过,重要的是不要在图表上使用更多或更少的对象。真正重要的是要明白,MQL5 允许每个人将 MetaTrader 5 变成适合自己需要的平台,而不是创建一个不同的竞争平台。
本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/11664


