Español Português
preview
Моделирование рынка: Position View (IV)

Моделирование рынка: Position View (IV)

MetaTrader 5Тестер |
100 0
Daniel Jose
Daniel Jose

Введение

Здравствуйте и приветствую всех в очередной статье из серии о том, как создать систему репликации/моделирования.

В предыдущей статье, Моделирование рынка: Position View (III), мы показали, насколько важно знать, когда и почему следует устанавливать свойство ZOrder для объектов. До этого момента я был довольно терпелив и не показывал, что именно мы собираемся делать. Возможно, прочитав предыдущие статьи, вы подумаете, что всё это очень красиво, но совершенно не практично. Что ж, лёгкие времена прошли. Сейчас мы сделаем то, без чего мы не сможем продолжить реализацию индикатора позиции. Нам нужно объединить четыре уже имеющихся приложения для продолжения реализации индикатора позиции. Однако, уважаемый читатель, пока не стоит беспокоиться об отложенных ордерах. Как мы будем с ними работать, станет ясно в будущем. Поэтому нам нужно двигаться вперед шаг за шагом.

На данном этапе наиболее важным шагом является не решение вопроса об отложенных ордерах, а обеспечение надлежащего управления позициями. Помните следующее: у нас уже есть советник, который может открывать и закрывать позиции по рыночной цене. У нас также есть индикатор Mouse Study, который уже позволяет выполнять простые исследования/построения, но вы можете изменить его для создания более сложного исследования. У нас также есть Chart Trade, который позволяет отправлять ордера советнику, чтобы указать, нужно ли покупать или продавать, а также объем для торговли. Но для этих трех приложений нужно ещё одно, чтобы у нас была жизнеспособная система. Это недостающее приложение — как раз тот индикатор, который мы сейчас реализуем.

На текущем этапе разработки индикатор позиции является не более чем интересным приложением. Но если мы объединим его с тремя другими уже созданными приложениями, весь комплекс станет гораздо интереснее. Однако вы, возможно, подумаете: как мы это сделаем? Будем ли мы использовать класс C_IndicatorPosition в советнике? Нет, мы этого делать не будем. Мы оставим индикатор позиции отдельным от советника. Таким образом, можно добавить его в свой собственный советник.

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

Предлагаемая мной идея и демонстрация её реализации не является чем-то новым. На самом деле, именно это позволяет различным программам использовать общие компоненты. Что-то похожее на компьютерные игры, использующие DirectX. Когда DirectX получает обновление, все использующие библиотеку DirectX игры и программы также обновляются. А теперь представьте, если бы каждое приложение приходилось обновлять вручную при каждом улучшении библиотеки DirectX. Это было бы совершенно нецелесообразно. Поэтому не стоит рассматривать данную модель как потенциальную проблему. Можно рассматривать это как решение, которое со временем позволит создавать всё более полезные и безопасные приложения. Любые исправления или улучшения в одном приложении отразятся на всей цепочке приложений, которые используются в MetaTrader 5. Итак, начнём.


Давайте вспомним, в каком состоянии сейчас находится советник.

Перед внесением любых изменений в код нам необходимо посмотреть, как выглядит основной код советника. Это связано с тем, что с момента последнего внесения изменений прошло довольно много времени. Таким образом, в приведенном ниже коде можно увидеть текущий статус реализации советника:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
04. #property description "Demo version between interaction"
05. #property description "of Chart Trade and Expert Advisor"
06. #property version   "1.84"
07. #property link "https://www.mql5.com/pt/articles/12598"
08. //+------------------------------------------------------------------+
09. #include <Market Replay\Order System\C_Orders.mqh>
10. //+------------------------------------------------------------------+
11. enum eTypeContract {MINI, FULL};
12. //+------------------------------------------------------------------+
13. input eTypeContract user00 = MINI;         //Cross order in contract
14. //+------------------------------------------------------------------+
15. C_Orders    *Orders;
16. long        GL_ID;
17. //+------------------------------------------------------------------+
18. int OnInit()
19. {
20.     GL_ID = 0;
21.     Orders = new C_Orders(0xC0DEDAFE78514269);
22.     
23.     return INIT_SUCCEEDED;
24. }
25. //+------------------------------------------------------------------+
26. void OnTick() {}
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30.     (*Orders).DispatchMessage(id, lparam, dparam, sparam);
31.     switch (id)
32.     {
33.         case CHARTEVENT_CHART_CHANGE:
34.             if (GL_ID > 0)    break; else GL_ID = ChartID();
35.         case CHARTEVENT_CUSTOM + evChartTrade_At_EA:
36.             EventChartCustom(GL_ID, evEA_At_ChartTrade, user00, 0, "");
37.             break;
38.     }
39. }
40. //+------------------------------------------------------------------+
41. void OnDeinit(const int reason)
42. {
43.     switch (reason)
44.     {
45.         case REASON_REMOVE:
46.         case REASON_INITFAILED:
47.             EventChartCustom(GL_ID, evEA_At_ChartTrade, -1, 0, "");
48.             break;
49.     }
50.     
51.     delete Orders;
52. }
53. //+------------------------------------------------------------------+

Код оригинального советника

Ничего себе! В последний раз этот код был изменен в статье 84? Да, с тех пор не были внесены никакие изменения. Но теперь пришло время вернуться к работе над этим. Для этого, прежде чем вносить какие-либо изменения в этот код, необходимо понять следующее. Обратите внимание, что в строке 13 представлена единственная форма взаимодействия с пользователем или трейдером. Это взаимодействие направлено на то, чтобы указать всем остальным приложениям, которые так или иначе будут взаимодействовать с советником, что он будет ориентирован на работу с полным контрактом или с мини-контрактом.

Для тех, кто не торгует на B3 (Бразильская фондовая биржа), это взаимодействие не имеет смысла. Но независимо от того, чем вы торгуете, вам понадобится индикатор Chart Trade на графике, а также данный советник. Как только советник будет добавлен на график, он отправит сообщение всем приложениям внутри MetaTrader 5. Я уже объяснял это ранее в этой же серии статей. Если вы не понимаете, о чем идет речь, постарайтесь прочитать предыдущие статьи, чтобы глубже разобраться в теме. Понимание этого вопроса с сообщениями будет иметь первостепенное значение в дальнейшем. Без этого понимания вы не сможете следовать за материалом следующих статей.

Отлично, после отправки этого сообщения все приложения будут знать, чего от них ожидает советник. Данный советник никоим образом не будет получать команды напрямую от оператора или пользователя. Он будет принимать и отправлять команды только посредством сообщений. Так что не ожидайте увидеть линейный и идеально упорядоченный код. Здесь происходит довольно активная динамика.

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

Однако я не буду включать индикатор позиции в советник. Идея заключается в том, чтобы индикатор можно было изменять или улучшать для использования всеми советниками без необходимости их перекомпиляции. Но для того, чтобы всё работало в полной гармонии, необходимо будет внести некоторые изменения. При этом, уважаемый читатель, вам следует внимательно прислушаться к тому, что будет объясняться. В противном случае вся система даст сбой и не предоставит данные, которые должны отображаться на графике. Еще одна деталь заключается в том, что на данный момент вам не нужно беспокоиться о деталях ценовых линий. Первое, что необходимо сделать сейчас — это создать способ взаимодействия между советником и индикатором позиции. После этого мы позаботимся о взаимодействии индикатора позиции с индикатором мыши, который будет отвечать за управление ценовыми линиями.


Приступаем к реализации взаимодействия

Хорошо, начнем с внесения некоторых изменений в основной код индикатора позиции. Причина заключается в том, что теперь он больше не будет — или, лучше сказать, не должен — добавляться на график трейдером или пользователем. Это сделает советник. Но поскольку мы хотим, чтобы советник знал, какой индикатор представляет каждую позицию, нам нужно изменить некоторые вещи как в коде индикатора, так и в коде советника.

Итак, понимая, что именно нам предстоит сделать, начнем с нового кода индикатора позиции. Его можно увидеть ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Indicator for tracking an open position on the server."
04. #property description "This should preferably be used together with an Expert Advisor."
05. #property description "For more details see the same article."
06. #property link "https://www.mql5.com/pt/articles/13168"
07. #property version   "1.116"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #define def_ShortName "Position View"
12. //+------------------------------------------------------------------+
13. #include <Market Replay\Order System\C_IndicatorPosition.mqh>
14. //+------------------------------------------------------------------+
15. input ulong user00 = 0;                 //For Expert Advisor use
16. input color user01 = clrRoyalBlue;      //Color Line Price
17. input color user02 = clrForestGreen;    //Color Line Take Profit
18. input color user03 = clrFireBrick;      //Color Line Stop Loss
19. //+------------------------------------------------------------------+
20. C_IndicatorPosition *Positions = NULL;
21. //+------------------------------------------------------------------+
22. int OnInit()
23. {
24.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
25.     Positions = new C_IndicatorPosition(user01, user02, user03);
26.     if (!Positions.CheckCatch(user00))
27.     {
28.         ChartIndicatorDelete(ChartID(), 0, def_ShortName);
29.         return INIT_FAILED;
30.     }
31. 
32.     return INIT_SUCCEEDED;
33. }
34. //+------------------------------------------------------------------+
35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
36. {
37.     return rates_total;
38. }
39. //+------------------------------------------------------------------+
40. void OnDeinit(const int reason)
41. {
42.     delete Positions;
43. }
44. //+------------------------------------------------------------------+

Код индикатора Position View

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

Прошу заметить, что в строке 26 мы передаем параметр функции проверки. Ранее такого параметра не существовало. Давайте рассмотрим код класса C_IndicatorPosition, который полностью приведен ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SufixTake    "Take"
05. #define def_SufixStop    "Stop"
06. //+------------------------------------------------------------------+
07. #include "..\Auxiliar\C_Terminal.mqh"
08. //+------------------------------------------------------------------+
09. class C_IndicatorPosition : private C_Terminal
10. {
11.     private    :
12.         struct st00
13.         {
14.             ulong        ticket;
15.             color        corPrice, corTake, corStop;
16.         }m_Infos;
17. //+------------------------------------------------------------------+
18.         void CreateLineInfos(const string szObjName, const double price, const color cor, const string szDescription = "\n")
19.         {
20.             if (price <= 0) return;
21.             CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == m_Infos.corPrice ? ePriorityNull : (ePriorityOrders + (cor == m_Infos.corStop))));
22.             ObjectSetDouble(GetInfoTerminal().ID, szObjName, OBJPROP_PRICE, price);
23.             ObjectSetString(GetInfoTerminal().ID, szObjName, OBJPROP_TEXT, szDescription);
24.             ObjectSetString(GetInfoTerminal().ID, szObjName, OBJPROP_TOOLTIP, szDescription);
25.             ObjectSetInteger(GetInfoTerminal().ID, szObjName, OBJPROP_SELECTABLE, cor != m_Infos.corPrice);
26.         }
27. //+------------------------------------------------------------------+
28.     public    :
29. //+------------------------------------------------------------------+
30.         C_IndicatorPosition(color corPrice, color corTake, color corStop)
31.             :C_Terminal()
32.         {
33.             ZeroMemory(m_Infos);
34.             m_Infos.corPrice = corPrice;
35.             m_Infos.corTake  = corTake;
36.             m_Infos.corStop  = corStop;
37.         }
38. //+------------------------------------------------------------------+
39.         ~C_IndicatorPosition()
40.         {
41.             if (m_Infos.ticket != 0)
42.                 ObjectsDeleteAll(GetInfoTerminal().ID, IntegerToString(m_Infos.ticket));
43.         }
44. //+------------------------------------------------------------------+
45.         bool CheckCatch(ulong ticket)
46.         {
47.             string szName = IntegerToString(m_Infos.ticket = ticket);
48.             
49.             if (!PositionSelectByTicket(m_Infos.ticket)) return false;
50.             if (ObjectFind(GetInfoTerminal().ID, szName) >= 0)
51.             {
52.                 m_Infos.ticket = 0;
53.                 return false;
54.             }
55.             IndicatorSetString(INDICATOR_SHORTNAME, szName);
56.             CreateLineInfos(szName, PositionGetDouble(POSITION_PRICE_OPEN), m_Infos.corPrice, "Position opening price.");
57.             CreateLineInfos(szName + def_SufixTake, PositionGetDouble(POSITION_TP), m_Infos.corTake, "Take Profit point.");
58.             CreateLineInfos(szName + def_SufixStop, PositionGetDouble(POSITION_SL), m_Infos.corStop, "Stop Loss point.");
59.             
60.             return true;
61.         }
62. //+------------------------------------------------------------------+
63. };
64. //+------------------------------------------------------------------+
65. #undef def_SufixTake
66. #undef def_SufixStop
67. //+------------------------------------------------------------------+

C_IndicatorPosition.mqh

Хотя в коде произошли некоторые изменения, они не слишком сложные, поэтому особого внимания это не заслуживает. Тем не менее, как упоминалось ранее, функция проверки теперь принимает аргумент. Давайте посмотрим, что изменилось в данной функции. Для этого перейдём к строке 45, где определена функция CheckCatch.

Обратите внимание, что как только функция запускается, мы видим строку 47. На этом этапе мы преобразуем данные, которые передаст нам советник, для использования в именах создаваемых объектов. Затем, в строке 49, мы проверяем, существует позиция или нет. Что ж, здесь стоит объяснить, почему снова выполняем эту проверку, ведь советник сообщит нам и гарантирует, что позиция существует. Хорошо. Дело в том, что у меня нет намерений делать этот индикатор позиции неотъемлемой частью советника. Поэтому, если трейдер или даже пользователь попытается добавить этот индикатор на график, нам необходимо подтвердить, что позиция существует. Прошу заметить, что в данном случае мы не ищем этот параметр в индикаторе, как это было в предыдущей статье.

Как только существование позиции будет подтверждено, выполнится новая проверка. Это делается в строке 50. Но в данном случае проверка служит для того, чтобы гарантировать наличие только одного индикатора на одну позицию на графике. Существуют и другие способы выполнить эту же проверку. Однако здесь мы проверяем наличие линии со специфическим именем. Если всё пойдёт хорошо, индикатор будет работать так, как было показано в предыдущей статье. Таким образом, мы можем перейти к коду советника, чтобы понять, как будет вести себя система, когда советник будет добавлен на график. Новый код советника можно увидеть ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
04. #property description "Demo version between interaction"
05. #property description "of Chart Trade and Expert Advisor"
06. #property version   "1.116"
07. #property link "https://www.mql5.com/pt/articles/13168"
08. //+------------------------------------------------------------------+
09. #include <Market Replay\Order System\C_Orders.mqh>
10. //+------------------------------------------------------------------+
11. enum eTypeContract {MINI, FULL};
12. //+------------------------------------------------------------------+
13. input eTypeContract user00 = MINI;         //Cross order in contract
14. //+------------------------------------------------------------------+
15. C_Orders    *Orders;
16. long        GL_ID;
17. //+------------------------------------------------------------------+
18. int OnInit()
19. {
20.     GL_ID = 0;
21.     Orders = new C_Orders(0xC0DEDAFE78514269);
22.     
23.     return INIT_SUCCEEDED;
24. }
25. //+------------------------------------------------------------------+
26. void OnTick() {}
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30.     (*Orders).DispatchMessage(id, lparam, dparam, sparam);
31.     switch (id)
32.     {
33.         case CHARTEVENT_CHART_CHANGE:
34.             if (GL_ID > 0)    break;
35.             else
36.             {
37.                 GL_ID = ChartID();
38.                 for (int handle, count = PositionsTotal() - 1; count >= 0; count--)
39.                 {
40.                     handle = iCustom(_Symbol, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", PositionGetTicket(count));
41.                     ChartIndicatorAdd(GL_ID, 0, handle);
42.                     IndicatorRelease(handle);
43.                 }
44.             }
45.         case CHARTEVENT_CUSTOM + evChartTrade_At_EA:
46.             EventChartCustom(GL_ID, evEA_At_ChartTrade, user00, 0, "");
47.             break;
48.     }
49. }
50. //+------------------------------------------------------------------+
51. void OnDeinit(const int reason)
52. {
53.     switch (reason)
54.     {
55.         case REASON_REMOVE:
56.         case REASON_INITFAILED:
57.             EventChartCustom(GL_ID, evEA_At_ChartTrade, -1, 0, "");
58.             break;
59.     }
60.     
61.     delete Orders;
62. }
63. //+------------------------------------------------------------------+

Expert Advisor.mq5

Обратите внимание, что код изменился очень незначительно по сравнению с кодом, представленным в начале данной статьи. Настоящая причина того, что изменений так мало, заключается в том, чтобы объяснить читателю, что для правильной работы системы не требуется много усилий. Прошу заметить, что все изменения, или, скорее, дополнения к коду, находятся между строками 38 и 43. Но этого уже будет достаточно, чтобы советник как минимум установил связь с индикатором позиции. На самом деле, то, что мы здесь делаем, раньше выполнялось в рамках кода индикатора. В этом коде для каждой открытой позиции на график будет устанавливаться один индикатор позиции.

Тем не менее, обратите внимание на кое-что важное. Мы отслеживаем все открытые позиции, но, и это важно, мы их не фильтруем. Мы просто добавляем индикаторы на график, на котором запущен советник. Но позиция может соответствовать символу, отличному от того, который ожидается отобразить на этом графике. Хорошо, можно подумать, что это была моя ошибка, но нет, я сделал это специально, чтобы показать, что эта ошибка встречается довольно часто. И когда это замечают, оказывается, что уже используется ненадежный механизм.

Обратите внимание, что проблема заключается не в том, что мы используем доступная в MQL5 переменную _Symbol, чтобы получить доступ к имени символа графика. Ошибка заключается в том, что мы не проверяем, является ли позиция, возвращенная функцией PositionGetTicket, позицией, принадлежащей или каким-либо образом связанной с символом графика, как это проверялось в коде индикатора. Это ошибка, которой следует всегда остерегаться. В процессе тестирования нам может показаться, что на демо-счете всё работает исправно. Однако при переходе на реальный счет можно потерять деньги из-за небрежности при программировании или недостаточного тестирования на этапе реализации. Поэтому вам, уважаемый читатель, если вы только учитесь или осваиваете новые концепции, стоит быть аккуратнее при изменении любого кода. Всегда тщательно проверяйте внесенные изменения, и внимательно читайте документацию, чтобы развеять любые сомнения.

Хорошо. Теперь вы начали замечать, что некоторые аспекты программирования немного сложнее, чем могут показаться на первый взгляд. Тем не менее, это не повод унывать и бросать программирование. Главное — уметь адаптироваться и найти правильный путь. Но вы, должно быть, думаете, что в таком случае достаточно проверить, соответствует ли символ позиции, указанной тикетом, тому, который мы ожидаем использовать. Правильно? В той или иной степени так и есть.

И я говорю это именно потому что не знаю, на каком рынке вы работаете. Об этом уже упоминалось в начале статьи. Существуют случаи, главным образом на B3 (Бразильской бирже), когда у нас есть полный контракт и мини-контракт. У нас также имеется история текущего контракта. Некоторые трейдеры предпочитают использовать исторические данные для совершения сделок. Другие же используют график того символа, которым они хотят торговать.

Обратите внимание: хотя это и кажется сложной ситуацией, и вы можете задаться вопросом, зачем кому-то так усложнять жизнь, пытаясь торговать на графике, отличающемся от графика конкретного контракта или символа, — это всегда выбор трейдера. Мы не можем ограничивать его; наша задача как программистов — удовлетворить потребности трейдера.

Таким образом, возникает другой вопрос: как нам справиться с этой ситуацией, если у нас нет возможности узнать, какой символ является правильным? Однако, мой уважаемый читатель, ситуация не так уж и сложна. Да, мы знаем, где находятся данные для соответствующего символа. Это отображается в индикаторе Chart Trade. И именно в этот момент ситуация становится по-настоящему интересной. У программиста есть два варианта. Первый вариант — спросить у панели Chart Trade, какой символ является правильным и чьи позиции должны отображаться на графике. Хорошо, вы можете подумать: "Но зачем мне задавать этот вопрос Chart Trade? Если данные отображаются в объекте Chart Trade, просто нужно прочитать содержимое этого объекта".

Вы должны это сделать, уважаемый читатель. Данные находятся в объекте Chart Trade, но доступ к нему будет не таким уж простым. Для получения этих данных из Chart Trade нам потребуется создать отдельное сообщение, но это значительно усложнило бы код советника. Однако существует другое решение, значительно более простое, но нужно действовать правильно. Иначе нарушим инкапсуляцию, которая была создана и протестирована довольно давно. Тогда, подумав спокойно и проанализировав код, вы заметите, что панель Chart Trade получает данные о символе из класса C_Terminal. Если быть более точным, из процедуры CurrentSymbol, которая находится на строке 47 класса C_Terminal.

Именно здесь кроется опасность, особенно для тех, кто только начинает изучать программирование. Многие начинающие разработчики попытаются получить доступ к этой процедуре напрямую из советника. Это невозможно, поскольку данная процедура находится в защищенном разделе. Итак, может вы подумаете: "Я собираюсь разместить эту процедуру в публичной области". Это ошибка. И вторая ошибка будет следующей: Если вы посмотрите на процедуру, то заметите, что она не возвращает никакого значения или аргумента. Тогда вы решаете изменить его, чтобы он возвращал данные правильного символа, но, сделав это, вы полностью нарушите процедуру, потому что в будущем это может измениться, что позволит нам добавить больше функций. Поэтому правильным решением будет НЕ трогать процедуру в строке 47 класса C_Terminal.

Итак, вы, как новичок, можете себе представить: "Так, решить эту проблему невозможно, поскольку невозможно узнать, какой символ будет использован". Но это неверная формулировка проблемы. Да, решение есть, причем простое, элегантное и экономное. Оно не окажет никакого побочного воздействия на уже созданный и протестированный код. Всё, что нужно сделать, это перейти к конструктору класса C_Terminal и внести в него изменения. Исходный код можно увидеть в следующем фрагменте:

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);

Оригинальный фрагмент файла C_Terminal.mqh

Необходимые изменения показаны ниже:

106. //+------------------------------------------------------------------+        
107.         C_Terminal(const long id = 0, const uchar sub = 0, const bool bFull = false)
108.             {
109.                 m_Infos.ID = (id == 0 ? ChartID() : id);
110.                 m_Mem.AccountLock = false;
111.                 m_Infos.SubWin = (int) sub;
112.                 CurrentSymbol(bFull);
113.                 ZeroMemory(m_Mouse);
114.                 m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);

Модифицированный фрагмент файла C_Terminal.mqh

Вы заметили разницу между кодами? Это очень тонко, просто и элегантно. Теперь мы можем использовать класс C_Terminal, чтобы узнать имя символа непосредственно в советнике. Или мы можем передать данные индикатору позиции, чтобы он сам выполнил эту задачу за нас. В любом случае, теперь советник сможет сообщить индикатору позиции, хочет ли он отображать полные контракты или мини-контракты. Это достигается путем непосредственного использования истории контрактов. Но можно подумать, что это создаст проблему, поскольку может противоречить тому, что указано в Chart Trade, но на самом деле это не так. Данные будут в точности совпадать с теми, что указаны в Chart Trade.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
04. #property description "Demo version between interaction"
05. #property description "of Chart Trade and Expert Advisor"
06. #property version   "1.116"
07. #property link "https://www.mql5.com/pt/articles/13168"
08. //+------------------------------------------------------------------+
09. #include <Market Replay\Order System\C_Orders.mqh>
10. #include <Market Replay\Auxiliar\C_Terminal.mqh>
11. //+------------------------------------------------------------------+
12. enum eTypeContract {MINI, FULL};
13. //+------------------------------------------------------------------+
14. input eTypeContract user00 = MINI;         //Cross order in contract
15. //+------------------------------------------------------------------+
16. C_Orders       *Orders = NULL;
17. C_Terminal     *Terminal = NULL;
18. //+------------------------------------------------------------------+
19. int OnInit()
20. {
21.     Orders = new C_Orders(0xC0DEDAFE78514269);
22.     
23.     return INIT_SUCCEEDED;
24. }
25. //+------------------------------------------------------------------+
26. void OnTick() {}
27. //+------------------------------------------------------------------+
28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
29. {
30.     (*Orders).DispatchMessage(id, lparam, dparam, sparam);
31.     switch (id)
32.     {
33.         case CHARTEVENT_CHART_CHANGE:
34.             if (Terminal != NULL) break;
35.             else
36.             {
37.                 ulong ul;
38.                 Terminal = new C_Terminal(0, 0, user00);
39.                 for (int handle, count = PositionsTotal() - 1; count >= 0; count--)
40.                 {
41.                     ul = PositionGetTicket(count);
42.                     if (PositionGetString(POSITION_SYMBOL) != (*Terminal).GetInfoTerminal().szSymbol) continue;
43.                     handle = iCustom(_Symbol, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", ul);
44.                     ChartIndicatorAdd((*Terminal).GetInfoTerminal().ID, 0, handle);
45.                     IndicatorRelease(handle);
46.                 }
47.             }
48.         case CHARTEVENT_CUSTOM + evChartTrade_At_EA:
49.             if (Terminal != NULL)
50.                 EventChartCustom((*Terminal).GetInfoTerminal().ID, evEA_At_ChartTrade, user00, 0, "");
51.             break;
52.     }
53. }
54. //+------------------------------------------------------------------+
55. void OnDeinit(const int reason)
56. {
57.     switch (reason)
58.     {
59.         case REASON_REMOVE:
60.         case REASON_INITFAILED:
61.             if (Terminal != NULL)
62.                 EventChartCustom((*Terminal).GetInfoTerminal().ID, evEA_At_ChartTrade, -1, 0, "");
63.             break;
64.     }
65.     
66.     delete Orders;
67.     delete Terminal;
68. }
69. //+------------------------------------------------------------------+

Expert Advisor.mq5

В этом и заключается прелесть программирования. Сравните предыдущий код с другими кодами советника, представленными в этой статье, и обратите внимание, насколько программирование — это прекрасная и удивительная дисциплина. Ведь используя немного знаний об указателях в C/C++, здесь, в MQL5, удалось избавиться от глобальной переменной и оставить только объявления классов. В действительности это были бы указатели на объекты классов. Но это нормально, MQL5 не использует указатели, как C/C++. Тем не менее, можно использовать часть того, что доступно в C/C++. Если вы не понимаете, что происходит в этом советнике, не волнуйтесь, ведь я здесь, чтобы помочь вам разобраться в этом коде.

Обратите внимание, что в строке 10 мы включили заголовочный файл C_Terminal.mqh, который мы только что настроили под наши интересы. В строке 17 мы объявляем глобальную переменную, чтобы иметь возможность доступа к классу. Однако, хотя это и не обязательно, хочу явно продемонстрировать, что переменная-указатель инициализируется значением NULL. Если не указать этот параметр, компилятор сделает это неявно. Но важно ещё уточнить, какую именно инициализацию мы проводим.

Хорошо. Как только будет выполнена функция OnInit, MetaTrader 5 запустит событие CHART_CHANGE. Оно будет перехвачено функцией OnChartEvent в строке 28, что положит начало обработке событий советником. При первом выполнении этой функции указатель Terminal будет иметь значение NULL. Следовательно, выполнение строки 34 завершится ошибкой, что позволит выполнить строку 35. Именно здесь и начинается волшебство. Обратите внимание, что код в этом месте очень похож на код, который мы видели ранее в советнике. Однако в строке 38 мы вызываем конструктор класса C_Terminal, указывая, будем ли мы использовать тот или иной тип контракта.

Благодаря этому конструктор сгенерирует правильное имя символа. Таким образом, в строке 41 мы получаем значение тикета. Нам необходимо сделать это перед вызовом других функций для получения данных о позиции. Об этом упоминается в документации MQL5; изучите её для получения более подробной информации. После этого мы уже можем в строке 42 проверить, совпадает ли имя символа, которому соответствует позиция, с тем, которое ожидает советник. Если это не так, мы вернемся к строке 39. В противном случае, то есть если символ имеет ожидаемое имя, мы вызываем индикатор позиции, как это делалось ранее.

Теперь обратите внимание на строки 49 и 61. Ответьте: почему я выполняю проверку в этих строках? Подумайте немного, прежде чем читать ответ, который я приведу ниже. Хорошо, надеюсь, вы хотя бы сделали попытку, прежде чем читать ответ. Причина, по которой эти строки существуют, заключается как раз в повышении надежности кода советника. Если вы попытаетесь вызвать какую-либо функцию или процедуру класса C_Terminal до того, как класс был правильно инициализирован, проверка значения указателя на NULL предотвратит простую генерацию ошибки советником в терминале MetaTrader 5. Выполняя эту проверку, вы гарантируете, что поток выполнения будет оставаться таким, как ожидается.

Многие программисты на C/C++, но в основном программисты на C, обычно не выполняют эти проверки при использовании указателей. Поэтому многие считают, что программы, написанные на языке C, содержат множество ошибок при запуске. Однако подобные ошибки связаны не с самим языком, а с тем, как была написана программа, или с причиной, по которой она была написана именно так. В некоторых случаях создание программ, ищущих уязвимости в системе, является даже преднамеренным, чтобы совершить действие, не предусмотренное владельцем системы.


Заключительные идеи

В этой статье мы начали объединять различные приложения, которые раньше были полностью изолированы друг от друга. Хотя Chart Trade, индикатор мыши и советник уже имели определенную взаимосвязь, всё ещё не было способа напрямую наблюдать на графике открытые на торговом сервере позиции. Часто используется система кросс-ордеров. С этого момента это становится возможным, открывая множество возможностей для новых идей и реализаций. Хотя мы только начинаем запускать компоненты в работу, у нас уже есть направление для движения.

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

Файл Описание
Experts\Expert Advisor.mq5
Демонстрирует взаимодействие между Chart Trade и советником (для взаимодействия требуется 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/13168

Прикрепленные файлы |
Anexo.zip (779.24 KB)
Адаптивная архитектура Smart Money (ASMA): Интеграция логики SMC с анализом сентимента для динамического переключения стратегий Адаптивная архитектура Smart Money (ASMA): Интеграция логики SMC с анализом сентимента для динамического переключения стратегий
В этой теме рассматривается, как построить адаптивную архитектуру Smart Money (ASMA) — интеллектуального советника, который объединяет концепции Smart Money Concepts (Order Blocks, Break of Structure, Fair Value Gaps) с рыночными настроениями в реальном времени, чтобы автоматически выбирать наиболее подходящую торговую стратегию исходя из текущего рыночного режима.
Рыночные секреты Ларри Уильямса (Часть 7): Эмпирическое исследование концепции "торгового дня недели" Рыночные секреты Ларри Уильямса (Часть 7): Эмпирическое исследование концепции "торгового дня недели"
Эмпирическое исследование концепции торгового дня недели по Ларри Уильямсу: как измерять, тестировать и применять временную поведенческую закономерность рынка с помощью MQL5. В статье представлена структурированная методика анализа для анализа win rate (процент прибыльных сделок) и результатов по торговым дням, чтобы улучшать краткосрочные торговые системы.
Рыночные секреты Ларри Уильямса (Часть 8): Объединение волатильности, структуры и временных фильтров Рыночные секреты Ларри Уильямса (Часть 8): Объединение волатильности, структуры и временных фильтров
Подробный разбор создания советника MQL5 для торговли пробоями волатильности, вдохновленного идеями Ларри Уильямса: свинг-структура, входы на основе волатильности, фильтр торгового дня недели, временные фильтры и гибкое управление риском, с полной реализацией и воспроизводимой тестовой конфигурацией.
Сеточный советник на клеточном автомате с онлайн-обучением в MQL5 (Часть II): Новый уровень онлайн-адаптации Сеточный советник на клеточном автомате с онлайн-обучением в MQL5 (Часть II): Новый уровень онлайн-адаптации
Во второй части клеточный автомат переводится с решётки на граф. Признаки становятся вершинами графа с локальными и дальними small‑world связями, а клетки — агентами, которые взаимодействуют не только с геометрическими, но и со смысловыми соседями. Рассматриваются графовая фильтрация признаков, построение графа соседей, обновлённое голосование по согласованности и метрики Graph Coherence и Graph Health. Это снижает влияние одиночных выбросов и ускоряет распространение рыночных режимов при полной совместимости с MQL5.