English 中文 Español Deutsch 日本語 Português
preview
Как построить советник, работающий автоматически (Часть 07): Виды счетов (II)

Как построить советник, работающий автоматически (Часть 07): Виды счетов (II)

MetaTrader 5Трейдинг | 20 февраля 2023, 11:20
1 135 0
Daniel Jose
Daniel Jose

Введение

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

В данной статье мы начали с объяснения о том, как можно предотвратить срабатывание отложенных или рыночных ордеров советника. Но хотя показанный там механизм способен поддерживать советника, у нас возникли некоторые другие проблемы, связанные с вопросом взаимодействия между советником и классом C_Orders. И эта проблема в основном заключается в счете NETTING, который будет одной из тем для рассмотрения в этой статье.

Однако, так или иначе, вы НИКОГДА не должны позволять автоматическому советнику работать без присмотра.

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


Новые процедуры взаимодействия между экспертом и классом C_Orders

Всё, о чем говорилось в предыдущих статьях, не будет иметь никакого значения, если у советника по-прежнему будет доступ к классу C_Orders, как показано ниже:

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                C_Orders::CreateOrder(type, Price, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                C_Orders::ToMarket(type, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                if ((m_TicketPending > 0) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                m_TicketPending = C_Orders::CreateOrder(type, Price, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                ulong tmp;
                                
                                if (m_bAccountHedging && (m_Position.Ticket > 0)) return;
                                tmp = C_Orders::ToMarket(type, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                                m_Position.Ticket = (m_bAccountHedging ? tmp : (m_Position.Ticket > 0 ? m_Position.Ticket : tmp));
                                if (PositionSelectByTicket(m_Position.Ticket)) SetInfoPositions(); else m_Position.Ticket = 0;
                        }
//+------------------------------------------------------------------+

Обратите внимание, что сейчас мы установили некоторые правила, чтобы у эксперта не было такой свободы при использовании класса C_Orders. Давайте посмотрим, что здесь происходит; начнем с функции CreateOrderЕсли у нас есть значение в переменной, сообщающей о тикете отложенного ордера, новый ордер будет отклонен, но он также будет отклонен, если мы находимся на счете HEDGING с открытой позицией: всё так просто. Но если данные условия позволяют такое, мы сможем отправить ордер для того, чтобы иметь отложенный ордер в книге, и если запрос будет успешным, то у нас в конце будет тикет ордера, который был размещен, а мы будем лишены возможности отправить новый ордер и последующий вызов.

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


Проблемы со счетом NETTING в автоматическом советнике

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

Проблема заключается в следующем: На счете NETTING торговый сервер будет автоматически создавать среднюю цену позиции по мере того, как цена предложения изменится в позиции покупки или продажи. Если вы работаете с SHORT, так или иначе, средняя цена, а также объем позиции изменятся.

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

Я постараюсь объяснить так, чтобы все поняли, но в идеале вы должны обладать некоторыми знаниями о работе рынка, чтобы вам было легче разобраться. Постараюсь изложить вопрос как можно яснее.

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

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

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

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

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


Ограничение объема сделок в советнике

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

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

Блокировка основана именно на этом. Но здесь есть еще одна деталь: Не существует такого понятия, как 100% надежная блокировка. Всё зависит от трейдера, контролирующего работу советника. Если трейдер по неосторожности отключит блокировку, перезапустив советник, то он несет и должен нести ответственность за любую неисправность, которая может произойти, ведь могло случиться так, что советник растратил часть капитала после того, как трейдер удалил его с графика и поместили обратно на график. В этом случае не существует блокировки, которая могла бы оградить нас от такого поведения.

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

static uint m_StaticLeverage;

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

uint C_Manager::m_StaticLeverage = 0;

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

Но здесь возникает другой не менее важный вопрос: Почему мы используем частную и статическую глобальную переменную в классе C_Manager? Разве нельзя использовать переменную, которая, по крайней мере, не являлась бы статической? Ответ простой - НЕТ, и причина в том, что если по какой-то причине платформа MetaTrader 5 перезапустит советника, то все данные, хранящиеся вне статической переменной, будут потеряны. Не упускайте этот момент, я имею в виду то, что платформа MetaTrader 5 перезапускает советника, а не то, что вы удаляете советника, а затем снова помещаете это на график. Это, безусловно, разные ситуации. Если советник будет удален с графика, а затем переустановлен, то любая информация, даже о статических переменных, будет потеряна. В этих случаях единственным способом остается хранить данные в файле, а затем восстановить их путем чтения того же файла. Перезапуск на самом деле не означает, что советник был удален, это может произойти в нескольких ситуациях, и все они будут связаны с запуском события DeInit, которое вызовет функцию OnDeInit в коде. Этот сброс влияет не только на советника, но и на индикаторы. Поэтому скрипты, размещенные на графике, могут быть удалены в случае активации события DeInit. Поскольку скрипты не сбрасываются (у них нет этого свойства), они просто «выходят» из графика, и MetaTrader 5 не сбрасывает их автоматически..

Теперь нам нужно добавить в код класса определение, чтобы обнаружить максимальное количество раз, когда минимальный объем может быть использован советником. Данное определение показано ниже:

#define def_MAX_LEVERAGE       10

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

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_TicketPending > 0) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                m_TicketPending = C_Orders::CreateOrder(type, Price, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                ulong tmp;
                                
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                tmp = C_Orders::ToMarket(type, m_InfosManager.FinanceStop, m_InfosManager.FinanceTake, m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                                m_Position.Ticket = (m_bAccountHedging ? tmp : (m_Position.Ticket > 0 ? m_Position.Ticket : tmp));
                                if (PositionSelectByTicket(m_Position.Ticket)) SetInfoPositions(); else m_Position.Ticket = 0;
                        }
//+------------------------------------------------------------------+

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

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

inline void UpdatePosition(const bool bSwap = false)
                        {
                                int ret;
                                
                                if ((m_bAccountHedging) && (m_Position.Ticket > 0) && (bSwap)) SetUserError(ERR_Unknown);
                                m_Position.Ticket = ((m_Position.Ticket == 0) && (bSwap) ? m_TicketPending : m_Position.Ticket);
                                m_TicketPending = (bSwap ? 0 : m_TicketPending);
                                if (PositionSelectByTicket(m_Position.Ticket))
                                {
                                        ret = SetInfoPositions();
                                        m_StaticLeverage += (ret > 0 ? ret : 0);
                                }
                        }

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

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

Если bSwap установили в true, это означает, что отложенный ордер прекратил свое существование, и это исправляется здесь. Теперь проведем следующую проверку: Мы проверяем, открыта ли позиция, если да, то мы запустим процедуру, которая обновляет данные. Во время этой процедуры обновления вычисляется разница между объемом до и после обновления, и этот объем возвращается к нам.. Если возвращаемая информация положительна, это укажет на то, что объем или коэффициент плеча увеличился. Подобные вещи очень распространены на счетах NETTING, но на счетах HEDGING возвращаемое значение всегда будет текущим значением плеча. Мы добавим это возвращаемое значение к тому значению, которое ранее было в статической переменной. Таким образом, мы получим способ учета объема, который советник будет использовать или уже использовал за время своей работы.

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

inline void RemovePosition(void)
                        {
                                if (m_Position.Ticket == 0) return;
                                if (PositionSelectByTicket(m_Position.Ticket)) ClosePosition(m_Position.Ticket);
                                ZeroMemory(m_Position);
                        }

Здесь нет ошибки, мы попали в цель: советник закрывает открытую позицию или просто сообщает классу C_Manager, что сервер закрыл позицию, либо потому что лимиты были достигнуты (тейк-профит или стоп-лосс), либо потому что нечто вызвало закрытие позиции, но это не имеет значения. Но если советник ошибочно вызовет эту функцию, не имея открытой позиции, мы просто вернемся назад. Если позиция открыта, то она будет закрыта, и в конце мы удалим все данные, присутствующие в области памяти, где находятся данные позиции.

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

В результате всей этой работы у нас наконец-то появилась система, которая теоретически является стабильной и достаточно надежной, что мы наконец-то можем начать ее автоматизировать, чтобы она могла работать только под наблюдением человека-оператора. Но обратите внимание, что это было сказано ТЕОРЕТИЧЕСКИ, потому что сбои в системе всё равно могут произойти. Но теперь эти проблемы будут проверяться с помощью переменной _LastError, чтобы мы могли проверить, не пошло ли что-то не так. А если сбой серьезный, как, например, при использовании пользовательского перечисления ошибок, рассмотренного в предыдущей статье, мы должны удалить советника из графика.

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

В худшем случае мы их просто закроем или удалим их вручную. Но давайте представим, что у нас 2 или более советников, работающих автоматически на счете вида HEDGING на одном и том же активе, и один из этих советников просто решит «сойти с рельсов», поэтому он и будет удален из графика. Теперь нам следует посмотреть на ордера и позиции, находящиеся на панели инструментов во вкладке "Торговля" (Рисунок 01), и попытаться закрыть сделки, которые открыл или исполнил испорченный советник. В этом случае «испорченный» это не значит, что советник был плохо запрограммирован. Я думаю, что идея понятна.

Рисунок 01

Рисунок 01 - Где нужно искать ордеры и позиции на платформе MetaTrader 5

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


Использование Деструктора

Деструктор - это функция класса, которая на самом деле не похожа на конструктор, вызываемый кодом в любой момент времени; на самом деле конструктор, как и его «коллега» деструктор, вызывается только один раз за время жизни класса. Конструктор вызывается, когда класс «рождается», а деструктор - когда класс «умирает». В обеих ситуациях вы, как программист, вряд ли можете знать, когда класс родится и когда умрёт. Обычно это делает компилятор, но в определенных случаях программист может определить, когда класс «родится» и когда «умрет», используя несколько вещей в коде. Но вызов, чтобы оживить класс, может даже осуществиться благодаря параметрам, которые мы используем в конструкторах, но то же самое нельзя сказать о том, что происходит, когда он «умирает».

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

Независимо от того, когда класс «умирает», мы можем рассказать вам, что делать, когда это произойдет. Таким образом, мы можем сообщить классу C_Manager, что когда он захочет удалить советника с графика, он сделал это и удалял всё, что он создал; другими словами, мы можем закрыть открытую позицию и удалить отложенный ордер, который находится в книге заявок. Но не забудьте подтвердить это, с помощью панели инструментов (рисунок 01), так как может оказаться, что советник был выброшен из графика, а деструктор не смог выполнить свою задачу.

Для этого добавим новое значение в перечисление ошибок, как показывается ниже:

class C_Manager : private C_Orders
{
        enum eErrUser {ERR_Unknown, ERR_Excommunicate};
        private :

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

                ~C_Manager()
                {
                        if (_LastError == (ERR_USER_ERROR_FIRST + ERR_Excommunicate))
                        {
                                if (m_TicketPending > 0) RemoveOrderPendent(m_TicketPending);
                                if (m_Position.Ticket > 0) ClosePosition(m_Position.Ticket);
                                Print("EA was kicked off the chart for making a serious mistake.");
                        }
                }

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

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


Создание уровня допустимости ошибок

Если вы просмотрели весь код в этой и предыдущей статье и сумели понять то, о чем мы рассказываем, тогда вы можете представить, что класс C_Manager «очень жесток» к советнику, он не допускает никаких, даже самых незначительных сбоев. Да, такое действительно происходит, но мы можем немного изменить это: не так, чтобы советник мог совершать ошибки или упущения, однако есть некоторые виды ошибок, которые не так серьёзны, а некоторые даже происходят не по вине советника.

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

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

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

                void CheckToleranceLevel(void)
                        {
                                switch (_LastError)
                                {
                                        case ERR_SUCCESSreturn;
                                        case ERR_USER_ERROR_FIRST + ERR_Unknown:
                                                Print("A serious error has occurred in the EA system. This one cannot continue on the chart.");
                                                SetUserError(ERR_Excommunicate);
                                                ExpertRemove();
                                                break;
                                        default:
                                                Print("A low severity error has occurred in the EA system. Your code is:", _LastError);
                                                ResetLastError();
                                }
                        }

Я здесь не предлагаю окончательного решения, и оно также не является на 100% подходящим, я всего лишь демонстрирую, как нужно действовать, чтобы создать способ допущения ошибок в советнике. В приведенном выше коде мы показываем 2 примера, в которых допуски будут разными.

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

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


Заключение

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

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


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

Возможности Мастера MQL5, которые вам нужно знать (Часть 5): Цепи Маркова Возможности Мастера MQL5, которые вам нужно знать (Часть 5): Цепи Маркова
Цепи Маркова — это мощный математический инструмент, который можно использовать для моделирования и прогнозирования данных временных рядов в различных областях, включая финансы. При моделировании и прогнозировании финансовых временных рядов цепи Маркова часто используются для моделирования эволюции финансовых активов с течением времени, таких как цены акций или обменные курсы. Одними из основных преимуществ моделей цепей Маркова являются их простота и удобство использования.
Как построить советник, работающий автоматически (Часть 06): Виды счетов (I) Как построить советник, работающий автоматически (Часть 06): Виды счетов (I)
Сегодня мы рассмотрим, как создать советник, который просто и безопасно работает в автоматическом режиме. Пока наш советник может работать в любой ситуации, но он ещё не готов к автоматизации, поэтому нам нужно проработать несколько моментов.
Как построить советник, работающий автоматически (Часть 08): OnTradeTransaction Как построить советник, работающий автоматически (Часть 08): OnTradeTransaction
В этой статье я покажу вам, как использовать систему обработки событий, для быстрой и лучшей обработки вопросов, связанных с системой ордеров, чтобы советник работал быстрее. Таким образом, ему не придется постоянно искать информацию.
Алан Эндрюс и его приемы анализа временных рядов Алан Эндрюс и его приемы анализа временных рядов
Алан Эндрюс — один из известнейших "просветителей" современного мира в области трейдинга. Его "вилы" включены практически во все современные программы анализа котировок. Но большинство трейдеров не используют и пятой части тех возможностей, что заложены в этом инструменте. А оригинальный курс Эндрюса включает описание не только вил (хотя они всё же главные), но и некоторых других полезных прямых. Эта статья даёт представление о тех изумительных техниках анализа графиков, которым учил Эндрюс в своем оригинальном курсе. Осторожно, много картинок.