Введение

В предыдущих статьях: Создание советника, работающего автоматически (Часть 06): Типы счетов (I) и Создание советника, работающего автоматически (Часть 07): Типы счетов (II), мы сосредоточились на важности осторожности при разработке Советник, торгующий автоматически.

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

Рисунок 01

Рисунок 01. Поток сообщений

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

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

В случае со 100% автоматическим советником нам еще нужны некоторые вещи. А для советника с минимальной автоматизацией нам еще нужно добавить некоторые детали. Все будет происходить между советником и классом C_Manager. Мы не будем добавлять код ни в какую другую часть советника. Ну, есть еще одна вещь. В 100% автоматизированном советнике нам придется удалить класс C_Mouse (а для 100% автоматизированного советника он бесполезен). Очень важно понимать, что такое поток сообщений, так как без этого мы не сможем перейти к дальнейшим темам.

Добавление функций управления и специальных возможностей

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

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

inline  void  EraseTicketPendig(const  ulong  ticket)
                        {
                                m_TicketPending = (ticket == m_TicketPending ? 0  : m_TicketPending);
                        }

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

Следующая новая функция показана ниже:

                void  PedingToPosition(void )
                        {
                                ResetLastError ();
                                if  ((m_bAccountHedging) && (m_Position.Ticket > 0 )) SetUserError (ERR_Unknown);
                                        else  m_Position.Ticket = (m_Position.Ticket == 0  ? m_TicketPending : m_Position.Ticket);
                                m_TicketPending = 0 ; 
                                if  (_LastError  != ERR_SUCCESS ) UpdatePosition(m_Position.Ticket); 
                                CheckToleranceLevel(); 
                        }

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

                void  UpdatePosition(const  ulong  ticket)
                        {
                                int  ret;
                                
                                if  ((ticket == 0 ) || (ticket != m_Position.Ticket)) return ;
                                if  (PositionSelectByTicket (m_Position.Ticket))
                                {
                                        ret = SetInfoPositions();
                                        m_StaticLeverage += (ret > 0  ? ret : 0 );
                                }else  ZeroMemory (m_Position);
                                ResetLastError ();
                        }

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

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

Рисунок 02

Рисунок 02. Поток сообщений с новыми функциями

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

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

Основываясь на этом понимании, давайте рассмотрим код советника в его текущем состоянии, прежде чем он станет автоматизированным. Но для тех, кто не видел, код советника не претерпел серьезных изменений со времени последней статьи, в которой он появился, а именно «Создание советника, работающего автоматически» (часть 05): ручные триггеры (II). Единственные изменения, которые действительно были внесены, показаны ниже:

int  OnInit ()
{
        manager = new  C_Manager(def_MAGIC_NUMBER, user03, user02, user01, user04, user08);
        mouse = new  C_Mouse(user05, user06, user07, user03, user02, user01);
        (*manager).CheckToleranceLevel(); 

        return  INIT_SUCCEEDED ;
}

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

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

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

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

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

Это первый пункт: в первую очередь используйте язык MQL5 для решения задач. Если этого как-то недостаточно, добавьте конкретный функционал. Вы можете использовать другой язык, например C/C++ или даже Python, но сначала попробуйте использовать MQL5.

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

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

Основываясь на этих 3 пунктах, у нас есть 3 варианта на выбор, чтобы советник взаимодействовал с классом C_Manager или любым другим классом, которому необходимо получать данные, предоставляемые Метатрейдер 5 платформа. Первый вариант — использовать триггер события для каждого нового полученного билета. Это событие вызовет OnTick функция. Однако, искренне, я не рекомендую использовать эту функцию. Мы увидим причину в другой раз.

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

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

Что такое OnTradeTransaction и для чего он нужен?

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

Чтобы было проще объяснить, хотя бы на этом начальном этапе, давайте посмотрим на код функции в советнике:

void  OnTradeTransaction (const  MqlTradeTransaction  &trans, const  MqlTradeRequest  &request, const  MqlTradeResult  &result)
{
        switch  (trans.type) 
        {
                case  TRADE_TRANSACTION_POSITION :
                        manager.UpdatePosition(trans.position);
                        break ;
                case  TRADE_TRANSACTION_ORDER_DELETE :
                        if  (trans.order == trans.position)...