Español Português
preview
Разработка системы репликации (Часть 43): Проект Chart Trade (II)

Разработка системы репликации (Часть 43): Проект Chart Trade (II)

MetaTrader 5Примеры | 31 июля 2024, 16:34
41 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Разработка системы репликации (Часть 42): Проект Chart Trade (I) я показал, как можно наладить взаимодействие между индикатором мыши и другими индикаторами.

Там мы начали писать код с целью обеспечить создание и поддержание индикатора Chart Trade в идеальной гармонии с индикатором мыши. Однако, в отличие от того, что было сделано в первых версиях этого индикатора Chart Trade, описанных в статьях:

здесь мы будем делать нечто более продвинутое и, следовательно, другое. Но в любом случае результат будет такой же, как на видео 01. Предлагаю вам посмотреть это видео прежде, чем приступить к чтению статьи, чтобы иметь представление о том, что именно мы будем делать. Это не то, что можно понять, просто прочитав код. Говорят, что одна картинка стоит тысячи слов, поэтому посмотрите видео, чтобы лучше понять, что будет объяснено в статье.


Видео 01 - Демонстрационное видео об этой статье

На видео видно, что мы выводим данные для отображения в окне Chart Trade. Вы, должно быть, заметили, что мы делаем все именно так, как показано в предыдущей статье, но при этом информация обновляется без фактического использования объектов. И наверное, задаетесь вопросом: как это возможно?

"Этот парень, должно быть, занимается каким-то мошенничеством... Я никогда не видел, чтобы кто-то делал подобные вещи... Это не имеет никакого смысла..."  Другие, наверное, думают, что я нечто вроде колдуна или мистика, с возможностями, выходящими за рамки воображения. Нет. Ничего подобного. Я просто использую и платформу MetaTrader 5, и язык MQL5 на том уровне, который многие не стремятся понять. Продолжают говорить и делать всегда одно и то же, не исследуя настоящий потенциал и возможности ни языка MQL5, ни платформы MetaTrader 5.

Надеюсь, все посмотрели видео. Потому что в этой статье я покажу, как сделать что-то, что в корне изменит ваше представление о возможном и невозможном. Важно: я объясню исключительно то, что показано на видео. То, чего в нем нет, будет рассмотрено позже.


Обновление кода индикатора

Изменения, которые необходимо внести, будут не такими уж большими. Но мы все равно будем продвигаться постепенно, иначе все останутся без четкого понимания происходящего.

Начнем с рассмотрения изменений, внесенных в код индикатора. Их можно увидеть в коде, полностью приведенном ниже:

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. //+------------------------------------------------------------------+

Исходный код индикатора Chart Trade

Если внимательно посмотреть на исходный код индикатора, который был приведен в статье Разработка системы репликации (часть 42): Проект Chart Trade (I), можно отметить, что он претерпел некоторые изменения. Это по сути, добавление средств, позволяющих пользователю сообщить значение, которое первоначально будет помещено в индикатор. Хотя у нас между строками 14 и 16 и есть такая форма взаимодействия, эти пункты не совсем необходимы, хотя и интересны для тестирования. Помните, что Chart Trade  это система взаимодействия с пользователем системы.

Вместо строк с 14 по 16 можно просто ввести в индикатор значение «по умолчанию» или default. Это можно сделать в строке 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, но здесь мы фокусируемся на нем, является концепт шаблонов или templates. Они используются как способ позволить нам настраивать, корректировать и организовывать определенные вещи простым и практичным способом. Начиная от аннотаций, и заканчивая упрощением процесса просмотра рынка определенным образом в определенный момент.

Шаблоны могут содержать различные вещи, и я уже показывал ранее, как исследовать некоторые из них. В статье Несколько индикаторов на графике (Часть 03): Разработка пользовательских определений я продемонстрировал, как можно размещать различные индикаторы рядом друг с другом, как это показано на рисунке 01.

Рисунок 01

Рисунок 01 – Использование нескольких индикаторов в одном подокне

Показанная выше концепция возможна только при использовании шаблонов. Независимо от того, насколько вы хороший программист и насколько обширны ваши познания в MQL5, вы никогда не сможете получить результат, показанный на рисунке 01, не понимая, как функционирует MetaTrader 5. Это потому, что не все сводится к написанию кода. Программирование только помогает создать решение, которое иначе было бы невозможно. Оно не является и не должно быть первой попыткой создания чего-то нового, а скорее инструментом для достижения желаемого результата.

И почему я это говорю? Причина именно в том, что показано на видео 01 в начале этой статьи. Независимо от того, насколько хорошо вы умеете программировать или понимаете, как работает MQL5, вы не сможете сделать то, что показано на видео, не понимая, как функционирует MetaTrader 5.

Немаловажный нюанс этой истории в том, что когда статья Несколько индикаторов на графике (часть 06): Превращаем MetaTrader 5 в систему RAD (II) была опубликована, в то время я еще не использовал определенные концепции MetaTrader 5. Я все еще был прикован к некоторым идеям и концепциям, которые со временем оказались неподходящими. Я не говорю, что они были неверными, просто менее подходящими. Существуют гораздо лучшие решения, чем те. И это связано именно с тем, что было показано в той статье, так же как и в предыдущей Несколько индикаторов на графике (часть 05): Певращаем MetaTrader 5 в систему RAD (I).

В обоих случаях можно заметить, что шаблон был открыт и была попытка работы над ним. Но вся эта работа была ни чем иным, как простой царапиной на поверхности, поскольку многое из того, что вы видите, представляет собой перепрограммирование одного и того же шаблона. Это было сделано таким образом, чтобы эмулировать систему RAD, которую можно реализовать путем программирования на MQL5.

Хотя это и работало, можно заметить, что на самом деле шаблон, созданный как в MetaTrader 5, позже, когда индикатор был помещен на график, был воссоздан самим индикатором. Следовательно все объекты, существовавшие в шаблоне, были воссозданы индикатором. Таким образом MetaTrader 5 получал доступ к находящимся там объектам, что позволяло ему настраивать и изменять значения, присутствующие в каждом из них.

Однако, по прошествии времени, стало ясно, что идея может быть усовершенствована. Именно поэтому я нашел более подходящую модель Chart Trade, где используется минимум объектов для удовлетворения всех потребностей. Правда, нужно будет сделать еще кое-что, помимо того, что будет показано в этой статье. Тем не менее, на видео 01 можно заметить, что в значениях произошли изменения, и при этом на экране у нас всего один объект.

И это именно то, что позволяет нам делать программирование. Без него мы были бы ограничены тем, что доступно в MetaTrader 5, а с помощью программирования мы расширяем возможности платформы. И это достигается за счет использования того, что уже существует в MetaTrader 5. То есть, мы создаем шаблон именно с тем, что нам нужно, и помещаем его в OBJ_CHART. Но без должного программирования изменить значения, присутствующие в объектах внутри шаблона, было бы невозможно. Однако, используя программирование правильно, мы решаем эту проблему, тем самым расширяя возможности MetaTrader 5.

Чтобы это стало ясней, необходимо открыть файл шаблона, используемый в 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 (шаблон)

Я знаю, что его содержание может показаться совершенно ненужным. Но запомните следующее: В ПРИЛОЖЕНИИ НЕ БУДЕТ ФАЙЛОВ, вся система будет опубликована таким образом, чтобы файлы можно было получить, переписав коды в соответствующие файлы. Я делаю это для того, чтобы вы действительно прочли и поняли статью. Я не хочу, чтобы вы просто скачивали приложенные файлы и использовали систему, не зная, что вы используете. Хотя может показаться, что так статья становится значительно длиннее, на самом деле это не так, просто объяснение становится гораздо более подробным.

Теперь давайте внимательно посмотрим на файл шаблона 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. Вопрос в следующем: можете ли вы понять, как это происходит?

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

Обратите внимание, что файл 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. Причина этого в том, что пользователь может ввести значение, которое для актива, на котором включен индикатор Chart Trade, не имеет смысла. Например, в случае с фьючерсами на доллар, движения всегда составляют от R$ 5,00 до R$ 5,00 за контракт. В этом случае указывать take profit или stop loss как значение, не кратное 5,  например, take profit R$ 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

Прикрепленные файлы |
Anexo.zip (420.65 KB)
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Возможности Мастера MQL5, которые вам нужно знать (Часть 14): Многоцелевое прогнозирование таймсерий с помощью STF Возможности Мастера MQL5, которые вам нужно знать (Часть 14): Многоцелевое прогнозирование таймсерий с помощью STF
Пространственно-временное слияние (Spatial Temporal Fusion, STF), которое использует как "пространственные", так и временные метрики при моделировании данных, в первую очередь применяется в дистанционном обследовании и во многих других областях, связанных с визуализацией, для лучшего понимания нашего окружения. Основываясь на опубликованной статье, мы изучим потенциал этого подхода для трейдеров.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Машинное обучение и Data Science (Часть 21): Сравниваем алгоритмы оптимизации в нейронных сетях Машинное обучение и Data Science (Часть 21): Сравниваем алгоритмы оптимизации в нейронных сетях
В этой статье мы заглянем в самую глубь нейронных сетей и поговорим об используемых в них алгоритмах оптимизации. В частности обсудим ключевые методы, которые позволяют раскрыть потенциал нейронных сетей и повысить точность и эффективность моделей.