Español Português
preview
Моделирование рынка (Часть 04): Создание класса C_Orders (I)

Моделирование рынка (Часть 04): Создание класса C_Orders (I)

MetaTrader 5Примеры |
76 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье, "Моделирование рынка (Часть 82): Вопрос производительности", мы внесли некоторые изменения в классы, чтобы обойти, по крайней мере, на данный момент, некоторые проблемы, с которыми мы столкнулись. Такие проблемы снижали общую производительность системы. Хотя на данный момент мы решили данные проблемы, теперь нам предстоит сделать кое-что довольно сложное. Но не в этой первой части, поскольку мы уже затрагивали этот вопрос в других, более старых статьях. Однако сейчас мы действуем немного иначе. Поэтому и подход к решению данного вопроса будет несколько иным.

Я знаю, что многим хочется увидеть, как приложение репликации/моделирования выполняет ордеры. Однако перед этим нам нужно убедиться в том, что система ордеров работает, и мы можем взаимодействовать с реальным торговым сервером, либо через DEMO-счет, либо через РЕАЛЬНЫЙ-счет. В любом случае те приложения, которыми в данном случае будут индикатор Chart Trade, указатель мыши и советник, должны работать слаженно, чтобы связь с реальным торговым сервером была бесперебойной.

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

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


Понимание идеи

Если вы следили за этой серией, то заметили, что в статье "Разработка системы репликации (Часть 78): Новый Chart Trade (V)", мы начали показывать, как будет происходить взаимодействие. Советник, по сути, не знает, откуда приходят ордеры. Однако он умеет интерпретировать входящие сообщения. Хотя используемые в то время средства не позволяли применять систему кросс-ордеров, мы решили эту проблему в последующих статьях, таких как "Моделирование рынка (Часть 02): Кросс-ордеры (II)", где мы показали, как будет выглядеть система сообщений, передаваемых советнику.

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

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


Запускаем рыночные ордеры

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

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Defines.mqh"
005. //+------------------------------------------------------------------+
006. class C_Orders
007. {
008.    protected:
009. //+------------------------------------------------------------------+
010. inline const ulong GetMagicNumber(void) const {return m_MagicNumber;}
011. //+------------------------------------------------------------------+
012.    private   :
013. //+------------------------------------------------------------------+
014.       MqlTradeRequest   m_TradeRequest;
015.       ulong             m_MagicNumber;
016.       bool              m_bTrash;
017. //+------------------------------------------------------------------+
018.       struct stChartTrade
019.       {
020.          struct stEvent
021.          {
022.             EnumEvents  ev;
023.             string      szSymbol,
024.                         szContract;
025.             bool        IsDayTrade;
026.             ushort      Leverange;
027.             double      PointsTake,
028.                         PointsStop;
029.          }Data;
030. //---
031.          bool Decode(const EnumEvents ev, const string sparam)
032.             {
033.                string Res[];
034.       
035.                if (StringSplit(sparam, '?', Res) != 7) return false;
036.                stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])};
037.                if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc;
038.                
039.                return true;
040.             }
041. //---
042.       }m_ChartTrade;
043. //+------------------------------------------------------------------+
044.       ulong SendToPhysicalServer(void)
045.          {
046.             MqlTradeCheckResult  TradeCheck;
047.             MqlTradeResult       TradeResult;
048.             
049.             ZeroMemory(TradeCheck);
050.             ZeroMemory(TradeResult);
051.             if (!OrderCheck(m_TradeRequest, TradeCheck))
052.             {
053.                PrintFormat("Order System - Check Error: %d", GetLastError());
054.                return 0;
055.             }
056.             m_bTrash = OrderSend(m_TradeRequest, TradeResult);
057.             if (TradeResult.retcode != TRADE_RETCODE_DONE)
058.             {
059.                PrintFormat("Order System - Send Error: %d", TradeResult.retcode);
060.                return 0;
061.             };
062.             
063.             return TradeResult.order;
064.          }
065. //+------------------------------------------------------------------+   
066.       ulong ToMarket(const ENUM_ORDER_TYPE type)
067.          {
068.             double price  = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID));
069.             double vol    = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP);
070.             uchar  nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS);
071.             
072.             ZeroMemory(m_TradeRequest);
073.             m_TradeRequest.magic        = m_MagicNumber;
074.             m_TradeRequest.symbol       = m_ChartTrade.Data.szContract;
075.             m_TradeRequest.price        = NormalizeDouble(price, nDigit);
076.             m_TradeRequest.action       = TRADE_ACTION_DEAL;
077.             m_TradeRequest.sl           = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit);
078.             m_TradeRequest.tp           = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit);
079.             m_TradeRequest.volume       = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit);
080.             m_TradeRequest.type         = type;
081.             m_TradeRequest.type_time    = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
082.             m_TradeRequest.stoplimit    = 0;
083.             m_TradeRequest.expiration   = 0;
084.             m_TradeRequest.type_filling = ORDER_FILLING_RETURN;
085.             m_TradeRequest.deviation    = 1000;
086.             m_TradeRequest.comment      = "Order Generated by Experts Advisor.";
087. 
088.             MqlTradeRequest TradeRequest[1];
089. 
090.             TradeRequest[0] = m_TradeRequest;
091.             ArrayPrint(TradeRequest);
092. 
093.             return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0);
094.          };
095. //+------------------------------------------------------------------+
096.    public   :
097. //+------------------------------------------------------------------+
098.       C_Orders(const ulong magic)
099.          :m_MagicNumber(magic)
100.          {
101.          }
102. //+------------------------------------------------------------------+   
103.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
104.          {
105.             switch (id)
106.             {
107.                case CHARTEVENT_CUSTOM + evChartTradeBuy     :
108.                case CHARTEVENT_CUSTOM + evChartTradeSell    :
109.                case CHARTEVENT_CUSTOM + evChartTradeCloseAll:
110.                   if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev)
111.                   {
112.                      case evChartTradeBuy:
113.                         ToMarket(ORDER_TYPE_BUY);
114.                         break;
115.                      case evChartTradeSell:
116.                         ToMarket(ORDER_TYPE_SELL);
117.                         break;
118.                      case evChartTradeCloseAll:
119.                         break;
120.                   }
121.                   break;
122.             }
123.          }
124. //+------------------------------------------------------------------+   
125. };
126. //+------------------------------------------------------------------+

Исходный код файла C_Replay.mqh

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

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

В статье "Разработка системы репликации (Часть 78): Новый Chart Trade (V)", мы объяснили сообщения, которые должен перехватывать советник. Вы, наверное, заметили, что для перехвата таких сообщений мы использовали процедуру OnChartEvent. Внутри данной процедуры мы сделали вызов класса. Этот класс перевел полученное сообщение и вывел его на терминал, чтобы мы могли его проанализировать. Это была самая простая часть, поскольку нам не нужно было использовать эти данные для связи с торговым сервером.

При просмотре исходного кода советника вы заметите, что в процедуре OnChartEvent есть вызов DispatchMessage. Такой вызов действительно дойдет до этого заголовочного файла. Точнее, он вызовет строку 103 данного файла. Но прежде, чем мы рассмотрим это, давайте начнем с самого начала, то есть, с конструктора класса.

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

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

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

Этот номер инициализируется в строке 99, а переменная, которая его хранит, объявляется в строке 15. Прошу заметить, что в строке 12 есть условие private, которое делает всё, что находится между этой строкой и строкой 96, инкапсулированным внутри класса, включая переменные, объявленные между строками 14 и 16.

Перед тем, как продолжить, я хотел бы обратить ваше внимание на строку 10. Здесь у нас есть функция, цель которой - вернуть вызывающей стороне магическое число, определенное в этом классе. Однако тот факт, что данная строка находится между строкой 08, где у нас есть защищенная часть кода, и строкой 12, где находится приватная часть кода, не позволяет использовать эту функцию вне системы наследования. То есть, если мы попытаемся получить доступ к этой защищенной функции извне класса, который наследует от этого класса C_Orders, мы получим сообщение об ошибке от компилятора.

Так что на будущее данная функция предусмотрена. Я не хочу добавлять ее позже только для того, чтобы выполнить задачу для личного использования. Возможно, в будущем мы объясним, о чем идет речь, Но пока вам не стоит об этом беспокоиться. Просто вы должны знать, что данная функция в строке 10 не может быть доступна вне системы наследования и служит только для возврата магического номера этого класса.

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


Процедура внутри структуры?

В статье "Разработка системы репликации (Часть 78): Новый Chart Trade (V), мы показали систему перевода с помощью класса. Здесь мы используем кое-что другое. Но возможно ли сделать такое? Да, именно так и делаю, поскольку классы - это просто более сложная структура. Однако, поскольку то, что нам нужно, можно смоделировать более простым способом, мы решили сделать это в рамках структуры. Вы сможете это увидеть между строками 18 и 42.

Теперь обратите внимание: в строке 18 мы объявляем структуру. Если бы мы объявили класс, то всё, что находится между строками 18 и 42, рассматривалось бы как приватное. В этом случае необходимо добавить публичную часть, чтобы изменить тип доступа. Но это не будет проблемой. Однако, подумайте вот о чем: зачем кому-то создавать класс внутри класса? Это не имеет смысла и часто делает код гораздо запутаннее.

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

Таким образом, сообщение рассматривается как структура данных, расположенная между строками 20 и 29. Но подождите, а разве структура данных не находится между строками 18 и 42? Нет, структура сообщения находится между строками 20 и 29. Однако, как объяснялось в предыдущей статье, сообщение следует рассматривать как строку. Но если взять эту строку и разбить ее на фрагменты в зависимости от длины структуры данных сообщения, мы рискуем неправильно понять сообщение. Поэтому нам нужен дополнительный код, который поможет его интерпретировать.

По этой причине между строками 31 и 40 у нас есть функция. Она находится в основной структуре, но не является частью структуры сообщений. Почему? Потому что элементы существуют отдельно. Ничто не мешает вам собрать всё воедино: структуру сообщения, функции и процедуры. Но, поступая так, мы рискуем столкнуться с проблемами при внедрении.

Теперь прошу внимания, потому что это довольно запутанно. Если вы сможете это понять, то вам будет ясно, почему многие программисты избегают использовать структуры, даже если это возможно. И когда некоторые программисты пытаются использовать структуры, они в итоге превращают свои программы в бомбу замедленного действия, особенно если код написан на унаследованном языке C.

Смотрите на строку 36. Данная строка представляет собой реальную опасность того, что всё может перемешаться. И опять же, делать это не обязательно, но строка 36 проясняет ситуацию, поскольку вставляет все нужные значения в структуру данных. Если бы, вместо структуры данных у нас была, например, функция или процедура, что бы произошло при выполнении строки 36? Или еще хуже: что произойдет при вызове функции или процедуры в памяти, которая была перезаписана в строке 36? Если вы не представляете, что может произойти, то вам повезло, что вы еще не столкнулись с настоящей проблемой и опасностью устаревшего языка C. Но если вы знаете, как это происходит, вы понимаете, почему были созданы эти классы.

Мы не будем вдаваться в подробности того, что может произойти. Но не забывайте, что по-настоящему злобный программист может сделать такое, что вы и представить себе не сможете. Однако давайте вернемся к рассмотрению нашего кода. Функция в строке 31 делает то же самое, что и раньше, поэтому я не вижу необходимости объяснять то же самое еще раз. Однако прошу посмотреть на строку 110, где вызывается функция. Обратите внимание, что здесь мы не вызываем функцию напрямую по ее имени, нам необходима дополнительная ссылка. Данная ссылка - как раз название переменной, которая содержит данные, представляющие нашу структуру данных. Хотя это может показаться запутанным, это не так. Забудьте на время о том, что мы используем структуру, и подумайте о том, что это класс: разве вы бы не делали всё так же? Так что на самом деле никакой путаницы здесь нет.

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


Поговорим с сервером

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

Как тот, на кого мы хотим работать, сервер не обязан понимать, что написано в анкете. Он просто просматривает её и, если ему не нравится, выбрасывает. В отличие от работодателя, сервер сообщит нам причину отказа. Поэтому хорошо выстроенная процедура общения, несомненно, позволит нам легко решать проблемы.

Для этого у нас есть функция, которая находится между строками 44 и 64. Несмотря на свою простоту, данная функция содержит всё, что нам нужно, чтобы узнать о произошедшем. Обратите внимание на логическую последовательность шагов, которые необходимо выполнить. Мы не можем просто делать всё по-старому. Необходимо соблюдать следующий порядок: сначала надо установить в ноль содержимое памяти ответных данных сервера. Это делается в строках 49 и 50. Затем, чтобы избежать отправки ордера, который не был принят по произвольной причине, мы проверяем, нет ли в ордере проблем. Вы как будто проверяете анкету перед отправкой работодателю. Данная проверка выполняется в строке 51. Если произошла ошибка, код ошибки укажет на это, чтобы мы могли устранить её и повторить попытку. Если всё в порядке, мы отправим наш ордер на торговый сервер в строке 56.

Чтобы компилятор не предупредил нас об отсутствии проверки возвращаемого сервером значения, мы отправим данный возврат в переменную, которая, по сути, будет выполнять роль мусорного бака. Причина в том, что ответ самой функции нас не интересует. Что действительно важно, так это содержание структуры ответа. Чтобы убедиться в этом, в строке 57 мы проверяем, отличается ли результат от значения TRADE_RETCODE_DONE. Если он отличается, то произошла какая-то ошибка. Затем, чтобы узнать, в чем состоит ошибка, мы выводим на терминал значение кода ошибки. Это делается в строке 59.

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

Такой ордер выполняется в другой функции. Это отображается в строке 66. Данная функция должна принимать только один аргумент. Этот аргумент указывает на то, будем ли мы осуществлять покупку или продажу. Он вызывается в двух местах: в строке 113 и в строке 116. Эти точки срабатывают, когда сообщение от Chart Trade просит нас совершить действие по рыночной цене. Но если эта функция в строке 66 получает только один аргумент, который указывает, осуществляем ли мы покупку или продажу, как сервер узнает, где разместить наш стоп-лосс или тейк-профит? И как будет передаваться другая информация, например, уровень плеча или актив, если мы используем систему кросс-ордеров? Как будут передаваться эти данные? Супер. Теперь мы подошли к интересному моменту.


Заполняем «анкету» заявки

Если вы обратили внимание на функцию связи с сервером в строке 44, то заметили, что в строках 51 и 56 мы используем структуру, которая не объявлена в данной функции. На самом деле эта структура объявлена в теле класса C_Orders. Находится она в строке 14. Это глобальная, но приватная структура класса. То есть всё тело класса может обращаться к этой переменной, но ни один код за пределами класса не имеет к ней доступа.

Поэтому переменная, используемая в функции в строке 44, должна быть где-то заполнена. Это место находится здесь, между строками 66 и 94. Правильное заполнение структуры, представленной этой переменной, позволит серверу выполнить именно то, что мы хотим, то есть продажа по рыночной цене или покупка по рыночной цене. Отложенные ордера рассмотрим позже, как и правильный способ изменения уровней тейк-профита и стоп-лосса позиции.

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

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

А теперь у нас есть самое основное. Мы можем начать заполнять структуру. Первое, что нужно сделать, - очистить данные в структуре, что и делается в строке 72. Далее каждая из следующих строк указывает, что именно должен делать сервер. Данный способ заполнения, который показан между строками 73 и 86, подходит как для биржевых, так и для внебиржевых рынков, а также для рынка Форекс. Важно знать, что каждое из этих полей имеет свое значение. Неправильная интерпретация любого из них может лишить нас денег или возможностей. Мы объясним значение каждого из этих полей уже в другой статье, так как при работе с системой отложенных ордеров будет проще разобраться в каждом из них.

В любом случае, созданная информация будет отображаться на терминале, чтобы мы могли проверить ее позже. Для этого используются строки с 88 по 91. Хотя мы могли бы реализовать это по-другому, использование вызова ArrayPrint из стандартной библиотеки MQL5 значительно проще и практичнее, так как все поля представлены достаточно наглядно. И наконец, в строке 93 функция возвращает ответ на запрос.


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

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

Файл Описание
Experts\Expert Advisor.mq5
Демонстрирует взаимодействие между Chart Trade и советником (для взаимодействия требуется Mouse Study)
Indicators\Chart Trade.mq5 Создает окно для настройки отправляемого ордера (для взаимодействия требуется Mouse Study).
Indicators\Market Replay.mq5 Создайте элементы управления для взаимодействия с сервисом репликации/моделирования (для взаимодействия требуется Mouse Study).
Indicators\Mouse Study.mq5 Обеспечивает взаимодействие между графическими элементами управления и пользователем (необходимо как для работы с симулятором воспроизведения, так и на реальном рынке).
Services\Market Replay.mq5 Создает и поддерживает сервис репликации/моделирования рынка (основной файл всей системы).

Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/12589

Прикрепленные файлы |
Anexo.zip (490.53 KB)
Нейросети в трейдинге: Модель темпоральных запросов (TQNet) Нейросети в трейдинге: Модель темпоральных запросов (TQNet)
Фреймворк TQNet открывает новые возможности в моделировании и прогнозировании финансовых временных рядов, сочетая модульность, гибкость и высокую производительность. В статье раскрывается возможность реализации сложных механизмом работы с глобальными корреляциями, включая продвинутые методы инициализации параметров.
Машинное обучение и Data Science (Часть 32): Как поддерживать актуальность AI-моделей с онлайн-обучением Машинное обучение и Data Science (Часть 32): Как поддерживать актуальность AI-моделей с онлайн-обучением
В постоянно меняющемся мире трейдинга адаптация к изменениям на рынке — это просто необходимость. Каждый день появляются новые закономерности и тенденции, из-за чего даже самым продвинутым моделям машинного обучения становится сложно оставаться эффективными в меняющихся условиях. В этой статье мы поговорим о том, как поддерживать актуальность моделей и их способность реагировать на новые рыночные данные с помощью автоматического дообучения.
Изучение MQL5 — от новичка до профи (Часть VII): Принципы отладки приложений MQL Изучение MQL5 — от новичка до профи (Часть VII): Принципы отладки приложений MQL
Исправление ошибок — неотъемлемая часть цикла программирования. В этой статье рассмотрены типовые приемы исправления ошибок (отладки) любого приложения, работающего в среде MetaTrader 5.
Нейросетевой торговый робот на современной архитектуре нейросети Mamba с селективной SSM Нейросетевой торговый робот на современной архитектуре нейросети Mamba с селективной SSM
Статья исследует революционную архитектуру нейронной сети Mamba/SSM для прогнозирования финансовых временных рядов. Представлена полная реализация на MQL5 современной альтернативы Transformer с линейной сложностью O(N) вместо квадратичной O(N²). Детально рассмотрены селективные State Space Models, hardware-aware оптимизации, patching техники и продвинутые методы обучения AdamW. Включены практические результаты тестирования, показавшие увеличение точности с 62% до 71% при снижении времени обучения с 45 до 8 минут. Представлен готовый торговый советник с автообучением и адаптивным риск-менеджментом для MetaTrader 5.