English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
preview
Разработка торгового советника с нуля (Часть 22): Новая система ордеров (V)

Разработка торгового советника с нуля (Часть 22): Новая система ордеров (V)

MetaTrader 5Трейдинг | 27 сентября 2022, 16:18
1 516 1
Daniel Jose
Daniel Jose

1.0 - Введение

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

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

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

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

В статье Разработка торгового советника с нуля (Часть 21) система была почти готова, не хватало только части, отвечающей за перемещение ордеров непосредственно внутри графика. Чтобы сделать это я осуществил проверку и заметил некоторые странности в коде: в нем было много повторяющихся частей. Даже при всей осторожности, проявленной для предотвращения подобных повторений, я заметил, что такое происходит. И хуже всего было то, что некоторые вещи работали не так, как надо, например, при использовании очень разных активов, то есть, в основном, когда советник начинал проектироваться, и особенно когда я начал документировать это в статьях, которые публикуются здесь. Я думал об использовании его в основном только при торговле фьючерсами на бирже B3, и в конечном итоге я его разрабатывал, чтобы помочь с торговлей фьючерсами на доллар или индекс, хорошо известными WDO и WIN. Но по мере того, как система становилась всё ближе и ближе к системе, которой я изначально хотел пользоваться, я понял, что могу распространить её на другие рынки или другие активы, и именно в этот момент возникла проблема.


2.0 - Вернуться к чертежной доске

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

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

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

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

Одно наблюдение: эти данные можно получать используя код, с помощью MQL5, но для упрощения работы я буду использовать MetaTrader 5, чтобы показать, где находятся данные.


2.0.1 - Активы компании (АКЦИИ), торгуемые на B3

Давайте посмотрим на следующие два изображения:

     

Выделенные места — это данные, которые нам нужны, чтобы рассчитать, как будет установлена позиция.

Это необходимо знать, потому что, если вы создаете позицию на основе финансового значения, то вам понадобятся эти данные для расчета значений стоп-лосса и тейк-профита. Если вы не используете OCO-ордера, то всё немного проще, но здесь я буду считать, что все ордера будут OCO-ордерами или позициями.

Важный момент: если актив торгуется на рынке c дробными акциями (если есть разница между рынками на B3), то минимальный объем будет 1% от указанного значения, поэтому вместо торговли 100 к 100 мы будем делать 1 к 1, это немного меняет расчет, помните об этой детали.


2.0.2 - Фьючерсные контракты в B3

Правила для фьючерсных контрактов отличаются от правил для акций, объем меняется в зависимости от того, торгуете ли вы с полным контрактом или мини-контрактом, и даже от одного актива к другому, например, если мы собираемся торговать BOI, мы должны посмотреть, как заполняются данные о плече, так как это отличается от того, на чем я собираюсь сосредоточиться. Здесь я остановлюсь на контрактах, поскольку в некоторых случаях полный контракт будет соответствовать 25 мини-контрактам, но это может быть варьироваться, поэтому вам нужно смотреть на правила объема, которые присутствуют на B3 (Бразильская фондовая биржа).

Давайте тогда рассмотрим следующие изображения мини-доллара:

     

У этих контрактов есть срок действия, но для данной статьи это не важно, потому что в статье Разработка торгового советника с нуля (Часть 11) я объяснял, как сделать систему кросс-ордеров, чтобы торговать фьючерсными контрактами прямо из вашей истории, и если вы используете этот метод, вам не придется беспокоиться о том, каким контрактом торгуется в данный момент - советник сам сделает это за вас. Но, если посмотреть на отмеченные точки, то вы увидите, что они отличаются от стоковой системы. Это часто может вызвать некоторые проблемы, но советник до этого момента справлялся с этим, правда, это было не очень интуитивно. Со временем вы привыкнете к нужным значениям плеча для торговли конкретным финансовым объемом.

Однако я решил поднять планку, и тут начинаются трудности.


2.0.3 - FOREX

Моя проблема заключалась в системе Forex, и мой советник не мог с ней справиться. Когда мне удалось разобраться с настройкой плеча на Forex, я не мог пользоваться тем же кодом, что и советник в B3, и это меня беспокоило, потому что я не вижу смысла в том, чтобы иметь два советника с одним лишь отличием в коде. Чтобы понять, что происходит, нам нужно посмотреть на следующие изображения:

     

Вот в чем проблема, в B3 у нас 4 значения, а на Forex 2 значения, пропали 2 значения и из-за этого расчеты между рынками не совпадали.

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

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

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

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


3.0 - Визуализация данных

Прежде всего, мы рассмотрим случай фондового рынка, в частности бразильского фондового рынка (B3). Давайте посмотрим ниже, как всё отображается в советнике и MetaTrader 5.


3.0.1 - Активы B3

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

Если мы используем дробные значения, нам нужно указать только количество дробей, которые мы будем использовать в плече, то есть 50 означает, что мы будем использовать 50 дробей, если мы укажем 15, мы будем использовать 15 дробей и так далее.

Результат этого мы видим чуть ниже в панели инструментов MetaTrader 5. Давайте посмотрим, как был создан ордер с минимальным размером лота. Если мы проанализируем значения тейков и стопов, то увидим, что они совпадают с указанными на графике, а это значит, что здесь советник сработал.


3.0.2 - Мини-доллар

Здесь объем с шагом 1, но в случае полных контрактов значение другое, и я хочу, чтобы мы посмотрели на Chart Trade, значения, указанные как Take и Stop, такие же, как находящиеся выше. Однако, советник корректирует значения правильно, поэтому учитываемые значения - это значения, найденные на индикаторе графика, а значения Chart Trade следует игнорировать, но они будут близки к указанным значениям на графике.


В панели инструментов можно увидеть следующие данные:

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


3.0.3 - Forex

Здесь всё немного сложнее, для тех, кто не знаком с Forex, пункты кажутся довольно странными, но всё же советнику удается с ними разобраться.


И MetaTrader 5 сообщит нам об этой незавершенной операции следующим образом:


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

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


4.0 - Реализация

Давайте начнем с рассмотрения некоторых моментов, которые были изменены в коде.

Во-первых, я убрал систему лимитов, реализованную 3 или 4 версии назад. Я так сделал потому, что иногда советник неправильно адаптировался к системе плеча, тем более, когда он был перенесен с ФОНДОГО РЫНКА на FOREX.

Я добавил новую модель расчета, чтобы советник мог одинаково работать на рынках FOREX и ФОНДОВЫХ РЫНКАХ. Важно, чтобы мы знали, что до этой версии это было невозможно. Советник в начале своей разработки был больше ориентирован на работу и помощь в операциях ФОНДОВОГО РЫНКА, но я решил расширить его функционал еще и для рынка FOREX, поскольку способ торговли и считывания рынка практически одинаков.

Есть подробности, касающиеся проблем с плечом, с которыми советник не мог справиться в предыдущих версиях, но с внесенными изменениями теперь его можно использовать как на FOREX, так и на ФОНДОВОМ РЫНКЕ без каких-либо серьезных изменений кода. Это привело к тому, что я внес в код множество изменений, чтобы советник соответствовал плечу FOREX, сохраняя при этом его совместимость с ФОНДОВЫМ РЫНКОМ.

Можно увидеть одно из таких изменений в начале кода:

int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        Mouse.Init(user50, user51, user52);
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(user20 * Terminal.GetVolumeMinimal(), user21, user22, user23);
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        TradeView.Initilize();
                
        OnTrade();
        EventSetTimer(1);
   
        return INIT_SUCCEEDED;
}

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


4.0.1 Перемещение ордеров прямо на графике

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

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

Проблема в том, что когда были созданы объекты, они заменили класс C_HLineTrade, что можно посмотреть в статье Разработка торгового советника с нуля (Часть 20). Система теперь имеет гораздо более сложную структуру, поэтому, чтобы не показывать снова всю описанную выше картину, мы посмотрим только то, что произошло.

Стрелка указывает на точку соединения, из которой класс C_HLineTrade вышел, чтобы перейти в новые классы. Это позволило нам реализовать то, что вы видели в предыдущих статьях. Но наличие класса C_OrderView мешало развитию, и мы в итоге были вынуждены его исключить. И это еще не всё, что случилось. На самом деле, класс C_TradeGraphics был объединен со старым классом C_OrderView, и они получили новое имя C_IndicatorTradeView. Затем этот класс заменил оба, но в то же время позволил нам разработать систему перемещения ордеров.

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


4.0.1.1 - Написание кода новой системы


После объединения новая система имеет конфигурацию, которую можно увидеть ниже:


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

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

Давайте пока посмотрим на некоторые важные моменты в текущем новом коде. Начнем с мало изученной функции, ее код выглядит так:

void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
#define def_IsBuy(A) ((A == ORDER_TYPE_BUY_LIMIT) || (A == ORDER_TYPE_BUY_STOP) || (A == ORDER_TYPE_BUY_STOP_LIMIT) || (A == ORDER_TYPE_BUY))

        ulong ticket;
        
        if (trans.symbol == Terminal.GetSymbol()) switch (trans.type)
        {
                case TRADE_TRANSACTION_DEAL_ADD:
                case TRADE_TRANSACTION_ORDER_ADD:
                        ticket = trans.order;
                        ticket = (ticket == 0 ? trans.position : ticket);
                        TradeView.IndicatorInfosAdd(ticket);
                        TradeView.UpdateInfosIndicators(0, ticket, trans.price, trans.price_tp, trans.price_sl, trans.volume, (trans.position > 0 ? trans.deal_type == DEAL_TYPE_BUY : def_IsBuy(trans.order_type)));
                        break;
                case TRADE_TRANSACTION_ORDER_DELETE:
                                if (trans.order != trans.position) TradeView.RemoveIndicator(trans.order);
                                else
                                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                                if (!PositionSelectByTicket(trans.position))
                                        TradeView.RemoveIndicator(trans.position);
                        break;
                case TRADE_TRANSACTION_ORDER_UPDATE:
                        TradeView.UpdateInfosIndicators(0, trans.order, trans.price, trans.price_tp, trans.price_sl, trans.volume, def_IsBuy(trans.order_type));
                        break;
                case TRADE_TRANSACTION_POSITION:
                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                        break;
        }
        
        
#undef def_IsBuy
}

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

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

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        TradeView.DispatchMessage(id, lparam, dparam, sparam);
        ChartRedraw();
}

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


4.1 - Класс C_IndicatorTradeView

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

Начнем с функции инициализации, код которой выглядит так:

void Initilize(void)
{
        int orders = OrdersTotal();
        ulong ticket;
        bool isBuy;
        long info;
        double tp, sl;

        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_OBJECT_DESCR, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_DRAG_TRADE_LEVELS, false);
        for (int c0 = 0; c0 <= orders; c0++) if ((ticket = OrderGetTicket(c0)) > 0) if (OrderGetString(ORDER_SYMBOL) == Terminal.GetSymbol())
        {
                info = OrderGetInteger(ORDER_TYPE);
                isBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(-1, ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), OrderGetDouble(ORDER_SL), OrderGetDouble(ORDER_VOLUME_CURRENT), isBuy);
        }
        orders = PositionsTotal();
        for (int c0 = 0; c0 <= orders; c0++) if (PositionGetSymbol(c0) == Terminal.GetSymbol())
        {
                tp = PositionGetDouble(POSITION_TP);
                sl = PositionGetDouble(POSITION_SL);
                ticket = PositionGetInteger(POSITION_TICKET);
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(1, ticket, PositionGetDouble(POSITION_PRICE_OPEN), tp, sl, PositionGetDouble(POSITION_VOLUME), PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
        }
        CreateIndicatorTrade(def_IndicatorTicket0, IT_PENDING);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP);
}

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

Следующий код, на который стоит обратить внимание, показан ниже:

void UpdateInfosIndicators(char test, ulong ticket, double pr, double tp, double sl, double vol, bool isBuy)
{
        bool isPending;
                                
        isPending = (test > 0 ? false : (test < 0 ? true : (ticket == def_IndicatorTicket0 ? true : OrderSelect(ticket))));
        PositionAxlePrice(ticket, (isPending ? IT_RESULT : IT_PENDING), 0);
        PositionAxlePrice(ticket, (isPending ? IT_PENDING : IT_RESULT), pr);
        SetTextValue(ticket, (isPending ? IT_PENDING : IT_RESULT), vol);
        PositionAxlePrice(ticket, IT_TAKE, tp);
        PositionAxlePrice(ticket, IT_STOP, sl);
        SetTextValue(ticket, IT_TAKE, vol, (isBuy ? tp - pr : pr - tp));
        SetTextValue(ticket, IT_STOP, vol, (isBuy ? sl - pr : pr - sl));
}

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

Ниже показываем следующую функцию.

inline double SecureChannelPosition(void)
{
        double Res = 0, sl, profit, bid, ask;
        ulong ticket;
                                
        bid = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_BID);
        ask = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_ASK);
        for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                ticket = PositionGetInteger(POSITION_TICKET);
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), profit = PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                sl = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                        if (ask < sl) ClosePosition(ticket);
                }else
                {
                        if ((bid > sl) && (sl > 0)) ClosePosition(ticket);
                }
                Res += profit;
        }
        return Res;
};

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

Давайте посмотрим на функцию, выделенную в приведенном выше фрагменте и которую можно увидеть ниже.

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;
                                
        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0 / Terminal.GetVolumeMinimal(), def_ColorVolumeEdit);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        break;
        }
}

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

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

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;

// ... Код ....

        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Clique esquerdo
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT Pressionada
                        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL Pressionada
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        Mouse.Hide();
                                        bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange);
                                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                                        bMounting = true;
                                }
                                tp = price + (bKeyBuy ? valueTp : (-valueTp));
                                sl = price + (bKeyBuy ? (-valueSl) : valueSl);
                                UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy);
                                if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT);
                        }else if (bMounting)
                        {
                                UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false);
                                Mouse.Show();
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell))
                        {
                                if (bEClick)
                                {
                                        bIsMove = false;
                                        m_TradeLine.SpotLight();
                                }
                                MoveSelection(price, mKeys);
                        }
                        break;

// ... Код ...
                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {

// ... Код ...

                                case EV_MOVE:
                                        if (bIsMove)
                                        {
                                                m_TradeLine.SpotLight();
                                                bIsMove = false;
                                        }else
                                        {
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                                bIsMove = true;
                                        }
                                        break;
                        }
                        break;
        }
}

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

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

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

void MoveSelection(double price, uint keys)
{
        static string memStr = NULL;
        static ulong ticket = 0;
        static eIndicatorTrade it;
        eEventType ev;
        double tp, sl, pr;
        bool isPending;
                                
        string sz0 = m_TradeLine.GetObjectSelected();
        
        if (sz0 != NULL)
        {
                if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev);
                isPending = OrderSelect(ticket);
                switch (it)
                {
                        case IT_TAKE:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP));
                                else ModifyPosition(ticket, price, macroGetPrice(IT_STOP));
                                break;
                        case IT_STOP:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price);
                                else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price);
                                break;
                        case IT_PENDING:
                                pr = macroGetPrice(IT_PENDING);
                                tp = macroGetPrice(IT_TAKE);
                                sl = macroGetPrice(IT_STOP);
                                ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr));
                                break;
                }
        };
}

Я называю эту функцию балластом, потому что ОНА ОТВЕТСТВЕННА ЗА ТО, ЧТОБЫ СИСТЕМА ПОЗИЦИОНИРОВАНИЯ БЫЛА МЕДЛЕННОЙ... если вы не понимаете, посмотрите на выделенные точки. Каждая из них является функцией, которая находится внутри класса C_Router и которая будет отправлять запрос в торговый сервер, поэтому если сервер по той или иной причине будет медленно отвечать (а это всегда будет происходить из-за задержки), система позиционирования будет более или менее медленной, но если сервер будет реагировать быстро, то система будет плавной, а точнее, всё произойдет более гладко. Позже мы изменим это, потому что эта система не позволяет нам делать что-либо еще. Необходимо принять во внимание, что таким образом мы будем работать более безопасным способом, тем более если вам нравится торговать в движениях большой волатильности, где цены могут смещаться очень быстро, но даже в этом случае мы рискуем, что лимиты «скакнут», но другого выхода нет, потому что нужно чем-то жертвовать. Для тех, кто соглашается торговать, зная, что это точно будет находиться внутри сервера, советник уже готов на этом этапе; а для тех, кто хочет добиться плавности в работе советника, даже ценой несоблюдения точек, для них в следующих статьях всё изменится. Так будет интереснее.

В следующем видео я покажу, как это работает на самом деле, поэтому обратите внимание на значения на графике и на панели инструментов, где показано, что происходит с ордером.



5.0 - Заключение

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

И не забывайте: если вы обнаружите, что перемещение ордеров или лимитов слишком медленное, вы можете удалить точки, которые я показал в статье, и использовать сам MetaTrader 5 для перемещения ордеров или лимитов, а также использовать советник в качестве поддержки, чтобы помочь в считывании и интерпретации данных. Делать это или нет - уже ваш выбор...


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

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Anatoli Kazharski
Anatoli Kazharski | 27 сент. 2022 в 16:56

Забыли, что в статье должны быть скриншоты только на английском языке. Всегда такие правила были.

Разработка торгового советника с нуля (Часть 23): Новая система ордеров (VI) Разработка торгового советника с нуля (Часть 23): Новая система ордеров (VI)
Мы сделаем систему ордеров более гибкой. Здесь я покажу вам, как и где внести изменения в код, чтобы делать его более гибким, что позволит нам намного быстрее изменять лимиты позиций.
DoEasy. Элементы управления (Часть 19): Прокрутка вкладок в элементе TabControl, события WinForms-объектов DoEasy. Элементы управления (Часть 19): Прокрутка вкладок в элементе TabControl, события WinForms-объектов
В статье создадим функционал для прокрутки заголовков вкладок в элементе управления TabControl при помощи кнопок управления прокруткой. Функционал будет работать для расположения заголовков вкладок в одну строку с любой из сторон элемента управления.
DoEasy. Элементы управления (Часть 20): WinForms-объект SplitContainer DoEasy. Элементы управления (Часть 20): WinForms-объект SplitContainer
Сегодня начнём разрабатывать элемент управления SplitContainer из набора элементов MS Visual Studio. Этот элемент состоит из двух панелей, разделённых вертикальным или горизонтальным перемещаемым разделителем.
Нейросети — это просто (Часть 30): Генетические алгоритмы Нейросети — это просто (Часть 30): Генетические алгоритмы
Сегодня я хочу познакомить Вас с немного иным методом обучения. Можно сказать, что он заимствован из теории эволюции Дарвина. Наверное, он менее контролируем в сравнении с рассмотренными ранее методами. Но при этом позволяет обучать и недифференцируемые модели.