Моделирование рынка (Часть 05): Создание класса C_Orders (II)
Введение
В предыдущей статье, "Моделирование рынка (Часть 04): Создание класса C_Orders (I)", мы в основном сосредоточились на объяснении того, как будет выглядеть код для отправки торговых ордеров на рынок. Цель всех этих объяснений - показать, как можно структурировать код класса так, чтобы декодировать информацию, полученную от индикатора Chart Trade.
Те, кто обладает определенными знаниями, могут реализовать его, даже если не видели исходного кода советника. Хотя может показаться, что код, приведенный в предыдущей статье, не может выполнять операции на реальном торговом сервере, это не совсем так. Данный код не подходит для использования со счетами HEDGING.
На счетах NETTING уже можно открывать и закрывать позиции. Но для HEDGING-аккаунтов это работает немного иначе.
Если мы попытаемся закрыть позицию на покупку с помощью кнопки продажи, мы фактически откроем позицию на продажу. Это происходит на счетах типа HEDGING. На счете типа NETTING это приведет к закрытию позиции на покупку или, по крайней мере, к какому-то эффекту, к примеру:
- Это стало бы изменением позиции, которое имело бы место, если бы мы продали больший объём, чем в позиции на покупку. В этом случае у нас будет уже не позиция на покупку, а позиция на продажу. Итак, объём проданной позиции будет равен разнице между объёмом купленной позиции и объёмом, отправленным на изменение позиции.
- Выполнение частичного закрытия. Этого можно достичь, если объём проданной позиции будет меньше объёма позиции на покупку. В этом случае часть купленного объёма будет закрыта, а позиция на покупку останется открытой с точки зрения рынка.
Всё это довольно интересно и подробно описано в серии статей, которые были опубликованы некоторое время назад. Цель данной серии - подробно рассказать о том, как создать советник, который можно использовать автоматически. Цикл состоит из пятнадцати статей, каждая из которых объясняет аспекты и меры предосторожности, необходимые для разработки такого советника. От модели с ручным управлением до полностью автоматической работы. Если хотите узнать больше, можно поискать статьи на эту тему. Первая из них доступна здесь: "Как построить советник, работающий автоматически (Часть 01): Концепции и структуры".
Здесь будет показан код, большая часть которого взята из этой серии, но фокус будет на другом, поскольку основная цель иная. Другими словами, мы не хотим разрабатывать систему для отправки ордеров или связи с реальным торговым сервером. Что мы хотим сделать (и действительно сделаем), так это разработаем некий смоделированный сервер. Но прежде чем мы это сделаем, нам нужно реализовать несколько моментов. Поскольку идеальным вариантом является возможность использования общих приложений между нашей системой репликации/моделирования и реальным торговым сервером как на демо-счетах, так и на реальных, мы сначала реализуем советник таким образом, чтобы он мог взаимодействовать с реальным сервером. Как только данная реализация будет завершена, мы разработаем моделирование торгового сервера.
Это необходимо для того, чтобы ограничить количество вызовов, которые нам придется использовать. По крайней мере, на данный момент нам не нужно моделировать все вызовы, на которые отвечает реальный сервер, поскольку в большинстве случаев в них нет необходимости. Хотя со временем я могу изменить свое мнение, но пока мы будем делать это так. Таким образом, можно начать с первой темы статьи.
Обработка сообщений для закрытия позиций
Хорошо. Если вы не поняли материал из предыдущей статьи, я вам советую вернуться к нему и сначала разобраться, так как здесь мы лишь продолжим то, что было рассмотрено там. Но, поскольку в структурировании кода были сделаны небольшие улучшения, вы можете увидеть всё это снова ниже. Однако те части, которые описывались в предыдущей статье, здесь рассматриваться не будут, так как проведенная «перестройка» ни в коей мере не отменяет приведенных в ней объяснений. Давайте теперь перейдем к самому коду.
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_Base.MagicNumber; } 011. //+------------------------------------------------------------------+ 012. bool ClosePosition(const ulong ticket) 013. { 014. bool IsBuy; 015. string szContract; 016. 017. if (!PositionSelectByTicket(ticket)) return false; 018. IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY; 019. szContract = PositionGetString(POSITION_SYMBOL); 020. ZeroMemory(m_Base.TradeRequest); 021. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 022. m_Base.TradeRequest.type = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); 023. m_Base.TradeRequest.price = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS)); 024. m_Base.TradeRequest.position = ticket; 025. m_Base.TradeRequest.symbol = szContract; 026. m_Base.TradeRequest.volume = PositionGetDouble(POSITION_VOLUME); 027. m_Base.TradeRequest.deviation = 1000; 028. 029. return SendToPhysicalServer() != 0; 030. }; 031. //+------------------------------------------------------------------+ 032. private : 033. //+------------------------------------------------------------------+ 034. struct stBase 035. { 036. MqlTradeRequest TradeRequest; 037. ulong MagicNumber; 038. bool bTrash; 039. }m_Base; 040. //+------------------------------------------------------------------+ 041. struct stChartTrade 042. { 043. struct stEvent 044. { 045. EnumEvents ev; 046. string szSymbol, 047. szContract; 048. bool IsDayTrade; 049. ushort Leverange; 050. double PointsTake, 051. PointsStop; 052. }Data; 053. //--- 054. bool Decode(const EnumEvents ev, const string sparam) 055. { 056. string Res[]; 057. 058. if (StringSplit(sparam, '?', Res) != 7) return false; 059. stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 060. if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc; 061. else return false; 062. 063. return true; 064. } 065. //--- 066. }m_ChartTrade; 067. //+------------------------------------------------------------------+ 068. ulong SendToPhysicalServer(void) 069. { 070. MqlTradeCheckResult TradeCheck; 071. MqlTradeResult TradeResult; 072. 073. ZeroMemory(TradeCheck); 074. ZeroMemory(TradeResult); 075. if (!OrderCheck(m_Base.TradeRequest, TradeCheck)) 076. { 077. PrintFormat("Order System - Check Error: %d", GetLastError()); 078. return 0; 079. } 080. m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult); 081. if (TradeResult.retcode != TRADE_RETCODE_DONE) 082. { 083. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 084. return 0; 085. }; 086. 087. return TradeResult.order; 088. } 089. //+------------------------------------------------------------------+ 090. ulong ToMarket(const ENUM_ORDER_TYPE type) 091. { 092. double price = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)); 093. double vol = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP); 094. uchar nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS); 095. 096. ZeroMemory(m_Base.TradeRequest); 097. m_Base.TradeRequest.magic = m_Base.MagicNumber; 098. m_Base.TradeRequest.symbol = m_ChartTrade.Data.szContract; 099. m_Base.TradeRequest.price = NormalizeDouble(price, nDigit); 100. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 101. m_Base.TradeRequest.sl = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit); 102. m_Base.TradeRequest.tp = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit); 103. m_Base.TradeRequest.volume = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit); 104. m_Base.TradeRequest.type = type; 105. m_Base.TradeRequest.type_time = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC); 106. m_Base.TradeRequest.stoplimit = 0; 107. m_Base.TradeRequest.expiration = 0; 108. m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN; 109. m_Base.TradeRequest.deviation = 1000; 110. m_Base.TradeRequest.comment = "Order Generated by Experts Advisor."; 111. 112. MqlTradeRequest TradeRequest[1]; 113. 114. TradeRequest[0] = m_Base.TradeRequest; 115. ArrayPrint(TradeRequest); 116. 117. return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0); 118. }; 119. //+------------------------------------------------------------------+ 120. void CloseAllsPosition(void) 121. { 122. for (int count = PositionsTotal() - 1; count >= 0; count--) 123. { 124. if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue; 125. if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue; 126. ClosePosition(PositionGetInteger(POSITION_TICKET)); 127. } 128. }; 129. //+------------------------------------------------------------------+ 130. public : 131. //+------------------------------------------------------------------+ 132. C_Orders(const ulong magic) 133. { 134. m_Base.MagicNumber = magic; 135. } 136. //+------------------------------------------------------------------+ 137. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 138. { 139. switch (id) 140. { 141. case CHARTEVENT_CUSTOM + evChartTradeBuy : 142. case CHARTEVENT_CUSTOM + evChartTradeSell : 143. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 144. if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev) 145. { 146. case evChartTradeBuy: 147. ToMarket(ORDER_TYPE_BUY); 148. break; 149. case evChartTradeSell: 150. ToMarket(ORDER_TYPE_SELL); 151. break; 152. case evChartTradeCloseAll: 153. CloseAllsPosition(); 154. break; 155. } 156. break; 157. } 158. } 159. //+------------------------------------------------------------------+ 160. }; 161. //+------------------------------------------------------------------+
Код заголовочного файла C_Orders.mqh
Вот и он. Код, показанный выше, отвечает на все сообщения, которые индикатор Chart Trade отправляет советнику для связи с торговым сервером. С данного момента все кнопки и элементы управления индикатора Chart Trade становятся функциональными. Можно использовать этот код в любом советнике, если проводить соответствующие настройки, о которых мы расскажем далее в этой статье. Именно в этом и заключается идея. С помощью данного кода можно будет использовать модули, входящие в состав Chart Trade и индикатора Mouse, и сделать свой советник совместимым с уже скомпилированными модулями Chart Trade и индикатора Mouse. Другими словами, можно запрограммировать один раз, проверить, что это работает, и не изменять это дальше. А потом перейти к следующему этапу. В этом и заключается идея.
Но для тех, кто только начинает и не может разобраться в этом коде, давайте посмотрим, что здесь нового. Напомним, что вышеупомянутое изменение в структурировании произошло в строке 34, где мы сгруппировали частные переменные класса в структуру. Но это, как уже говорили, не меняет ничего из того, что уже рассказывалось в предыдущей статье.
Первое, что можно подумать, это: "Как этот класс узнает, какую позицию закрывать, когда мы нажимаем кнопку закрытия позиции на индикаторе Chart Trade? Дело в том, что класс не знает, какую позицию надо закрыть. Можно подумать, что это глупо, потому что при рассмотрении кнопок, та кнопка, которая закрывает все позиции, объявлена как CLOSE ALL POSITIONS. Поэтому можно предположить, что все позиции будут закрыты, верно? Ответ неверный. Потому что это не совсем то, чем будет заниматься данный класс. По крайней мере, не без некоторых изменений в показанном выше коде.
Вполне вероятно, что вы сейчас немного запутались во всем этом. "Если нажатие кнопки CLOSE ALL POSITIONS не закрывает все позиции, почему отображается это сообщение?" Чтобы понять это, необходимо вернуться в прошлое и проанализировать, как на самом деле работает код класса.
В статьях о системе кросс-ордеров:
Мы объяснили, как и почему нам иногда нужно использовать особый тип символа - исторический. Ещё объяснили, что мы должны заставить индикатор Chart Trade сообщать советнику, какой именно символ следует торговать. Это связано с тем, что история, по сути, не является торговым символом, поэтому через нее нельзя отправлять торговые ордеры. Многие из упомянутых исторических символов существуют благодаря контрактам. В тех двух статьях я рассказал о контрактах. Однако здесь проблема значительно усугубляется, поскольку можно использовать график исторического инструмента и попросить советника в любой момент переключиться с полного контракта на мини-контракт. И это приведет к тому, что индикатор Chart Trade изменит торгуемый символ.
Теперь возникает настоящая проблема. Если мы, например, давно используем исторический график доллара и попросили советника отметить, что сделки должны заключаться на полном долларовом контракте, а затем передумали и попросили использовать мини-контракт, советник выдаст сообщение индикатору Chart Trade, чтобы сообщить о смене контракта. Пока всё хорошо. Проблема начинается, когда у нас есть открытая позиция по всему контракту. Если мы скажем советнику, что по каким-то причинам хотим перейти на мини-контракт, даже если у нас открыта позиция по полному контракту, советник просто подчинится. Однако при этом изменится и Chart Trade. Теперь у нас есть проблема. И нам нужно понять эту проблему, чтобы адаптировать код к тому, что нужно сделать.
Если в этот момент Chart Trade указывает, что торгуемым контрактом является мини-контракт, что произойдет, если мы запросим закрывать все открытые позиции? Помните, что открытая позиция сохраняется по всему контракту. Индикатор Chart Trade сгенерирует сообщение для советника. Как уже говорилось в предыдущих статьях, это сообщение будет содержать все данные, необходимые советнику для того, чтобы он знал, что делать. Однако советник не будет знать, что делать. Это происходит так, потому что советник находится на графике, содержимое которого не может быть предметом торговли, т.е. на историческом графике контракта. Чтобы советник знал, что делать, перехватим сообщение в строке 153 из кода. Стоит помнить, что к тому времени, когда это произойдет, сообщение уже будет переведено. Данный перевод был выполнен без каких-либо проблем. Затем код будет направлен в строку 120. И теперь начинается настоящая работа.
В строке 122 у нас есть цикл. В этом цикле будут прочитаны все открытые позиции. Абсолютно все. Но зачем мы это делаем, зачем читать все открытые позиции, даже те, которые не имеют никакого отношения к долларовому контракту в данном примере? Причина в том, что мы не храним тикет, который сервер предоставил нам при открытии позиции. Поэтому нам нужно снова искать этот тикет.
Теперь другой вопрос: почему бы нам не хранить эти тикеты? Это упростило бы работу. Это правда упростило бы её. Однако мы не можем хранить их внутри советника, ведь если на графике произойдет что-то связанное с ним, мы потеряем соответствующую информацию. И нам придется искать их снова. Это будет происходить каждый раз, когда меняется таймфрейм графика или тип контракта.
На данный момент мы так и поступим. Однако, если мы это сделаем, то не сможем просто вызвать процедуру, закрывающую позицию, так как в этом случае мы пройдемся по всем позициям, даже тем, которые не связаны с сообщением Chart Trade.
Таким образом, мы можем фильтровать результаты. Первый фильтр находится в строке 124, где мы проверяем название символа для этой позиции и сравниваем его с названием, предоставленным Chart Trade. А теперь внимание: В нашем примере есть открытая позиция по всему контракту. Но в сообщении Chart Trade указано, что символом является мини-контракт. Поэтому проверка завершится неудачей, поскольку имя символа не совпадает. То же самое бы случилось и в обратной ситуации, то есть если бы у нас была открытая позиция в мини-контракте и мы использовали Chart Trade для закрытия позиций в полном контракте. Данная проверка позволит избежать путаницы.
Теперь необходимо провести одно наблюдение. Хотя может показаться, что это усложняет дело, эта проверка необходима. Даже если мы используем историю контрактов для торговли мини-контрактом или полным контрактом с помощью кросс-ордера, не следует удалять эту галочку из строки 124 из кода, даже если в этот момент мы хотим закрыть все позиции, связанные с долларовым контрактом. Причина проста: без данной проверки при использовании Chart Trade могут быть закрыты позиции даже по другим символам. Это связано с тем, что все открытые позиции будут закрыты.
Однако, если проверка по строке 124 пройдет успешно, мы получим новый тикет. Это делается для того, чтобы изолировать и отфильтровать другой момент. В предыдущей статье мы упоминали, что этот класс будет представлять собой единое целое из-за магического номера. Таким образом, один советник может содержать несколько различных и независимых торговых стратегий. Это разделение будет осуществляться с помощью магического номера.
Однако для счетов NETTING это не имеет смысла, так как на этом виде счета торговый сервер усредняет цену позиции. Тогда у нас будет только одна открытая позиция на символ.
Но в случае с HEDGING-счетами ситуация полностью меняется. В этом случае у нас может быть одновременно несколько открытых позиций, включая на продажу и на покупку на один и тот же актив. И именно в таких случаях имеет смысл отличать данный класс от других, присутствующих в одном и том же советнике. Но, чтобы не усложнять ситуацию, мы не будем вдаваться в подробности о том, как это сделать. Нам просто нужно знать, что это возможно и предусмотрено в данном классе.
Помимо того, что советник открывает позиции, помеченные магическим номером, указывающим на то, кто это сделал, есть и другая возможность: трейдер мог бы открыть позицию вручную. В этом конкретном случае ситуация может стать проблематичной. А это связано с тем, что трейдер может не захотеть закрывать свою позицию при использовании Chart Trade. Советник сравнивает магический номер позиции с магическим номером класса и, если они не совпадают, игнорирует позицию, которая остается открытой. Поэтому, если трейдер открыл позицию вручную, советник проигнорирует её, и она останется открытой, поэтому трейдеру придется закрыть её вручную.
Вы видели, как всё происходит? Хотя это поведение можно изменить, вся система рассчитана на работу с HEDGING-счетами и не вмешивается туда, где ей не место. Если обе проверки прошли успешно, выполняется строка 126, которая фиксирует тикет позиции и вызывает процедуру закрытия указанной позиции. Данная процедура находится в строке 12. Прошу заметить, что она находится в защищенной части кода, т.е. может использоваться через наследование, но не может быть доступна напрямую вне системы наследования. Проще говоря, вызвать данную процедуру непосредственно из кода советника или из любого другого кода будет невозможно.
Вот несколько любопытных моментов, которые вы, возможно, решите убрать из кода в будущем. Дело в следующем: когда этот код вызывается в строке 12, некоторые данные уже известны, например название символа позиции и тикет актива. Однако здесь, в строке 17, мы снова проверяем, действительно ли тикет соответствует открытой позиции. Почему мы снова делаем это здесь? Причина в том, что на данный момент мы не знаем, как другой инструмент, который ещё предстоит создать, будет взаимодействовать с этой процедурой. Однако необходимо использовать библиотечную функцию PositionSelectByTicket, чтобы обновить некоторую информацию, которую мы должны получить от сервера. Данное обновление также было выполнено, когда в строке 124 мы вызываем PositionGetSymbol. Другими словами, мы продублируем некоторые вещи. Но пока что это не проблема.
В любом случае, нужно обновить информацию. Когда мы обновили информацию и тикет соответствует позиции, в строке 18 мы проверим, является ли данная позиция позицией на покупку или продажу. Это очень важно для нас. Затем, в строке 19, мы фиксируем имя символа позиции. Это можно будет изменить в будущем, когда внедрим другой инструмент. Если запрашивать только код этого класса, то данная информация будет совершенно лишней, так как её может предоставить процедура в строке 120. Я не знаю, как эта процедура ClosePosition будет выглядеть в будущем. Далее мы переходим к этапу заполнения формы, то есть к структуре, которая отправится на сервер.
В отличие от процедуры ToMarket в предыдущей статье, где было сказано, что объяснение каждого поля будет дано позже, здесь я могу объяснить основные поля. Важно знать, что каждый из них означает, потому что это нам понадобится в будущем. В строке 21 мы сообщаем серверу, какое действие следует предпринять. Теперь многие зададутся вопросом: "Почему бы нам не использовать действие TRADE_ACTION_CLOSE_BY, если идея заключается в том, чтобы закрыть позицию?" Это интересный вопрос, и основная причина в том, что это счет типа NETTING. Чтобы понять это, необходимо посмотреть на значение в строке 26, где мы указываем серверу используемый объём. Если бы использовалось действие TRADE_ACTION_CLOSE_BY, сервер просто закрыл бы позицию.
Иначе нам пришлось бы создавать целую логику и код, чтобы делать нечто отличающееся от того, что позволяет нам делать NETTING-счет. Эта частичная позиция, когда мы закрываем часть нашей позиции. Я не включаю сюда логику создания частичных позиций или логику смены позиций. Вопрос о том, что будет реализовано, решается самостоятельно. Поскольку мы можем использовать данную систему на любом типе счета, мы должны расставить приоритеты между некоторыми моментами. А поскольку NETTING-счет позволяет делать то, что невозможно на HEDGING-счете, и наоборот, я стараюсь охватить оба типа счетов. Вот почему данное объяснение так важно.
Все остальные поля предназначены для того, чтобы сообщить серверу, что мы собираемся осуществить продажу или покупку, это находится в строке 22. Цена, по которой будет совершена сделка, в строке 23. Используемый тикет позиции находится в строке 24. Имя символа в строке 25. Одна важная деталь: это имя должно совпадать с именем, указанным в тикете, иначе произойдет ошибка. Это ещё помимо объёма, от которого во многом зависит то, что будет сделано, это на строке 26. И, наконец, отклонение в заявленной цене, находится в строке 27. Вся эта информация подскажет серверу, как выполнить запрос, отправленный на рынок, т.е. исполнить сделку по наилучшей возможной цене, изменив при этом объём, или ожидаемое направление движения, или закрыв позицию.
Заметили ли вы, что в отличие от действия TRADE_ACTION_CLOSE_BY, которое только закрывает позицию, здесь мы можем сделать гораздо больше? Поэтому мы используем действие TRADE_ACTION_DEAL. Но это полезно только тогда, когда у нас уже есть открытая позиция. Это не используется для изменения точек стоп-лосса или тейк-профита: это делается с помощью другого действия, о котором мы поговорим в будущем. На данный момент мы будем использовать систему 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. //+------------------------------------------------------------------+
Исходный код советника
Большую часть данного кода мы уже видели в статье Моделирование рынка (Часть 02): Кросс-ордера (II), поэтому для использования класса C_Orders мы внесли всего лишь незначительные изменения. Данные изменения довольно просты. Однако если у вас нет большого опыта программирования на C, некоторые вещи здесь могут показаться странными. Ничего серьезного, но только они отличаются. Первое изменение находится в строке 09, где мы включаем заголовочный файл, о котором шла речь в этой статье. Ещё одно изменение - добавление строки 15, где мы объявляем класс с указателем.
В строке 21 мы инициализируем класс с помощью оператора NEW. Однако, вы можете заметить в этой строке нечто странное. Конструктор класса объявлен для получения 64-битного значения, но мы видим нечто необычное: 0xC0DEDAFE78514269, где есть не только цифры, но и буквы. Что это? На самом деле это не так уж и редко. Это шестнадцатеричное значение. Прошу заметить, что в начале значения стоит 0x; это указывает компилятору, что следующее за ним значение шестнадцатеричное. Это очень полезно в различных ситуациях. Однако, когда мы видим его как магический номер позиции, мы видим нечто совершенно иное, поскольку это десятичное значение, которое представляет собой шестнадцатеричный код. Я оставлю десятичное значение, чтобы вы увидели его позже, не буду раскрывать его здесь.
И, в качестве последнего изменения, у нас есть строка 30. Данная строка будет передавать данные, чтобы класс мог выполнять свою работу. Таким образом, советник сможет обрабатывать сообщения, поступающие от индикатора Chart Trade. Как можно видеть, при очень небольшом количестве кода удалось достичь чего-то интересного, поскольку вся система разделена на модули. Таким образом, при улучшении одного модуля другой не затрагивается напрямую, что позволяет коду расти более безопасным и динамичным образом. По сравнению с монолитной системой, где всё было бы заключено в одном огромном файле, разработка и совершенствование гораздо более утомительны.
Заключительные идеи
В последних двух статьях мы представили советник вместе с кодом класса, отвечающего за отправку ордеров на рынок. Данная система может прекрасно работать на любом типе счета, будь то NETTING или HEDGING, поэтому один и тот же советник можно использовать на форексе, фондовом или внебиржевом рынках. Это делает всё намного проще и доступнее.
Однако данный советник еще не закончен. Многое ещё предстоит сделать, прежде чем его основная функция будет реализована. Его основная роль: моделировать торговый сервер для использования в репликации/моделировании.
Однако до того, как мы это сделаем, нам придется выполнить еще несколько действий. Поэтому не пропустите следующую статью, в которой мы начнем объяснение того, что уже можно сделать даже с этим недоработанным советником. Я хочу начать объяснение сейчас, чтобы упростить ситуацию. Если я отложу это, объяснение может стать слишком сложным. И если это произойдет, вы, возможно, не сможете понять всех деталей.
| Файл | Описание |
|---|---|
| Experts\Expert Advisor.mq5 | Демонстрирует взаимодействие между Chart Trade и советником (для взаимодействия требуется Mouse Study). |
| Indicators\Chart Trade.mq5 | Создает окно для настройки отправляемого ордера (для взаимодействия требуется Mouse Study). |
| Indicators\Market Replay.mq5 | Создайте элементы управления для взаимодействия с сервисом репликации/моделирования (для взаимодействия требуется Mouse Study). |
| Indicators\Mouse Study.mq5 | Позволяет взаимодействовать между графическими элементами управления и пользователем (необходимо как для воспроизведения, так и для торговли на реальном рынке). |
| Servicios\Market Replay.mq5 | Создает и поддерживает сервис репликации/моделирования рынка (основной файл всей системы). |
Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/12598
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
От новичка до эксперта: Мониторинг бэкэнд операций с использованием MQL5
Нейросети в трейдинге: Модели многократного уточнения прогнозов (Основные компоненты)
Нейросети в трейдинге: Модели многократного уточнения прогнозов (Окончание)
От новичка до эксперта: Торговля с использованием уровней Фибоначчи после публикации NFP
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования