Книга: Язык программирования MQL5: Продвинутое использование торговой платформы MetaTrader 5
Назад: Общая структура советника
Дальше: Пример создания эксперта

Функция OnTick ()

Как уже было сказано, в функции OnTick (), код, как правило, перед вычислениями торговой стратегии начинается с различного рода проверок, хотя некоторые проверки можно выполнить и в функции OnInit ().

Информацию о счете клиента можно получить с помощью функций AccountInfoDouble, AccountInfoInteger и AccountInfoString.

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

Для функции AccountInfoInteger это следующие свойства:

ACCOUNT_LOGIN – функция возвращает номер счета.

ACCOUNT_TRADE_MODE – функция возвращает тип торгового счета. Функция возвращает 0 для демонстрационного торгового счета, 1 для конкурсного торгового счета, 2 для реального торгового счета.

ACCOUNT_LEVERAGE – возвращает размер кредитного плеча счета, например, для плеча 1:100, функция вернет 100.

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

ACCOUNT_MARGIN_SO_MODE – в чем задается минимально допустимый уровень залоговых средств, в процентах или в деньгах. Минимально допустимый уровень залоговых средств это уровень залоговых средств, при котором требуется или пополнение счета, или уровень залоговых средств, при достижении которого происходит принудительное закрытие самой убыточной позиции. Минимально допустимый уровень залоговых средств устанавливается брокером и функция возвращает 0, если уровень задается в процентах, и возвращает 1, если уровень задается в деньгах.

ACCOUNT_TRADE_ALLOWED – функция возвращает 0, если для счета запрещена торговля в случае подключения к счету в режиме инвестора, отсутствия соединения к серверу, запрета торговли на стороне сервера, если счет отправлен в архив. Функция возвращает 1, если торговля на счете разрешена.

ACCOUNT_TRADE_EXPERT – функция возвращает 0, если брокер запрещает автоматическую торговлю, и возвращает 1, если автоматическая торговля разрешена.

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

Для этого можно объявить константу, представляющую валидный номер счета и в функции OnInit () сравнить ее с текущим счетом:

const long _ACCOUNT=50009917;



if (AccountInfoInteger (ACCOUNT_LOGIN)!=_ACCOUNT) {

Print («Не соответствует номер счета»);

return (0);

}

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

Print («ACCOUNT_TRADE_MODE», EnumToString ((ENUM_ACCOUNT_TRADE_MODE) AccountInfoInteger (ACCOUNT_TRADE_MODE)));

Свойство ACCOUNT_TRADE_MODE можно использовать для проверки в функции OnInit () запуска эксперта на реальном счете:

if ((ENUM_ACCOUNT_TRADE_MODE) AccountInfoInteger (ACCOUNT_TRADE_MODE) ==ACCOUNT_TRADE_MODE_REAL) {

int mb=MessageBox («Запустить советник на реальном счете?», «Message Box», MB_YESNO|MB_ICONQUESTION);

if (mb==IDNO) return (0);

}

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





Свойство ACCOUNT_LIMIT_ORDERS может быть использовано для проверки и установки максимального количества отложенных ордеров:

bool IsNewOrderAllowed (int _max_orders)

{

int orders=OrdersTotal ();

int max_allowed_orders= (int) AccountInfoInteger (ACCOUNT_LIMIT_ORDERS);

if (max_allowed_orders!=0&&_max_orders> max_allowed_orders) {

_max_orders=max_allowed_orders;

}

return (orders <_max_orders);

}

Объявим входной параметр:

input int max_orders=1; //максимальное количество ордеров

И вызовем определенную нами функцию:

Print (IsNewOrderAllowed (max_orders));

Проверку свойств ACCOUNT_TRADE_ALLOWED и ACCOUNT_TRADE_EXPERT можно организовать в функции OnInit ():

// – — – — – — – — – — – — – — – — – — – — – — – — —

if (!TerminalInfoInteger (TERMINAL_CONNECTED)) {

Alert («No connection to the trade server»);

return (0);

} else {

if (!AccountInfoInteger (ACCOUNT_TRADE_ALLOWED)) {

Alert («Trade for this account is prohibited»);

return (0);

}

}

if (!AccountInfoInteger (ACCOUNT_TRADE_EXPERT)) {

Alert («Trade with the help of experts for the account is prohibited»);

return (0);

}

// – — – — – — – — – — – — – — – — – — – — – — – — —

Дополнительно отдельно проверку соединения с сервером можно сделать в функции OnTick ():

if (!TerminalInfoInteger (TERMINAL_CONNECTED)) {

Alert («No connection to the trade server»);

return;

}

Для функции AccountInfoDouble определены следующие свойства:

ACCOUNT_BALANCE – баланс счета. Соответствует значению Баланс вкладке Торговля клиентского терминала.

Print (AccountInfoDouble (ACCOUNT_BALANCE));

ACCOUNT_CREDIT – размер предоставленного кредита. Типичная ситуация, когда это значение равно 0.

ACCOUNT_PROFIT – размер текущей прибыли на счете. Соответствует столбцу Прибыль во вкладке Торговля клиентского терминала.

Print (AccountInfoDouble (ACCOUNT_PROFIT));

ACCOUNT_EQUITY – значение собственных средств на счете. Соответствует значению Средства вкладке Торговля клиентского терминала.

Print (AccountInfoDouble (ACCOUNT_EQUITY));

ACCOUNT_MARGIN – размер зарезервированных залоговых средств на счете. Соответствует значению Маржа вкладке Торговля клиентского терминала. Если открытых позиций нет, это значение равно 0.

Print (AccountInfoDouble (ACCOUNT_MARGIN));

ACCOUNT_MARGIN_FREE – размер свободных средств на счете, доступных для открытия позиции. Соответствует значению Свободная маржа вкладке Торговля клиентского терминала.

Print (AccountInfoDouble (ACCOUNT_MARGIN_FREE));

ACCOUNT_MARGIN_LEVEL – уровень залоговых средств на счете в процентах. Соответствует значению Уровень маржи вкладке Торговля клиентского терминала. Рассчитывается как Средства/Маржа*100%. Если открытых позиций нет, это значение равно 0.

Print (AccountInfoDouble (ACCOUNT_MARGIN_LEVEL));

ACCOUNT_MARGIN_SO_CALL – уровень залоговых средств, при котором требуется пополнение счета (Margin Call). В зависимости от установленного ACCOUNT_MARGIN_SO_MODE выражается в процентах либо в валюте депозита. Margin Call это скорее информационный сигнал для трейдера, что его счет близок к закрытию, и не сопровождается действиями брокера. Последствия наступают в случае возникновения Stop Out. Например, при ACCOUNT_MARGIN_SO_CALL = 50%, событие Margin Call наступит, когда размер средств на счете станет как половина от маржи.

if ((ENUM_ACCOUNT_STOPOUT_MODE) AccountInfoInteger (ACCOUNT_MARGIN_SO_MODE) ==ACCOUNT_STOPOUT_MODE_PERCENT)

Print (AccountInfoDouble (ACCOUNT_MARGIN_SO_CALL),» %»);

if ((ENUM_ACCOUNT_STOPOUT_MODE) AccountInfoInteger (ACCOUNT_MARGIN_SO_MODE) ==ACCOUNT_STOPOUT_MODE_MONEY)

Print (AccountInfoDouble (ACCOUNT_MARGIN_SO_CALL),» USD»);

ACCOUNT_MARGIN_SO_SO – уровень залоговых средств, при достижении которого происходит принудительное закрытие самой убыточной позиции (Stop Out). В зависимости от установленного ACCOUNT_MARGIN_SO_MODE выражается в процентах либо в валюте депозита. Например, при ACCOUNT_MARGIN_SO_SO = 10%, событие Stop Out наступит, когда размер средств на счете будет 10% от маржи, при этом открытые позиции начнут принудительно закрываться брокером.

if ((ENUM_ACCOUNT_STOPOUT_MODE) AccountInfoInteger (ACCOUNT_MARGIN_SO_MODE) ==ACCOUNT_STOPOUT_MODE_PERCENT)

Print (AccountInfoDouble (ACCOUNT_MARGIN_SO_SO),» %»);

if ((ENUM_ACCOUNT_STOPOUT_MODE) AccountInfoInteger (ACCOUNT_MARGIN_SO_MODE) ==ACCOUNT_STOPOUT_MODE_MONEY)

Print (AccountInfoDouble (ACCOUNT_MARGIN_SO_SO),» USD»);

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

ACCOUNT_MARGIN_MAINTENANCE – размер средств, зарезервированных на счёте, для обеспечения минимальной суммы по всем открытым позициям. Как правило, эта величина равна 0.

ACCOUNT_ASSETS – текущий размер активов на счёте. Как правило, эта величина равна 0.

ACCOUNT_LIABILITIES – текущий размер обязательств на счёте. Как правило, эта величина равна 0.

ACCOUNT_COMMISSION_BLOCKED – текущая сумма заблокированных комиссий по счёту. Как правило, эта величина равна 0.

С помощью свойств функции AccountInfoDouble можно организовать различного рода проверки в функции OnTick () эксперта.

Наступление события Margin Call:

if ((ENUM_ACCOUNT_STOPOUT_MODE) AccountInfoInteger (ACCOUNT_MARGIN_SO_MODE) ==ACCOUNT_STOPOUT_MODE_PERCENT) {

if (AccountInfoDouble (ACCOUNT_MARGIN_LEVEL)!=0&&AccountInfoDouble (ACCOUNT_MARGIN_LEVEL) <=AccountInfoDouble (ACCOUNT_MARGIN_SO_CALL))

Alert («Margin Call!!!»);

}

if ((ENUM_ACCOUNT_STOPOUT_MODE) AccountInfoInteger (ACCOUNT_MARGIN_SO_MODE) ==ACCOUNT_STOPOUT_MODE_MONEY) {

if (AccountInfoDouble (ACCOUNT_EQUITY) <=AccountInfoDouble (ACCOUNT_MARGIN_SO_CALL))

Alert («Margin Call!!!»);

}

Наступление события Stop Out:

if ((ENUM_ACCOUNT_STOPOUT_MODE) AccountInfoInteger (ACCOUNT_MARGIN_SO_MODE) ==ACCOUNT_STOPOUT_MODE_PERCENT) {

if (AccountInfoDouble (ACCOUNT_MARGIN_LEVEL)!=0&&AccountInfoDouble (ACCOUNT_MARGIN_LEVEL) <=AccountInfoDouble (ACCOUNT_MARGIN_SO_SO)) {

Alert («Stop Out!!!»);

return;

}

}

if ((ENUM_ACCOUNT_STOPOUT_MODE) AccountInfoInteger (ACCOUNT_MARGIN_SO_MODE) ==ACCOUNT_STOPOUT_MODE_MONEY) {

if (AccountInfoDouble (ACCOUNT_EQUITY) <=AccountInfoDouble (ACCOUNT_MARGIN_SO_SO)) {

Alert («Stop Out!!!»);

return;

}

}

Проверка размера свободных средств на счете, доступных для открытия позиции:

double margin;

MqlTick last_tick;

ResetLastError ();

if (SymbolInfoTick (Symbol (),last_tick))

{if(OrderCalcMargin(ORDER_TYPE_BUY,Symbol(),Lot,last_tick.ask, margin))

{

if (margin> AccountInfoDouble (ACCOUNT_MARGIN_FREE)) {

Alert («Not enough money in the account!»);

return;

}

}

}

else

{

Print (GetLastError ());

}

Здесь MqlTick это стандартная структура для хранения цен, которая заполняется значениями с помощью функции SymbolInfoTick.

Вызов функции ResetLastError () производится для обнуления ошибки перед вызовом функции, после которой проверяется возникновение ошибки.

Функция OrderCalcMargin вычисляет размер средств, необходимых для открытия позиции. И если размер свободных средств на счете (ACCOUNT_MARGIN_FREE) меньше, чем размер средств, необходимых для открытия позиции, денег на счете недостаточно и торговля невозможна.

Для функции AccountInfoString определены такие свойства как имя клиента ACCOUNT_NAME, имя торгового сервера ACCOUNT_SERVER, валюта депозита ACCOUNT_CURRENCY, имя компании, обслуживающей счет ACCOUNT_COMPANY.

С помощью свойства ACCOUNT_NAME, также как и с помощью свойства ACCOUNT_LOGIN, можно защитить советник:

if (AccountInfoString (ACCOUNT_NAME)!=_name) {

Print («Не соответствует имя пользователя»);

return (0);

}

Информацию о клиентском терминале можно получить с помощью функций TerminalInfoInteger () и TerminalInfoString (). В качестве аргумента эти функции также принимают свойства.

Мы уже видели проверку подключения терминала к серверу с помощью свойства TERMINAL_CONNECTED.

Свойство TERMINAL_DLLS_ALLOWED позволяет выяснить, есть ли разрешение на использование DLL:

Print ((bool) TerminalInfoInteger (TERMINAL_DLLS_ALLOWED));

Файлы DLL это еще один способ создания повторно используемых библиотек – модулей кода для MQL5-программ.

DLL-библиотеки находятся в папке MQL5\Libraries торгового терминала и включаются в код MQL5-программы с помощью команды:

#import «dll_lib. dll»

При этом разрешение на использование DLL-библиотек устанавливается во вкладке Советники настроек клиентского терминала.

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

Свойство TERMINAL_TRADE_ALLOWED показывает, включена ли кнопка авто-торговли в клиентском терминале. Для проверки этого свойства в функцию OnTick () можно включить код:

if (!TerminalInfoInteger (TERMINAL_TRADE_ALLOWED))

Alert («Разрешение на автоматическую торговлю выключено!»);

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

if (!MQLInfoInteger (MQL_TRADE_ALLOWED))

Alert («Автоматическая торговля запрещена в свойствах эксперта», __FILE__);

С помощью свойства SYMBOL_SPREAD функции SymbolInfoInteger можно осуществить контроль над спредом брокера:

double _spread=SymbolInfoInteger (Symbol (),SYMBOL_SPREAD) *MathPow (10, -SymbolInfoInteger (Symbol (),SYMBOL_DIGITS)) /MathPow (10, -4);

if (_spread> spreadLevel) {

Alert («Слишком большой спред!»);

return;

}

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

С помощью свойства SYMBOL_TRADE_MODE функции SymbolInfoInteger можно проверить ограничения на торговые операции по символу, установленные брокером:

if ((ENUM_SYMBOL_TRADE_MODE) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_MODE)!=SYMBOL_TRADE_MODE_FULL) {

Alert («Установлены ограничения на торговые операции»);

return;

}

В функции OnTick () можно ограничить работу эксперта по времени.

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

input int startHour=0; //start hour

input int startMin=0; //start minute

input int stopHour=23; // stop hour

input int stopMin=0; // stop minute





long _seconds=TimeLocal () %86400;

long startSec=3600*startHour+60*startMin;

long stopSec=3600*stopHour+60*stopMin;

if (_seconds <startSec || _seconds> stopSec) return;

Здесь берется локальное время, если вы хотите сравнивать с серверным временем, используйте функцию TimeCurrent ().

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

input datetime startSession=D'2015.05.05»;

input datetime stopSession=D'2015.05.06»;





datetime _session=TimeLocal ();

if (_session <startSession || _session> stopSession) return;

В функции OnTick () эксперта также было бы неплохо проверить, достаточно ли баров в истории для расчета советника. Сделать это можно двумя способами – с помощью функции Bars и с помощью свойства SERIES_BARS_COUNT функции SeriesInfoInteger:

if (Bars (Symbol (), 0) <100)

{

Alert («In the chart little bars, Expert will not work!!»);

return;

}

Или

if (SeriesInfoInteger (Symbol (),0,SERIES_BARS_COUNT) <100)

{

Alert («In the chart little bars, Expert will not work!!»);

return;

}

Ограничить вычисления советника в функции OnTick () по появлению нового бара на графике также можно двумя способами, с помощью свойства SERIES_LASTBAR_DATE функции SeriesInfoInteger или с помощью функции CopyTime:

static datetime last_time;

datetime last_bar_time= (datetime) SeriesInfoInteger (Symbol (),Period (), SERIES_LASTBAR_DATE);

if (last_time!=last_bar_time)

{

last_time=last_bar_time;

} else {

return;

}

Или

static datetime Old_Time;

datetime New_Time [1];

int copied=CopyTime (Symbol (),Period (),0,1,New_Time);

ResetLastError ();

if (copied> 0)

{

if (Old_Time!=New_Time [0])

{

Old_Time=New_Time [0];

} else {

return;

}

}

else

{

Print (GetLastError ());

return;

}

Если вы, предположим, хотите, после того как поймали StopLoss, прекратить на сегодня торговлю советником, вам нужно правильно обработать это StopLoss событие.

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

void OnTrade ()

{

static int _deals;

ulong _ticket=0;





if (HistorySelect (0,TimeCurrent ()))

{

int i=HistoryDealsTotal () -1;





if (_deals!=i) {

_deals=i;

} else {return;}





if ((_ticket=HistoryDealGetTicket (i))> 0)

{

string _comment=HistoryDealGetString (_ticket, DEAL_COMMENT);





if (StringFind (_comment,«sl», 0)!=-1) {

flagStopLoss=true;

}





}

}

}

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

С помощью функции HistoryDealGetTicket получаем тикет последней сделки и, используя свойство DEAL_COMMENT функции HistoryDealGetString, получаем комментарий к сделке. Если комментарий содержит sl, тогда это была сделка закрытия позиции по StopLoss.

flagStopLoss это глобальная переменная, которую мы теперь можем использовать в функции OnTick ():

bool flagStopLoss=false;





static datetime Old_TimeD1;

datetime New_TimeD1 [1];

bool IsNewBarD1=false;





int copiedD1=CopyTime (_Symbol, PERIOD_D1,0,1,New_TimeD1);

ResetLastError ();

if (copiedD1> 0)

{

if (Old_TimeD1!=New_TimeD1 [0])

{

IsNewBarD1=true;

Old_TimeD1=New_TimeD1 [0];

flagStopLoss=false;

}

}

else

{

Print (GetLastError ());

return;

}





if (IsNewBarD1==false)

{

if (flagStopLoss==true) {

return;

}

}

Здесь эксперт прекращает вычисления, если получен StopLoss и не наступил новый дневной бар.

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

bool BuyOpened=false;

bool SellOpened=false;





if (PositionSelect (_Symbol) ==true)

{

if (PositionGetInteger (POSITION_TYPE) ==POSITION_TYPE_BUY)

{

BuyOpened=true;

}

else if (PositionGetInteger (POSITION_TYPE) ==POSITION_TYPE_SELL)

{

SellOpened=true;

}

}

Здесь функция PositionSelect копирует данные об открытой позиции символа в программное окружение. Затем, с помощью свойства POSITION_TYPE функции PositionGetInteger, выясняется, является ли открытая позиция позицией на продажу или покупку.

После выполнения различных проверок в функции OnTick () следуют вычисления сигналов торговой системы эксперта.

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

MqlRates mrate [];

ResetLastError ();

if (CopyRates (Symbol (),Period (),0,3,mrate) <0)

{

Print (GetLastError ());

return;

}

ArraySetAsSeries (mrate, true);

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

Наконец, после вычислений сигналов торговой системы эксперта можно открывать или зарывать позиции.

Но перед совершением сделки было бы неплохо проверить корректность объема, с которым мы собираемся выйти на рынок:

if (Lot <SymbolInfoDouble (Symbol (),SYMBOL_VOLUME_MIN) ||Lot> SymbolInfoDouble (Symbol (),SYMBOL_VOLUME_MAX)) return;

Открытие и закрытие позиции, изменение объема открытой позиции, изменение значения Stop Loss и Take Profit у открытой позиции, установка, модификация и удаление отложенных ордеров, все это может быть сделано с помощью функции OrderSend:

bool OrderSend (

MqlTradeRequest& request, // структура запроса

MqlTradeResult& result // структура ответа

);

Тип торговой операции, которую будет пытаться выполнить функция OrderSend, определяется структурой MqlTradeRequest:

struct MqlTradeRequest

{

ENUM_TRADE_REQUEST_ACTIONS action;

// Тип выполняемого действия

ulong magic;

// Штамп эксперта (идентификатор magic number)

ulong order;

// Тикет ордера

string symbol;

// Имя торгового инструмента

double volume;

// Запрашиваемый объем сделки в лотах

double price;

// Цена

double stoplimit;

// Уровень StopLimit ордера

double sl;

// Уровень Stop Loss ордера

double tp;

// Уровень Take Profit ордера

ulong deviation;

// Максимально приемлемое отклонение от запрашиваемой цены

ENUM_ORDER_TYPE type;

// Тип ордера

ENUM_ORDER_TYPE_FILLING type_filling;

// Тип ордера по исполнению

ENUM_ORDER_TYPE_TIME type_time;

// Тип ордера по времени действия

datetime expiration;

// Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED)

string comment;

// Комментарий к ордеру

};

Первый параметр action структуры MqlTradeRequest определяет тип торговой операции функции OrderSend с помощью перечисления ENUM_TRADE_REQUEST_ACTIONS.

Это может быть немедленное совершение сделки на покупку или продажу (TRADE_ACTION_DEAL), изменение значений Stop Loss и Take Profit у открытой позиции (TRADE_ACTION_SLTP), установка отложенного ордера на покупку или продажу (TRADE_ACTION_PENDING), изменение параметров отложенного ордера (TRADE_ACTION_MODIFY), удаление отложенного ордера (TRADE_ACTION_REMOVE).

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

Это может быть немедленное исполнение (Instant Execution), исполнение по запросу (Request Execution), исполнение по рынку (Market Execution), биржевое исполнение (Exchange Execution).

Выяснить тип исполнения ордера можно с помощью свойства SYMBOL_TRADE_EXEMODE функции SymbolInfoInteger:

Print (EnumToString ((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)));

Для немедленного исполнения (Instant Execution), исполнение рыночного ордера осуществляется по цене, которую вы предлагаете брокеру. Если брокер не может принять ордер по предложенным ценам, он предложит трейдеру новые цены исполнения, которые будут содержаться в структуре MqlTradeResult.

Для немедленного исполнения (Instant Execution), заполнение структуры MqlTradeRequest для ордера на покупку будет иметь следующий вид:

MqlTradeRequest mrequest;

ZeroMemory (mrequest);

MqlTick latest_price;

if (!SymbolInfoTick (_Symbol, latest_price))

{

Alert («Ошибка получения последних котировок – ошибка:», GetLastError (),»!!»);

return;

}

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_INSTANT) {

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest.price = NormalizeDouble(latest_price.ask,_Digits);

mrequest.sl = NormalizeDouble(latest_price.bid – 0.01,_Digits);

mrequest.tp = NormalizeDouble(latest_price.ask +0.01,_Digits);

mrequest.deviation=10;

mrequest. type = ORDER_TYPE_BUY;

mrequest. type_filling = ORDER_FILLING_FOK;

}

Здесь с помощью функции SymbolInfoTick в структуру MqlTick получаются текущие цены символа для предложения их брокеру.

Для немедленного исполнения (Instant Execution) заполняются обязательные поля структуры MqlTradeRequest action, symbol, volume, price, sl, tp, deviation, type, type_filling.

На практике, максимально приемлемое отклонение от запрашиваемой цены deviation, задаваемое в пунктах, которое принимается брокером, не более 5 пунктов. При сильном движении рынка, при поступлении ордера брокеру, если цена ушла на большее значение, произойдет так называемое «Перекотирование» (Requote) – брокер вернет цены, по которым может быть исполнен данный ордер.

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

Для ордера на продажу заполнение Instant Execution обязательных полей структуры MqlTradeRequest будет иметь следующий вид:

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_INSTANT) {

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest.price = NormalizeDouble(latest_price.ask,_Digits);

mrequest.sl = NormalizeDouble(latest_price.ask +0.01,_Digits);

mrequest.tp = NormalizeDouble(latest_price.bid – 0.01,_Digits);

mrequest.deviation=10;

mrequest. type = ORDER_TYPE_SELL;

mrequest. type_filling = ORDER_FILLING_FOK;

}

После заполнения полей структуры MqlTradeRequest рекомендуется проверить ее с помощью функции OrderCheck:

bool OrderCheck (

MqlTradeRequest& request, // структура запроса

MqlTradeCheckResult& result // структура ответа

);

Результаты проверки будут содержаться в структуре MqlTradeCheckResult:

struct MqlTradeCheckResult

{

uint retcode;

// Код ответа

double balance;

// Баланс после совершения сделки

double equity;

// Значение собственных средств после совершения сделки

double profit;

// Плавающая прибыль

double margin;

// Маржевые требования

double margin_free;

// Свободная маржа

double margin_level;

// Уровень маржи

string comment;

// Комментарий к коду ответа (описание ошибки)

};

Функция OrderCheck возвращает true в случае успешной проверки структуры MqlTradeRequest, при этом код retcode будет равен 0, в противном случае функция вернет false:

MqlTradeCheckResult check_result;

ZeroMemory (check_result);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

}

После проверки структуры MqlTradeRequest можно отсылать запрос на совершение торговой операции брокеру, используя функцию OrderSend:

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

MqlTradeRequest mrequest;

MqlTradeCheckResult check_result;

MqlTradeResult mresult;





MqlTick latest_price;

if (!SymbolInfoTick (_Symbol, latest_price))

{

Alert («Ошибка получения последних котировок – ошибка:», GetLastError (),»!!»);

return;

}

if (TradeSignalBuy==true&&BuyOpened==false) {

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_INSTANT) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest.price = NormalizeDouble(latest_price.ask,_Digits);

mrequest.sl = NormalizeDouble(latest_price.bid – 0.01,_Digits);

mrequest.tp = NormalizeDouble(latest_price.ask +0.01,_Digits);

mrequest.deviation=10;

mrequest. type = ORDER_TYPE_BUY;

mrequest. type_filling = ORDER_FILLING_FOK;





ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

Print («Price», mresult.price);

}

else

{

if(mresult.retcode==10004) //Реквота

{

Print («Requote bid ",mresult.bid);

Print («Requote ask ",mresult.ask);

} else {

Print («Retcode ",mresult.retcode);

}

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — —

if (TradeSignalSell==true&&SellOpened==false) {

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_INSTANT) {

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest.price = NormalizeDouble(latest_price.ask,_Digits);

mrequest.sl = NormalizeDouble(latest_price.ask +0.01,_Digits);

mrequest.tp = NormalizeDouble(latest_price.bid – 0.01,_Digits);

mrequest.deviation=10;

mrequest. type = ORDER_TYPE_SELL;

mrequest. type_filling = ORDER_FILLING_FOK;





ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

Print («Price», mresult.price);

}

else

{

if(mresult.retcode==10004) //Реквота

{

Print («Requote bid ",mresult.bid);

Print («Requote ask ",mresult.ask);

} else {

Print («Retcode ",mresult.retcode);

}

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

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

После проверки OrderCheck производится повторная проверка структуры MqlTradeRequest в виде возвращаемого значения функции OrderSend. Далее выполняется проверка кода результата операции структуры MqlTradeResult.

Исполнение ордера по запросу (Request Execution) я лично не встречал у брокеров. Вместо немедленного исполнения (Instant Execution) брокер может предложить исполнение ордера по рынку (Market Execution) или биржевое исполнение (Exchange Execution).

В режиме исполнения по рынку (Market Execution) сделка совершается по цене, предложенной брокером, при этом реквоты отсутствуют. В режиме биржевого исполнения (Exchange Execution) торговые операции якобы выводятся во внешнюю торговую систему и сделки выполняются по текущим рыночным ценам, при этом реквоты также отсутствуют.

При исполнении по рынку (Market Execution) или биржевом исполнении (Exchange Execution) обязательными для заполнения являются поля структуры MqlTradeRequest action, symbol, volume, type, type_filling:

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_EXCHANGE) {

//или SYMBOL_TRADE_EXECUTION_MARKET

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest. type = ORDER_TYPE_BUY;

mrequest. type_filling = ORDER_FILLING_FOK;





ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

// – — – — – — – — – — – —

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_SLTP;

mrequest.symbol = _Symbol;

mrequest.sl = NormalizeDouble(mresult.price – 0.01,_Digits);

mrequest.tp = NormalizeDouble(mresult.price +0.01,_Digits);

ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

Print («SL», mrequest.sl, «TP ",mrequest.tp);

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

// – — – — – — – — – — – — – — – — – — – — —

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

if (((ENUM_SYMBOL_TRADE_EXECUTION) SymbolInfoInteger (Symbol (),SYMBOL_TRADE_EXEMODE)) ==SYMBOL_TRADE_EXECUTION_EXCHANGE) {

//или SYMBOL_TRADE_EXECUTION_MARKET

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_DEAL;

mrequest.symbol = _Symbol;

mrequest. volume = Lot;

mrequest. type = ORDER_TYPE_SELL;

mrequest. type_filling = ORDER_FILLING_FOK;





ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10014) Alert («Неправильный объем в запросе»);

if(check_result.retcode==10019) Alert («Нет достаточных денежных средств для выполнения запроса»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

// – — – — – — – — – — – —

ZeroMemory (mrequest);

mrequest.action = TRADE_ACTION_SLTP;

mrequest.symbol = _Symbol;

mrequest.tp = NormalizeDouble(mresult.price – 0.01,_Digits);

mrequest.sl = NormalizeDouble(mresult.price +0.01,_Digits);

ZeroMemory (check_result);

ZeroMemory (mresult);

if (!OrderCheck (mrequest, check_result))

{

if(check_result.retcode==10015) Alert («Неправильная цена в запросе»);

if(check_result.retcode==10016) Alert («Неправильные стопы в запросе»);

return;

} else {

if (OrderSend (mrequest, mresult)) {

if(mresult.retcode==10009 || mresult.retcode==10008) //запрос выполнен или ордер успешно помещен

{

Print («SL», mrequest.sl, «TP ",mrequest.tp);

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

// – — – — – — – — – — – — – — – — – — – — —

}

else

{

Print («Retcode ",mresult.retcode);

}

} else {

Print («Retcode ",mresult.retcode);

}

}

}

// – — – — – — – — – — – — – — – — – — – — – — – — – — – — – — – —

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

Для установки отложенного ордера на покупку или продажу (TRADE_ACTION_PENDING), требуется указание 11 полей структуры MqlTradeRequest: action, symbol, volume, price, stoplimit, sl, tp, type, type_filling, type_time, expiration. При этом поле type может принимать значения:

ORDER_TYPE_BUY_LIMIT – отложенный ордер на покупку, при этом текущие цены выше цены ордера.

ORDER_TYPE_SELL_LIMIT – отложенный ордер на продажу, при этом текущие цены ниже цены ордера.

ORDER_TYPE_BUY_STOP – отложенный ордер на покупку, при этом текущие цены ниже цены ордера.

ORDER_TYPE_SELL_STOP – отложенный ордер на продажу, при этом текущие цены выше цены ордера.

ORDER_TYPE_BUY_STOP_LIMIT – отложенное выставление отложенного ордера типа Buy Limit для торговле на откате, при этом текущие цены ниже цены ордера.

ORDER_TYPE_SELL_STOP_LIMIT – отложенное выставление отложенного ордера типа Sell Limit для торговли на откате, при этом текущие цены выше цены ордера.

Для изменения параметров отложенного ордера (TRADE_ACTION_MODIFY), требуется указание 7 полей структуры MqlTradeRequest: action, order, price, sl, tp, type_time, expiration. При этом значение поля order берется из структуры MqlTradeResult результата выставления ордера.

Для удаления отложенного ордера (TRADE_ACTION_REMOVE), требуется указание 2 полей структуры MqlTradeRequest: action и order.

Назад: Общая структура советника
Дальше: Пример создания эксперта