Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Книга по написанию робота под MT4.docx
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
484 Кб
Скачать
☆

Формат функции OrderSelect():

bool OrderSelect ( int index, int select, int pool=MODE_TRADES )

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

Ордер/позицию можно выбрать двумя способами:

  1. по номеру тикера;

  2. по порядковому номеру ордера/позиции в списке.

Если Вы знаете тикер ордера или позиции, то в качестве параметра select надо передать значение SELECT_BY_TICKET, а в качестве параметра index - номер тикера ордера или позиции. Третий параметр в этом случае не используется и его можно опустить.

Пример выбора по тикеру:

//---- выбираем ордер/позицию с тикером 77777

if ( OrderSelect (77777, SELECT_BY_TICKET) == true)

Print("Ордер #77777 выбран успешно");

else

Print("OrderSelect() вернул ошибку - ",GetLastError());

Если Вы не знаете тикера, то существует другой способ выбрать ордер или позицию. Для этого Вам надо выбрать ордер или позицию из списка открытых позиций или неудаленных отложенных ордеров (значение параметра pool равно MODE_TRADES) или из списка закрытых позиций или удаленных ордеров (параметр pool равен MODE_HISTORY). В обоих случаях параметр select должен быть равен SELECT_BY_POS. В параметре index Вы должны указать порядковый номер этой позиции или ордера в списке открытых позиций или неудаленных отложенных ордеров (pool равен MODE_TRADES) или в списке закрытых позиций и удаленных отложенных ордеров (pool равен MODE_HISTORY).

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

int pos;

int total = OrdersTotal();

for ( pos = 0; pos<total; pos++ )

{

if (OrderSelect(pos, SELECT_BY_POS, MODE_TRADES) == true)

{

Print("Выбран ордер номер ", pos, " в списке открытых позиций");

// делаем что-то с этой позицией

}

else

Print("Ошибка ", GetLastError(), " при выборе ордера номер ", pos);

}

В этом примере для нас новой является функция OrdersTotal(). Эта функция выводит количество открытых позиций и неудаленных отложенных ордеров. Ее, а также функцию HistoryTotal(), я рассмотрю в следующих выпусках.

HistoryTotal(): количество закрытых позиций и удаленных ордеров

В выпуске про OrderSelect() - функцию выбора ордера или позиции - я рассказал, что можно также выбрать закрытую позицию или удаленный отложенный ордер, указав в качестве параметра select значение SELECT_BY_POS, а в качестве pool - MODE_HISTORY. В качестве первого параметра (index) функции OrderSelect() в этом случае указывается номер позиции/ордера в этом списке.

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

Формат функции HistoryTotal():

int HistoryTotal ()

Эта функция возвращает количество закрытых позиций или удаленных отложенных ордеров, которые загруженны в клиентском терминале MetaTrader 4 в соответствии с текущими настройками вкладки "История счета".

Пример использования HistoryTotal():

int pos;

int total = HistoryTotal();

for ( pos = 0; pos<total; pos++ )

{

if (OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY) == true)

{

Print("Выбран ордер номер ", pos, " в списке закрытых позиций");

// делаем что-то с этой позицией

}

else

Print("Ошибка ", GetLastError(), " при выборе ордера номер ", pos);

}

В следующих выпусках я расскажу, какую информацию мы можем получить после выделения позиции или ордера с помощью функции OrderSelect().

OrderClosePrice() - цена закрытия выделенного ордера или позиции

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

Формат функции OrderClosePrice():

double OrderClosePrice()

Функция OrderClosePrice() возвращает цену закрытия ордера или позиции, выделенной с помощью функции OrderSelect().

Рассмотрим пример использования функции OrderClosePrice(). Предположим, что мы знаем, что позиция закрылась по ордеру и нам надо определить по какому: по Stop Loss или Take Profit:

//---- выбираем ордер/позицию с тикером 77777

if ( OrderSelect (77777, SELECT_BY_TICKET) == true)

{

// позиция выбрана успешно

if ( OrderClosePrice() == OrderStopLoss() )

{

// позиция закрылась по Stop Loss

// ...

}

else

{

// позиция закрылась по Take Profit

// ...

}

}

else

Print("OrderSelect() вернул ошибку - ",GetLastError());

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

В этом примере Вам незнакома функция OrderStopLoss(). Эта функция возвращает значение Stop Loss выделенного ордера или позиции. Подробно эту функцию мы рассмотрим в следующем выпуске.

OrderStopLoss() - цена закрытия выделенного ордера или позиции

В прошлом выпуске, рассказывая о функции OrderClosePrice(), я рассмотрел пример, как определить по какому ордеру (Stop Loss или Take Profit) была закрыта позиция, если точно известно, что она была закрыта по ордеру.

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

Для того, чтобы изучение функции OrderStopLoss() стало не просто скучным теоретическим описанием, попробуем слегка изменить прошлый пример, чтобы получить максимум практической пользы.

Прошлый наш пример работал только в тех случаях, когда Stop Loss был сработан без проскальзывания. Изменим немного наш код:

//---- выбираем ордер/позицию с тикером 77777

if ( OrderSelect (77777, SELECT_BY_TICKET) == true)

{

// позиция выбрана успешно

if ( ((OrderClosePrice() <= OrderStopLoss()) && (OrderType() == OP_BUY)) ||

((OrderClosePrice() >= OrderStopLoss()) && (OrderType() == OP_SELL)) )

{

// позиция закрылась по Stop Loss

// ...

}

else

{

// позиция закрылась по Take Profit

// ...

}

}

else

Print("OrderSelect() вернул ошибку - ",GetLastError());

Этот пример базируется на том, что:

  • Если позиция была на покупку (условие OrderType() == OP_BUY истинно), то Stop Loss сработает или по цене ордера, или хуже, т.е. ниже: (OrderClosePrice() <= OrderStopLoss()).

  • Если позиция была на продажу (условие OrderType() == OP_SELL истинно), то Stop Loss сработает или по цене ордера, или хуже, т.е. выше: (OrderClosePrice() >= OrderStopLoss()).

Таким образом, в любом из этих случаех мы можем быть уверены, что позиция закрылась по Stop Loss ордеру.

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

Получение типа ордера с помощью OrderType()

В прошлых выпусках мы научились получать информацию о выделенном с помощью функции OrderSelect() ордере или позиции:

  • Stop Loss (функция OrderStopLoss);

  • Цену закрытия (функция OrderClosePrice).

В этом выпуске я расскажу о функции OrderType().

int OrderType()

Функция OrderType() возвращает значение, которое определяет тип операции ордера, который предварительно выбран с помощью OrderSelect():

  • OP_BUY - позиция на покупку,

  • OP_SELL - позиция на продажу,

  • OP_BUYLIMIT - отложенный ордер Buy Limit,

  • OP_BUYSTOP - отложенный ордер Buy Stop,

  • OP_SELLLIMIT - отложенный ордер Sell Limit,

  • OP_SELLSTOP - отложенный ордер Sell Stop.

В предыдущем выпуске я поместил пример, которые определял, как была закрыта позиция - по Stop Loss ордеру или по Take Profit. Вместе с тем в этом примере есть небольшая ошибка: если выделенный ордер является отложенным ордером, то этот кусочек кода будет ошибочно считать, что позиция закрылась по Take Profit. Изменим наш пример, чтобы исключить эту ошибку:

//---- выбираем ордер/позицию с тикером 77777

if ( OrderSelect (77777, SELECT_BY_TICKET) == true)

{

if ((OrderType() == OP_BUY) || (OrderType() == OP_SELL))

{

// позиция выбрана успешно

if ( ((OrderClosePrice() <= OrderStopLoss()) && (OrderType() == OP_BUY)) ||

((OrderClosePrice() >= OrderStopLoss()) && (OrderType() == OP_SELL)) )

{

// позиция закрылась по Stop Loss

// ...

}

else

{

// позиция закрылась по Take Profit

// ...

}

}

else

Print("Ошибка: это не закрытая позиция, а отложенный ордер");

}

else

Print("OrderSelect() вернул ошибку - ",GetLastError());

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

if ((OrderType() == OP_BUY) || (OrderType() == OP_SELL))

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

OrderCloseTime() - время закрытия позиции

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

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

datetime OrderCloseTime()

Функцию можно вызвать после того, как ордер или позиция были предварительно выбраны с помощью функции OrderSelect().

Возвращает дату и время закрытия позиции или ордера:

  • Если позиция открыта, то возвращает 0 (ноль).

  • Если позиция закрыта, то возвращает дату и время закрытия позиции.

  • Если отложенный ордер удален, то возвращает дату и время удаления ордера.

  • Если отложенный ордер еще не удален, то возвращает 0 (ноль).

Исходя из этих правил нам надо проверить время закрытия выделенной позиции. Если оно равно нулю, то это открытая позиция и нам надо сообщить об ошибке:

//---- выбираем ордер/позицию с тикером 77777

if ( OrderSelect (77777, SELECT_BY_TICKET) == true)

{

// это позиция, а не отложеный ордер?

if ((OrderType() == OP_BUY) || (OrderType() == OP_SELL))

{

// это закрытая позиция, а не открытая?

if ( OrderCloseTime() != 0 )

{

if ( ((OrderClosePrice() <= OrderStopLoss()) && (OrderType() == OP_BUY)) ||

((OrderClosePrice() >= OrderStopLoss()) && (OrderType() == OP_SELL)) )

{

// позиция закрылась по Stop Loss

// ...

}

else

{

// позиция закрылась по Take Profit

// ...

}

}

else

Print("Ошибка: это не закрытая позиция, а открытая");

}

else

Print("Ошибка: это не закрытая позиция, а отложенный ордер");

}

else

Print("OrderSelect() вернул ошибку - ",GetLastError());

То, что это не может быть отложенный ордер, мы уже проверили (см. прошлый выпуск).

Теперь наш пример по определению типа ордера, по которому была закрыта позиция, стал содержать на одну ошибку меньше, но по-прежнему остается небезгрешным. Мы почему-то с первых строчек кода безусловно верим в тот факт, что позиция закрыта именно по Stop Loss или Take Profit ордера. А если нет?

Об этом мы поговорим в следующем выпуске, когда будем рассматривать функцию OrderTakeProfit().

OrderTakeProfit() - уровень Take Profit ордера

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

  • OrderSelect(),

  • OrdersTotal(),

  • HistoryTotal(),

  • OrderClosePrice(),

  • OrderStopLoss(),

  • OrderType(),

  • OrderCloseTime().

В этом выпуске мы изучим функцию OrderTakeProfit() и доработаем наш пример. Мы почему-то с первых строчек кода безусловно верим в тот факт, что позиция была закрыта именно по Stop Loss или Take Profit ордера. А если нет? Это легко проверить.

  • Если для позиции на покупку цена закрытия находится не ниже уровня Take Profit, то позиция закрыта по Take Profit.

  • Если для позиции на продажу цена закрытия находится не выше уровня Take Profit, то позиция закрыта по Take Profit.

Мы знаем, что уровень Stop Loss ордера выделенной позиции или ордера можно определить с помощью функции OrderStopLoss(). По аналогии уровень Take Profit определяется с помощью функции OrderTakeProfit():

double OrderTakeProfit()

Ордер должен быть предварительно выбран с помощью функции OrderSelect().

Изменим наш пример, чтобы убедиться, что позиция закрыта по ордеру, а не с рынка:

//---- выбираем ордер/позицию с тикером 77777

if ( OrderSelect (77777, SELECT_BY_TICKET) == true)

{

// это позиция, а не отложеный ордер?

if ((OrderType() == OP_BUY) || (OrderType() == OP_SELL))

{

// это закрытая позиция, а не открытая?

if ( OrderCloseTime() != 0 )

{

if ( ((OrderClosePrice() <= OrderStopLoss()) && (OrderType() == OP_BUY)) ||

((OrderClosePrice() >= OrderStopLoss()) && (OrderType() == OP_SELL)) )

{

// позиция закрылась по Stop Loss

// ...

}

else

{

if ( ((OrderClosePrice() >= OrderTakeProfit()) && (OrderType() == OP_BUY)) ||

((OrderClosePrice() <= OrderTakeProfit()) && (OrderType() == OP_SELL)) )

{

// позиция закрылась по Take Profit

// ...

}

else

Print("Ошибка: позиция закрыта не по ордеру");

}

}

else

Print("Ошибка: это не закрытая позиция, а открытая");

}

else

Print("Ошибка: это не закрытая позиция, а отложенный ордер");

}

else

Print("OrderSelect() вернул ошибку - ",GetLastError());

В следующем выпуске я продолжу изучение торговых функций языка MetaQuotes Language 4 и расскажу о функции OrderSymbol().

OrderSymbol() - инструмент выделенного ордера

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

Используя функцию OrderSymbol(), мы можем получить информация о выделенном с помощью функции OrderSelect() ордере или позиции.

string OrderSymbol()

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

int pos;

for (pos=0; pos<OrdersTotal(); pos++)

{

// выделим ордер

if (OrderSelect(pos, SELECT_BY_POS, MODE_TRADES) == true)

{

// ордер выбран; проверим является ли этот ордер отложенным

if (OrderType()>=OP_BUYLIMIT)

{

// ордер оказался отложенным; проверим инструмент

if (OrderSymbol()==Symbol())

{

// делаем что-то с ордером

// ...

}

}

}

else

Print("Ошибка ", GetLastError(), " при выборе ордера номер ", pos);

В этом коде Вам могут быть непонятны два момента.

Во-первых, я как-то странно на первый взгляд провожу определение типа ордера: if (OrderType()>=OP_BUYLIMIT). На самом деле условие OrderType()>=OP_BUYLIMIT будет истинно в тех случаях, когда выбран ордер, и ложно в тех случаях, когда выбрана позиция. Дело в том, что константы, определяющие тип ордера, имеют следующие числовые значения:

Константа

Значение

Описание

OP_BUY

0

Позиция на покупку

OP_SELL

1

Продажа

OP_BUYLIMIT

2

Отложенный ордер BUY LIMIT

OP_SELLLIMIT

3

Отложенный ордер SELL LIMIT

OP_BUYSTOP

4

Отложенный ордер BUY STOP

OP_SELLSTOP

5

Отложенный ордер SELL STOP

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

Второй непонятный момент в коде - функция Symbol(). Эта функция возвращает текстовую строку с именем финансового инструмента, к которому прикреплен советник:

string Symbol()

Все остальное Вы уже на данный момент знаете:

  • целочисленные переменные (int),

  • цикл for,

  • функцию выделения ордера OrderSelect(),

  • функцию получения типа выделенного ордера OrderType(),

  • функцию печати информации в журнал экспертов Print().

В следующем выпуске я продолжу рассмотрение торговых функций.

OrderComment() - комментарий выделенного ордера

В прошлых выпусках мы решали задачу определения того, была ли закрыта позиция по Stop Loss или Take Profit ордеру. Существует еще один способ узнать это. При этом данный способ будет более простым, чем рассмотренные ранее.

В этом нам поможет функция OrderComment().

string OrderComment()

После того как ордер или позиция выбраны функцией OrderSelect() вызов функции OrderComment() вернет комментарии этого ордера/позиции в виде строки.

Очень часто сервер пишет в комментарий ордера или позиции полезные вещи:

  • [sl] - если сработал Stop Loss;

  • [tp] - если сработал Take Profit;

  • cancelled - если отложенный ордер был отменен клиентом;

  • deleted [no money] - если отложенный ордер должен был сработать, но был удален сервером из-за недостатка свободных средств на счете;

  • partial close - если позиция была закрыта частично;

  • close hedge by #тикер - если локированная позиция была закрыта полностью другой локированной позицией;

  • [число%] - позиция была закрыта по Stop Out (т.е. сервер автоматически закрыл позицию, когда equity стало меньше 20% необходимой маржи);

  • deleted [good for day] - удаленный отложенный ордер по CFD, который имел статус Day Order;

  • expiration [YYYY.MM.DD HH:MM] - отложенный ордер, который был удален сервером при достижении даты эксперации YYYY.MM.DD HH:MM;

  • split from #тикер - при закрытии одной позицией другой с разными объемами появляется в рынке новая позиция с таким комментарием; #тикер - номер тикера позиции с бОльшим объемом.

Может быть этот список неполон, но в данный момент нас это не должно волновать. Нас интересуют те позиции и ордера, в комментарии которых встречается подстрока [sl] (позиция закрыта по Stop Loss ордеру) или подстрока [tp] (позиция закрыта по Take Profit ордеру):

//---- выбираем ордер/позицию с тикером 77777

if ( OrderSelect (77777, SELECT_BY_TICKET) == true)

{

// это позиция, а не отложеный ордер?

if ((OrderType() == OP_BUY) || (OrderType() == OP_SELL))

{

// это закрытая позиция, а не открытая?

if ( OrderCloseTime() != 0 )

{

if ( StringFind(OrderComment(), "[sl]") != -1 )

{

// позиция закрылась по Stop Loss

// ...

}

else

{

if ( StringFind(OrderComment(), "[tp]") != -1 )

{

// позиция закрылась по Take Profit

// ...

}

else

Print("Ошибка: позиция закрыта не по ордеру");

}

}

else

Print("Ошибка: это не закрытая позиция, а открытая");

}

else

Print("Ошибка: это не закрытая позиция, а отложенный ордер");

}

else

Print("OrderSelect() вернул ошибку - ",GetLastError());

В этом примере есть неизвестная пока нам функция - StringFind():

int StringFind ( string text, string matched_text, int start=0 )

Эта функция ищет в строке text подстроку matched_text начиная с позиции start (если третий параметр не указан, то поиск идет с нулевой позиции, т.е. с первого элемента). Если подстрока не найдена, то функция возвращает -1; если найдена - номер позиции в строке, с которой начинается искомая подстрока.

В следующем выпуске я продолжу рассмотрение торговых функций.

OrderSwap() - начисленный сторидж по выделенному ордеру

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

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

double OrderSwap()

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

В следующем выпуске я расскажу о функции OrderCommission().

OrderCommission() - комиссия выделенного функцией OrderSelect() ордера

Вчерашний и сегодняшний выпуски посвящены не очень нужным с практической точки зрения торговым функциям: OrderSwap() и OrderCommission().

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

double OrderCommission()

Функция OrderCommission() возвращает удержанную комиссию по ордеру, выделенному функцией OrderSelect().

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

OrderOpenPrice() - получение информации о цене открытии позиции или цене отложенного ордера

В этом выпуске я рассмотрю функцию OrderOpenPrice(). В качестве практического примера применения этой функции рассмотрим установку и управление трейлинг стопом.

Напомню, что Trailing Stop (трейлинг стоп) – это алгоритм управления уровнем Stop Loss ордера. После выставления трейлинг-стопа (например, на Х пипсов) происходит следующее:

  • MetaTrader не предпринимает никаких действий до того момента, пока по открытой позиции не образуется прибыль в Х пипсов. После этого MetaTrader выставляет Stop Loss ордер на расстоянии Х пипсов от текущей цены (в данном случае - на уровне безубыточности).

  • После выполнения первого шага MetaTrader посылает команду на изменение уровня Stop Loss ордера на расстояние Х пипсов от текущей котировки каждый раз, когда расстояние между ней и старым уровнем ордера превысит Х пипсов. В результате этого Stop Loss ордер "подтягивается" к текущей цене.

Реализуем этот принцип на языке MetaQuotes Language 4. Будем полагать, что открытая позиция уже выбрана и мы точно знаем, что эта позиция открыта по инструменту, к которому прикреплен эксперт. Также предположим, что значение трейлинг стопа в пипсах содержится в переменной TrailingStop.

int err;

if (OrderType() == OP_BUY)

{

// позиция на покупку

if ((Bid-OrderOpenPrice())>=(TrailingStop*Point))

{

// выставляем Stop Loss

if (OrderModify(OrderTicket(), OrderOpenPrice(), Bid-TrailingStop*Point,

OrderTakeProfit(), 0))

Print("#", OrderTicket(),": trailing stop ", Bid-TrailingStop*Point);

else

{

err = GetLastError();

Print("#", OrderTicket(),": trailing stop error ", err);

}

}

}

else

{

// позиция на продажу

if ((OrderOpenPrice()-Ask)>=(TrailingStop*Point))

{

// выставляем Stop Loss

if (OrderModify(OrderTicket(), OrderOpenPrice(), Ask+TrailingStop*Point,

OrderTakeProfit(), 0))

Print("#", OrderTicket(),": trailing stop ", Ask+TrailingStop*Point);

else

{

err = GetLastError();

Print("#", OrderTicket(),": trailing stop error ", err);

}

}

}

Прежде всего нас интересует новая функция OrderOpenPrice():

double OrderOpenPrice()

Эта функция возвращает цену открытия для выбранного функцией OrderSelect() ордера или позиции.

В этом коде Вам могут быть непонятны еще несколько функций:

  • OrderTicket() - возвращает тикер выделенного функцией OrderSelect() ордера или позиции;

  • Bid - Bid последней котировки по инструменту, к которому прикреплен эксперт;

  • Ask - Ask последней котировки по инструменту, к которому прикреплен советник;

  • Point - размер пункта текущего инструмента в валюте котировки (для EURUSD это 0.0001, для USDJPY- 0.01 и т.д.).

В следующем выпуске я расскажу о функции OrderTicket().

OrderTicket() - тикер выделенного ордера

В прошлом выпуске я рассказал о реализации алгоритма трейлинг стопа на языке MetaQuotes Language 4.

int err;

if (OrderType() == OP_BUY)

{

// позиция на покупку

if ((Bid-OrderOpenPrice())>=(TrailingStop*Point))

{

// выставляем Stop Loss

if (OrderModify(OrderTicket(), OrderOpenPrice(), Bid-TrailingStop*Point,

OrderTakeProfit(), 0))

Print("#", OrderTicket(),": trailing stop ", Bid-TrailingStop*Point);

else

{

err = GetLastError();

Print("#", OrderTicket(),": trailing stop error ", err);

}

}

}

else

{

// позиция на продажу

if ((OrderOpenPrice()-Ask)>=(TrailingStop*Point))

{

// выставляем Stop Loss

if (OrderModify(OrderTicket(), OrderOpenPrice(), Ask+TrailingStop*Point,

OrderTakeProfit(), 0))

Print("#", OrderTicket(),": trailing stop ", Ask+TrailingStop*Point);

else

{

err = GetLastError();

Print("#", OrderTicket(),": trailing stop error ", err);

}

}

}

В этом примере нами была использована функция OrderTicket():

int OrderTicket()

Эта функция возвращает тикер выделенного с помощью функции OrderSelect() ордера или позиции.

В следующем выпуске я продолжу рассмотрение торговых функций.

OrderProfit() - прибыль выделенного ордера

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

Сегодня я расскажу о функции OrderProfit().

double OrderProfit()

Функция OrderProfit() возвращает прибыль для ордера, выделенного с помощью функции OrderSelect().

Предположим, что мы хотим подсчитать прибыль, которую мы получили по закрытым позициям.

int profit = 0;

int pos;

for ( pos = 0; pos<HistoryTotal(); pos++ )

{

// выделим позицию

if (OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY) == true)

{

// это не отложенный ордер?

if ( OrderType() <= OP_SELL) profit += OrderProfit();

}

else

Print("Ошибка ", GetLastError(), " при выделении ордера ", OrderTicket());

}

Print("Суммарный профит по закрытым позициям = ", profit);

В следующем выпуске я продолжу рассмотрение торговых функций и расскажу о функции OrderOpenTime().

OrderOpenTime() - время открытия ордера или позиции, выделенного функцией OrderSelect()

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

С помощью функции OrderOpenTime() мы можем получить дату и время открытия ордера или позиции, выделенного предварительно с помощью функции OrderSelect().

datetime OrderOpenTime()

  • В случае отложенного ордера функция OrderOpenTime() возвращает дату и время выставления ордера.

  • В случае открытой или закрытой позиции функция возвращает дату и время открытия позиции.

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

В этом нам помогут четыре новых функции:

  • datetime CurTime() - время прихода последней котировки в виде количества секунд, прошедших после 00:00 1 января 1970 года;

  • int TimeHour(datetime time) - возвращает порядковый номер часа для времени time;

  • int TimeMinute(datetime time) - возвращает порядковый номер минуты для времени time;

  • int TimeSeconds(datetime time) - возвращает количество секунд с начала минуты для времени time.

Например, для 02:16:05 функции TimeHour, TimeMinute и TimeSeconds вернут соответственно 2, 16 и 5.

Мы помним, что значение типа datetime представляет собой количество секунд, прошедшее с 00:00 1 января 1970 года. Также мы помним, что в часе 60*60 секунд, а в минуте 60 секунд. Поэтому, зная текущее время (мы можем получить его с помощью CutTime), мы можем получить время текущего дня на 00:00:00 часов:

int c_time = CurTime();

datetime day_start;

day_start=c_time-TimeHour(c_time)*60*60-TimeMinute(c_time)*60-TimeSeconds(c_time);

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

//---- вычислим начало дня - переменная day_start

int c_time = CurTime();

datetime day_start;

day_start=c_time-TimeHour(c_time)*60*60-TimeMinute(c_time)*60-TimeSeconds(c_time);

//---- подсчитаем прибыль

int profit = 0;

int pos;

for ( pos = 0; pos<HistoryTotal(); pos++ )

{

// выделим позицию

if (OrderSelect(pos, SELECT_BY_POS, MODE_HISTORY) == true)

{

// это не отложенный ордер?

if ( OrderType() <= OP_SELL)

{

// ордер был открыт сегодня?

if (OrderOpenTime()>=day_start) profit += OrderProfit();

}

}

else

Print("Ошибка ", GetLastError(), " при выделении ордера ", OrderTicket());

}

Print("Суммарный профит по закрытым позициям = ", profit);

В следующем выпуске я продолжу рассмотрение торговых функций и расскажу о функции OrderLots().

OrderLots() - количество лотов в выделенном ордере или позиции

В последующих функциях мы рассмотрим торговые функции, с помощью которых можно открывать/закрывать позиции, размещать/модифицировать и удалять ордера:

  • OrderClose()

  • OrderCloseBy()

  • OrderDelete()

  • OrderModify()

  • OrderSend()

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

double OrderLots()

Функция возвращает количество лотов в выделенном ордере или позиции.

Как я уже сказал, основное применение функции - передать количество лотов в качестве параметра в функции OrderClose() и OrderModify().

Забегая вперед, расскажу о формате функции OrderClose():

bool OrderClose(int ticket, double lots, double price, int slippage, color Color=CLR_NONE)

Где:

  • ticket - тикер ордера или позиции;

  • lots - количество лотов для закрытия позиции;

  • price - цена закрытия;

  • slippage - значение максимального проскальзывания в пунктах;

  • color - цвет стрелки закрытия на графике. Если параметр отсутствует или его значение равно CLR_NONE, то стрелка на графике не отображается.

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

// закрыть позицию с тикером 77777 по текущей цене

// предположим, что позиция уже выделена с помощью OrderSelect

// и мы точно знаем, что это открытая позиция

if (OrderType() == OP_SELL)

OrderClose(OrderTicket(), OrderLots(), Ask, 3);

else

OrderClose(OrderTicket(), OrderLots(), Bid, 3);

В этом примере нам встретились две предопределенные переменные типа double:

  • Bid - бид последней котировки по текущему инструменту (к которому прикреплен советник);

  • Ask - аск последней котировки по текущему инструменту.

В следующем выпуске я продолжу рассмотрение торговых функций.

OrderExpiration() - дата истечения отложенного ордера

Перед тем, как я перейду к рассмотрению торговых функций для открытия/закрытия позиции или управления ордерами, мне осталось рассказать о двух функциях: OrderExpiration() и OrderMagicNumber().

В этом выпуске я расскажу о торговой функции OrderExpiration().

datetime OrderExpiration()

Эта функция возвращает дату эксперации (истечения) отложенного ордера. Если до указанного времени ордер не сработал, то он автоматически удаляется из системы. Ордер должен быть предварительно выбран с помощью функции OrderSelect().

Если дата истечения не установлена (т.е. ордер может существовать бесконечное время, пока не исполнится или не будет удален), то OrderExpiration() вернет 0.

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

В качестве примера использования функции OrderExpiration() напишем кусок кода, которых пробегает по всем неудаленным отложенным ордерам и если дата истечения не установлена, то устанавливает ее на 23:59:59 ближайшей пятницы.

// вычислим 23:59:59 ближайшей пятницы

datetime friday;

datetime c_time;

c_time = CurTime();

// в friday - дата и время на 00:00:00 текущего дня

friday = c_time-TimeHour(c_time)*60*60-TimeMinute(c_time)*60-TimeSeconds(c_time);

// в friday - дата и время на 00:00:00 ближайшей пятницы

friday = friday + (5-TimeDayOfWeek(c_time))*60*60*24;

// в friday - дата и время на 23:59:59 ближайшей пятницы

friday = friday + 23*60*60 + 59*60 + 59;

// пробежимся по отложенным ордерам и изменим эксперацию

int pos;

for(pos=0; pos<OrdersTotal(); pos++)

{

// выберем ордер или позицию

if (OrderSelect(pos, SELECT_BY_POS, MODE_TRADES) == true)

{

// это отложенный ордер?

if (OrderType()>OP_SELL)

{

// дата эксперации отсутствует?

if (OrderExpiration()==0)

// изменить дату эксперации

OrderModify(OrderTicket(), OrderOpenPrice(), OrderStopLoss(),

OrderTakeProfit(), friday);

}

}

else

Print("Ошибка ", GetLastError(), " при выборе ордера номер ", pos);

}

В этом коде есть несколько неизвестных нам функций:

  • int TimeDayOfWeek(datetime date) - возвращает день недели (0-Воскресенье,1,2,3,4,5,6) для указанной даты;

  • datetime CurTime() - время прихода последней котировки в виде количества секунд, прошедших после 00:00 1 января 1970 года;

  • int TimeHour(datetime time) - возвращает порядковый номер часа для времени time;

  • int TimeMinute(datetime time) - возвращает порядковый номер минуты для времени time;

  • int TimeSeconds(datetime time) - возвращает количество секунд с начала минуты для времени time.

Например, для 02:16:05 среды функции TimeHour, TimeMinute и TimeSeconds вернут соответственно 2, 16 и 5. А функция TimeDayOfWeek вернет 3.

Мы помним, что значение типа datetime представляет собой количество секунд, прошедшее с 00:00 1 января 1970 года. Также мы помним, что в часе 60*60 секунд, а в минуте 60 секунд. Исходя из этого становится понятной наша логика получения в переменной friday даты и времени, которые соответствуют 23:59:59 ближайшей пятницы:

// вычислим 23:59:59 ближайшей пятницы

datetime friday;

datetime c_time;

c_time = CurTime();

// в friday - дата и время на 00:00:00 текущего дня

friday = c_time-TimeHour(c_time)*60*60-TimeMinute(c_time)*60-TimeSeconds(c_time);

// в friday - дата и время на 00:00:00 ближайшей пятницы

friday = friday + (5-TimeDayOfWeek(c_time))*60*60*24;

// в friday - дата и время на 23:59:59 ближайшей пятницы

friday = friday + 23*60*60 + 59*60 + 59;

В следующем выпуске я расскажу о функции OrderMagicNumber().

OrderMagicNumber() - идентификационное (магическое) число для выбранного ордера

Очень часто трейдеру бывает нужно как-то идентифицировать ордера или позиции. Например, полезно знать, открыт ли данный ордер именно этим экспертом или на каком периоде графика сработал торговый сигнал. Разработчики MetaTrader 4 предусмотрели для таких случаев очень полезную торговую функцию - OrderMagicNumber().

int OrderMagicNumber()

Функция OrderMagicNumber() возвращает целое число, которое было установлено для этого ордера функцией OrderSend() в параметре magic.

Немного забегая вперед, расскажу о формате функции OrderSend():

int OrderSend(string symbol, int cmd, double volume, double price, int slippage,

double stoploss, double takeprofit, string comment=NULL,

int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)

Функция OrderSend() предназначена для размещения отложенного ордера или открытия позиции. В следующем выпуске я расскажу о ней подробнее.

Как я уже говорил ранее, магическое число для ордера или позиции передается в функцию через параметр magic.

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

Для этих целей нам подойдет следующий формат магического числа: последние 2 цифры числа - ID эсперта, первые цифры - период графика.

В языке MetaQuotes Language существуют следующие константы, которые обозначают периоды графиков:

Константа

Значение

Описание

PERIOD_M1

1

1 минута

PERIOD_M5

5

5 минут

PERIOD_M15

15

15 минут

PERIOD_M30

30

30 минут

PERIOD_H1

60

1 час

PERIOD_H4

240

4 часа

PERIOD_D1

1440

1 день

PERIOD_W1

10080

1 неделя

PERIOD_MN1

43200

1 месяц

0 (ноль)

0

Период текущего графика

Табл. 1. Периоды графиков

Исходя из этого мы можем описать 3 функции:

// получить магическое число из периода графика и ID эксперта

int GetMagicNumber( int chart_period, int expertID )

{

return( chart_period * 100 + expertID );

}

// получить период графика из магического числа

int GetPeriod( int magic )

{

return( magic / 100 );

}

// получить ID эксперта из магического числа

int GetExpertID( int magic )

{

return( magic % 100 );

}

В следующем выпуске я расскажу о функции OrderSend(). С помощью этой функции можно открывать позиции и размещать отложенные ордера.

OrderSend() - открытие позиции или размещение отложенного ордера

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

Сегодня я расскажу о первой такой функции - функции OrderSend().

Формат функции:

int OrderSend(string symbol, int cmd, double volume, double price, int slippage,

double stoploss, double takeprofit, string comment=NULL,

int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)

Функция OrderSend() предназначена для размещения отложенного ордера или открытия позиции. Функция вовзращает номер тикера открытой позиции или размещенного отложенного ордера. В случае неудачи функция OrderSend() возвращает число -1.

При этом:

  • symbol - инструмент, по которому будет открыта позиция или размещен отложенный ордер;

  • cmd - тип ордера (см. таблицу 1);

  • volume - объем в лотах;

  • price - цена открытия позиции или уровень отложенного ордера;

  • slippage - максимально допустимое отклонение между price и ценой сервера, при которым позиция будет открыта (для установки отложенных ордеров величина параметра slippage значения не имеет);

  • stoploss - уровень Stop Loss;

  • takeprofit - уровень Take Profit;

  • comment - комментарий к ордеру или позиции (впоследствии это поле может быть изменено сервером - см. выпуск "OrderComment() - комментарий выделенного ордера");

  • magic - магическое число ордера (может быть впоследствии получено функцией OrderMagicNumber());

  • expiration - дата и время истечения отложенного ордера (если к этой дате и времени отложенный ордер не сработает, то он будет удален - см. выпуск "OrderExpiration() - дата истечения отложенного ордера");

  • arrow_color - цвет открывающей стрелки на графике. Если параметр отсутствует или его значение равно CLR_NONE, то открывающая стрелка не отображается на графике.

В следующем выпуске я рассмотрю параметры функции OrderSend() более подробно.

Параметры функции OrderSend()

В прошлом выпуске я рассказал о функции OrderSend(). В этом выпуске я хотел бы остановиться на параметрах этой функции более подробно.

В параметре cmd мы передаем тип приказа:

Константа

Значение

Описание

OP_BUY

0

Открыть позицию на покупку

OP_SELL

1

Открыть позицию на продажу

OP_BUYLIMIT

2

Разместить отложенный ордер BUY LIMIT

OP_SELLLIMIT

3

Разместить отложенный ордер SELL LIMIT

OP_BUYSTOP

4

Разместить отложенный ордер BUY STOP

OP_SELLSTOP

5

Разместить отложенный ордер SELL STOP

Таблица 1. Возможные значения параметра cmd функции OrderSend()

Таким образом, для того, чтобы открыть позицию на продажу в качестве параметра cmd надо указать OP_SELL. Для открытия позиции на покупку - OP_BUY. Для установки отложенного ордера надо использовать значения OP_BUYLIMIT, OP_SELLLIMIT, OP_BUYSTOP или OP_SELLSTOP в зависимости от типа размещаемого отложенного ордера.

При открытии позиции в качестве параметра price надо использовать текущий Bid (если cmd равен OP_SELL) или текущий Ask (если cmd равен OP_BUY):

  • функция Bid возвращает текущий Bid по инструменту, к которому "прикреплен" эксперт;

  • функция Ask возвращает текущий Ask по инструменту, к которому "прикреплен" эксперт;

  • функция MarketInfo(string symbol, int type) c параметром type, равным MODE_BID или MODE_ASK, возвращает текущий Bid или Ask по инструменту, который передан ей в качестве параметра symbol.

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

Для того, чтобы "нормализовать" цену, надо использовать функцию NormalizeDouble():

double NormalizeDouble ( double value, int digits )

Эта функция округляет вещественное число value с точностью до digits знаков после запятой. Число цифр после десятичной точки должно быть в диапазоне 0 .. 8.

В случае использования "неправильной" цены в параметре price будут выданы следующие коды ошибок:

  • ERR_INVALID_PRICE (129) - если цена не была "нормализована" или такой цены вообще не было в потоке;

  • ERR_REQUOTE (138) - если цена сильно устарела (независимо от значения параметра slippage);

Если же цена устарела, но еще присутствует в потоке, то будет совершена сделка по текущей цене, если текущая цена находится в диапазоне price +/- slippage.

В следующем выпуске я расскажу об оставшихся параметрах функции OrderSend().

Параметры функции OrderSend() - продолжение

В прошлых выпусках я рассказал о функции OrderSend() и начал рассмотрение ее параметров. В прошлом выпуске мною были рассмотрены параметры cmd и price.

В этом выпуске я расскажу о параметрах StopLoss, TakeProfit и expiration.

Когда Вы открываете позициию (выставляете отложенный ордер), то ордера Stop Loss и Take Profit должны находиться по отношению к текущей цене (цене отложенного ордера) не ближе, чем на расстоянии определенного количества пунктов. Например, если Вы имеет счет в Дилинговом центре "Альпари", то Вы не можете выставлять Stop Loss и Take Profit ордера на FOREX ближе одного спрэда к текущей цене для открытой позиции или к цене отложенного ордера.

Если Вы попытаетесь разместить Stop Loss или Take Profit ближе, чем это дозволено, то функция OrderSend() вернет ошибку 130 (ERR_INVALID_STOPS).

Если Вы не знаете этого минимально допустимого значения, то Вы всегда можете его получить с помощью функции MarketInfo(). Функция MarketInfo(string symbol, int type) c параметром type, равным MODE_STOPLEVEL, возвращает это значение по инструменту, который передан ей в качестве параметра symbol.

Инструмент графика, к которому прикреплен эксперт, можно получить с помощью функции Symbol():

string Symbol()

Если функция OrderSend() возратила ошибку 147 (ERR_TRADE_EXPIRATION_DENIED), это означает, что на торговом сервере запрещены отложенные ордера с установленной датой эксперации. В этом случае следует в дальнейшем использовать функцию OrderSend() с параметром expiration, равным нулю. Дилинговый центр "Альпари" разрешает своим клиентам устанавливать любую дату и время, когда неисполненный отложенный ордер будет удален автоматически.

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

Пример использования функции OrderSend() можно найти в нашем первом эксперте:

MyOrderTicket = OrderSend(Symbol(), OP_SELL, LotsNumber, Bid, 3, 0, 0,

NULL, 0, 0, CLR_NONE);

if (MyOrderTicket<0)

{

err = GetLastError();

Print("Ошибка при открытии позиции: ", err);

MyOrderTicket = 0;

}

В следующем выпуске я расскажу о примере использования функции OrderSend().

Пример использования функции OrderSend()

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

Советник должен в определенное время (параметры MyHour и MyMinute) на заранее заданном расстоянии от текущей цены (параметр MyPendingLevel) выставлять два ордера - Sell Stop и Buy Stop. При этом должны выставляться ордера Stop Loss на расстоянии MySL пипсов от цены ордера и Take Profit на расстоянии MyTP пипсов.

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

//+------------------------------------------------------------------+

//| 2nd Expert.mq4 |

//| Andrey Vedikhin |

//| http://www.vedikhin.ru |

//+------------------------------------------------------------------+

#property copyright "Andrey Vedikhin"

#property link "http://www.vedikhin.ru"

//---- input parameters

extern int MyPendingLevel=15;

extern int MySL=30;

extern int MyTP=15;

extern int MyHour=19;

extern int MyMinute=40;

extern int MyLots=1.0;

datetime LastTradeTime; // время последней торговой операции

//+------------------------------------------------------------------+

//| expert initialization function |

//+------------------------------------------------------------------+

int init()

{

//----

// установим время последней торговой операции вчерашним днем

LastTradeTime = CurTime()-24*60*60;

//----

return(0);

}

//+------------------------------------------------------------------+

//| expert deinitialization function |

//+------------------------------------------------------------------+

int deinit()

{

//----

//----

return(0);

}

//+------------------------------------------------------------------+

//| expert start function |

//+------------------------------------------------------------------+

int start()

{

//----

// проверим, не выставляли ли мы ордер уже сегодня

// если выставляли - выходим

if (TimeDayOfYear(CurTime())==TimeDayOfYear(LastTradeTime))

return(0);

// проверим, не наступило ли время выставить ордер

if ((TimeHour(CurTime())==MyHour)&&(TimeMinute(CurTime())==MyMinute))

{

// выставим Buy Stop

if (OrderSend(Symbol(), OP_BUYSTOP, MyLots,

Ask+Point*MyPendingLevel, 0, Ask+Point*MyPendingLevel-Point*MySL,

Ask+Point*MyPendingLevel+Point*MyTP)!=-1)

LastTradeTime = CurTime();

// ордер не выставлен

else

return(0);

// обязательная пауза в 10 секунд

Sleep(10000);

// обновим текущий Bid и Ask

RefreshRates();

// выставим Sell Stop

if (OrderSend(Symbol(), OP_SELLSTOP, MyLots,

Bid-Point*MyPendingLevel, 0, Bid-Point*MyPendingLevel+Point*MySL,

Bid-Point*MyPendingLevel-Point*MyTP)!=-1)

LastTradeTime = CurTime();

// ордер не выставлен

else

return(0);

}

//----

return(0);

}

//+------------------------------------------------------------------+

В следующем выпуске я разберу этот эксперт по "косточкам".

Пример использования функции OrderSend() - продолжение

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

Советник должен в определенное время (параметры MyHour и MyMinute) на заранее заданном расстоянии от текущей цены (параметр MyPendingLevel) выставлять два ордера - Sell Stop и Buy Stop. При этом должны выставляться ордера Stop Loss на расстоянии MySL пипсов от цены ордера и Take Profit на расстоянии MyTP пипсов.

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

Прежде всего этот эксперт имеет несколько параметров:

extern int MyPendingLevel=15;

extern int MySL=30;

extern int MyTP=15;

extern int MyHour=19;

extern int MyMinute=40;

extern int MyLots=1.0;

Эти параметры имеют следующих смысл:

  • MyPendingLevel - расстояние в пипсах от текущей цены, на котором выставляется отложенный ордер;

  • MySL и MyTP - Stop Loss и Tale Profit в пипсах от цены отложенного ордера;

  • MyHour и MyMinute - час и минута, когда выставляет отложенный ордер;

  • MyLots - размер лота отложенного ордера.

Напомню, что о том, как описать в коде параметры эксперта, я рассказывал в выпуске "Внешние переменные".

В реальности может возникнуть ситуация, когда на баре MyHour:MyMinute может быть несколько тиков, поэтому чтобы избежать выставления отложенных ордеров на каждом тике, мы завели глобальную переменную LastTradeTime:

datetime LastTradeTime; // время последней торговой операции

Этой переменной мы присваиваем в качестве начального значения вчерашнюю дату при инициализации эксперта - в функции init():

//+------------------------------------------------------------------+

//| expert initialization function |

//+------------------------------------------------------------------+

int init()

{

//----

// установим время последней торговой операции вчерашним днем

LastTradeTime = CurTime()-24*60*60;

//----

return(0);

}

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

// проверим, не выставляли ли мы ордер уже сегодня

// если выставляли - выходим

if (TimeDayOfYear(CurTime())==TimeDayOfYear(LastTradeTime))

return(0);

Здесь используется неизвестная нам пока функция TimeDayOfYear():

int TimeDayOfYear( datetime date )

Эта функция возвращает порядковый номер дня (с начала года): 1 - 1 января, ... , 365 или 366 - 31 декабря.

Если же порядковый номер дня последней торговой операции - TimeDayOfYear(LastTradeTime) - равен порядковому дню текущего времени - TimeDayOfYear(CurTime()), - значит мы уже сегодня ордера выставляли, поэтому выходим из эксперта: return(0).

Теперь проверим, не наступило ли время выставить отложенный ордер:

// проверим, не наступило ли время выставить ордер

if ((TimeHour(CurTime())==MyHour)&&(TimeMinute(CurTime())==MyMinute))

{

...

}

Расскажу о двух незнакомых функциях в этом участке кода:

  • int TimeHour(datetime time) - возвращает час для времени time: 0..23;

  • int TimeMinute(datetime time) - возвращает минуту для времени time: 0..59.

Если уже пора выставить отложенный ордер, то сначала с помощью функции OrderSend() размещаем ордер Buy Stop.

Потом выжидаем паузу в 10 секунд с помощью функции Sleep():

void Sleep(int milliseconds)

Эта функция делает паузу в работе эксперта на milliseconds милисекунд (1 секунда = 1000 милисекунд).

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

Далее мы выставляем отложенный ордер Sell Stop.

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

В следующем выпуске я расскажу о функции OrderModify().

OrderModify() - изменение параметров отложенных ордеров и открытых позиций

В прошлых выпусках я рассмотрел очень полезную функцию - OrderSend(). А в этом выпуске я расскажу о функции OrderModify().

bool OrderModify(int ticket, double price, double stoploss, double takeprofit,

datetime expiration, color arrow_color=CLR_NONE)

Эта функция позволяет изменять параметры отложенных ордеров и открытых позиций. В случае успеха она возвращает true, а в случае неудачи - false. Код ошибки можно будет получить с помощью функции GetLastError().

Параметры функции:

  • ticket - тикер отложенного ордера или открытой позиции;

  • price - новый уровень отложенного ордера (для открытых позиций не может быть изменен);

  • stoploss - новый уровень Stop Loss;

  • takeprofit - новый уровень Take Profit;

  • expiration - новые дата и время истечения отложенного ордера (если к этой дате и времени отложенный ордер не сработает, то он будет удален - см. выпуск "OrderExpiration() - дата истечения отложенного ордера");

  • arrow_color - цвет открывающей стрелки на графике. Если параметр отсутствует или его значение равно CLR_NONE, то открывающая стрелка не отображается на графике.

В случае неправильных параметров функция возвращает следующие коды ошибок:

  • 1 (ERR_NO_RESULT) - если ни один из параметров не был изменен;

  • 147 (ERR_TRADE_EXPIRATION_DENIED) - если в настройках торгового сервера запрещено выставлять дату эксперации ордера. В этом случае параметр expiration всегда должен быть равен нулю).

В качестве практического примера применения этой функции рассмотрим установку и управление трейлинг стопом.

Напомню, что Trailing Stop (трейлинг стоп) – это алгоритм управления уровнем Stop Loss ордера. После выставления трейлинг-стопа (например, на Х пипсов) происходит следующее:

  • MetaTrader не предпринимает никаких действий до того момента, пока по открытой позиции не образуется прибыль в Х пипсов. После этого MetaTrader выставляет Stop Loss ордер на расстоянии Х пипсов от текущей цены (в данном случае - на уровне безубыточности).

  • После выполнения первого шага MetaTrader посылает команду на изменение уровня Stop Loss ордера на расстояние Х пипсов от текущей котировки каждый раз, когда расстояние между ней и старым уровнем ордера превысит Х пипсов. В результате этого Stop Loss ордер "подтягивается" к текущей цене.

Реализуем этот принцип на языке MetaQuotes Language 4. Будем полагать, что открытая позиция уже выбрана и мы точно знаем, что эта позиция открыта по инструменту, к которому прикреплен эксперт. Также предположим, что значение трейлинг стопа в пипсах содержится в переменной TrailingStop.

int err;

if (OrderType() == OP_BUY)

{

// позиция на покупку

if ((Bid-OrderOpenPrice())>=(TrailingStop*Point))

{

// выставляем Stop Loss

if (OrderModify(OrderTicket(), OrderOpenPrice(), Bid-TrailingStop*Point,

OrderTakeProfit(), 0))

Print("#", OrderTicket(),": trailing stop ", Bid-TrailingStop*Point);

else

{

err = GetLastError();

Print("#", OrderTicket(),": trailing stop error ", err);

}

}

}

else

{

// позиция на продажу

if ((OrderOpenPrice()-Ask)>=(TrailingStop*Point))

{

// выставляем Stop Loss

if (OrderModify(OrderTicket(), OrderOpenPrice(), Ask+TrailingStop*Point,

OrderTakeProfit(), 0))

Print("#", OrderTicket(),": trailing stop ", Ask+TrailingStop*Point);

else

{

err = GetLastError();

Print("#", OrderTicket(),": trailing stop error ", err);

}

}

}

В этом коде Вам могут быть непонятны несколько функций:

  • Bid - Bid последней котировки по инструменту, к которому прикреплен эксперт;

  • Ask - Ask последней котировки по инструменту, к которому прикреплен советник;

  • Point - размер пункта текущего инструмента в валюте котировки (для EURUSD это 0.0001, для USDJPY- 0.01 и т.д.).

В следующем выпуске я расскажу, как удалить отложенный ордер с помощью функции OrderDelete().

OrderDelete() - удаление отложенного ордера

Мы уже знаем, как можно выставить отложенный ордер. Для этого надо использовать функцию OrderSend(). Также мы умеем изменять параметры отложенных ордеров с помощью функции OrderModify().

В этом выпуске я расскажу о том, как можно удалить отложенный ордер. Это можно сделать с помощью функции OrderDelete().

bool OrderDelete(int ticket)

Эта функция удаляет отложенный ордер с тикером ticket. Функция возвращает true в случае успешного удаления и false - в случае возникновения ошибки. Код ошибки можно получить, вызвав функцию GetLastError().

Применение функции OrderDelete() рассмотрим на следующем примере:

Предположим, что наш эксперт по какой-то логике (не будем здесь вдаваться в подробности) выставляет по одному инструменту сразу два отложенных ордера - Buy Limit и Sell Limit. Наша задача - если один из ордеров сработал, то удалить оставшийся отложенный ордер.

Предположим, что по данному инструменту работает только наш эксперт.

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

Пример использования функции OrderDelete()

В прошлом выпуске я рассмотрел функцию OrderDelete(). Эта функция удаляет отложенный ордер.

Рассмотрим применение этой функции на примере:

Предположим, что наш эксперт по какой-то логике (не будем здесь вдаваться в подробности) выставляет по одному инструменту сразу два отложенных ордера - Buy Limit и Sell Limit. Наша задача - если один из ордеров сработал, то удалить оставшийся отложенный ордер.

Предположим, что по данному инструменту работает только наш эксперт.

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

// просмотрим все открытые позиции и выставленные ордера

int pos;

for(pos=0; pos<OrdersTotal(); pos++)

{

// выделим текущий ордер, чтобы получить о нем информацию

if (OrderSelect(pos, SELECT_BY_POS))

{

// Если "магическое" число совпадает и это открытая позиция

if ((OrderMagicNumber()==MyMagicNumber) && (OrderType()<=OP_SELL))

{

// найти второй отложенный ордер

int i;

for(i=0; i<OrdersTotal(); i++)

{

if (OrderSelect(i, SELECT_BY_POS))

{

if ((OrderMagicNumber()==MyMagicNumber) && (OrderType()>OP_SELL))

{

// ордер найден - удалим его

if (OrderDelete(OrderTicket()))

{

Print("Ордер удален");

}

else

{

Print("Ошибка ", GetLastError(), " при удалении ордера");

}

}

}

}

// выйти из цикла

break;

}

}

}

В следующем выпуске я продолжу рассмотрение торговых функций и расскажу о функции OrderClose().

Закрытие позиции с помощью функции OrderClose()

В предыдущих выпусках я рассмотрил уже много полезных торговых функций:

  • С помощью функции OrderSend() можно открыть позицию или выставить отложенный ордер;

  • Функция OrderModify() изменяет параметры открытой позиции или отложенного ордера;

  • Используя функцию OrderDelete() можно удалить отложенный ордер.

Пришло время рассказать о том, как закрыть открытую позицию с помощью функции OrderClose().

bool OrderClose(int ticket, double lots, double price, int slippage,

color Color=CLR_NONE)

Параметры функции:

  • ticket - тикер открытой позиции;

  • lots - объем в лотах;

  • price - цена закрытия позиции;

  • slippage - максимально допустимое отклонение между price и ценой сервера, при котором позиция будет закрыта;

  • arrow_color - цвет закрывающей стрелки на графике. Если параметр отсутствует или его значение равно CLR_NONE, то стрелка не отображается на графике.

Функция OrderClose() вернет true, если позиция закрыта успешно. Если же произошла какая-то ошибка, то функция вернет false и код ошибки можно будет получить с помощью функции GetLastError().

В следующем выпуске я рассмотрю пример использования функции OrderClose().

Пример использования функции OrderClose()

В прошлом выпуске я рассказал о том, как можно закрыть открытую позицию с помощью функции OrderClose(). Практических примеров применения этой функции можно привести бесчисленное количество, т.к. в большинстве экспертов существует потребность закрывать позиции не только по Stop Loss или Take Profit ордеру, но и по текущей цене.

Мы же в качестве примера применения функции OrderClose() рассмотрим эксперт, который закрывает все открытые позиции и удаляет все отложенные ордера в пятницу после 22:00 (по времени торговой платформы).

//+------------------------------------------------------------------+

//| Close everything on Friday.mq4 |

//| Copyright © 2006, Andrey Vedikhin |

//| http://www.vedikhin.ru |

//+------------------------------------------------------------------+

#property copyright "Copyright © 2006, Andrey Vedikhin"

#property link "http://www.vedikhin.ru"

//---- input parameters

extern int MyHour=22;

extern int MyMinute=00;

// 5 - Friday

#define MyDay 5

datetime LastTradeTime;

//+------------------------------------------------------------------+

//| expert initialization function |

//+------------------------------------------------------------------+

int init()

{

//----

// установим время последней торговой операции вчерашним днем

LastTradeTime = CurTime()-24*60*60;

//----

return(0);

}

//+------------------------------------------------------------------+

//| expert deinitialization function |

//+------------------------------------------------------------------+

int deinit()

{

//----

//----

return(0);

}

//+------------------------------------------------------------------+

//| expert start function |

//+------------------------------------------------------------------+

int start()

{

//----

// проверим, не закрывали ли мы все уже сегодня

// если да - выходим

if (TimeDayOfYear(CurTime())==TimeDayOfYear(LastTradeTime)) return(0);

// если не пятница - выходим

if (DayOfWeek()!=MyDay) return(0);

// проверим, не наступило ли время закрывать позиции

if (((TimeHour(CurTime())==MyHour)&&(TimeMinute(CurTime())>=MyMinute))

||(TimeHour(CurTime())>MyHour))

{

// закроем все позиции и удалим ордера

while (OrdersTotal()>0)

{

// выделим первую позицию или ордер в списке

if (!OrderSelect(0, SELECT_BY_POS)) break; // в случае неудачи выйти из цикла

// если отложенный ордер, то удалить

if (OrderType()>OP_SELL)

{

if (!OrderDelete(OrderTicket()))

{

Print("Ошибка ", GetLastError()," при удалении отложенного ордера ",

OrderTicket());

break;

}

}

// если открытая позиция, то закрыть

else

{

double price;

if (OrderType()==OP_SELL)

price = MarketInfo(OrderSymbol(), MODE_ASK);

else

price = MarketInfo(OrderSymbol(), MODE_BID);

if (!OrderClose(OrderTicket(), OrderLots(), price, 3))

{

Print("Ошибка ", GetLastError()," при закрытии позиции ", OrderTicket());

break;

}

}

// пауза 10 секунд

Sleep(10000);

}

if (OrdersTotal()==0) LastTradeTime = CurTime();

}

//----

return(0);

}

//+------------------------------------------------------------------+

В следующем выпуске я объясню принцип работы этого эксперта.

Пример использования функции OrderClose() - продолжение

Уважаемые читатели!

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

В прошлом выпуске я разместил код эксперта, который закрывает все открытые позиции и удаляет все отложенные ордера в пятницу после 22:00 (по времени торговой платформы). Этого эксперта я написал, чтобы показать Вам практический пример использования функции OrderClose().

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

Прежде всего этот эксперт имеет несколько параметров:

extern int MyHour=22;

extern int MyMinute=00;

Эти параметры имеют следующих смысл:

  • MyHour и MyMinute - час и минута, когда эксперт закрывает открытые позиции и удаляет отложенные ордера.

Напомню, что о том, как описать в коде параметры эксперта, я рассказывал в выпуске "Внешние переменные".

Мы объявили глобальную переменную LastTradeTime:

datetime LastTradeTime;

С ее помощью мы сможем избежать "сработки" эксперта на каждом тике после 22:00, если в этот день мы уже закрыли все открытые позиции и удалили отложенные ордера.

При инициализации эксперта в функции init() мы присваиваем этой переменной значение вчерашнего дня:

int init()

{

//----

// установим время последней торговой операции вчерашним днем

LastTradeTime = CurTime()-24*60*60;

//----

return(0);

}

Напомню, что функция CurTime() возвращает текущее время в формате datetime.

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

if (TimeDayOfYear(CurTime())==TimeDayOfYear(LastTradeTime)) return(0);

Здесь используется функция TimeDayOfYear():

int TimeDayOfYear( datetime date )

Эта функция возвращает порядковый номер дня (с начала года): 1 - 1 января, ... , 365 или 366 - 31 декабря.

Если же порядковый номер дня последней торговой операции - TimeDayOfYear(LastTradeTime) - равен порядковому дню текущего времени - TimeDayOfYear(CurTime()), - значит мы уже сегодня ордера удаляли и позиции закрывали, поэтому выходим из эксперта: return(0).

Проверим, не пятница ли сегодня:

if (DayOfWeek()!=MyDay) return(0);

Функция DayOfWeek() возвращает порядковый номер дня недели (воскресенье - 0, 1 - понедельник, ..., 6 - суббота) последнего известного времени сервера.

Теперь проверим, не наступило ли время закрывать позиции:

if (((TimeHour(CurTime())==MyHour)&&(TimeMinute(CurTime())>=MyMinute))

||(TimeHour(CurTime())>MyHour))

{

...

}

В этом кусочке кода я использовал две новые функции:

  • int TimeHour(datetime time) - возвращает час для времени time: 0..23;

  • int TimeMinute(datetime time) - возвращает минуту для времени time: 0..59.

Далее с помощью функции OrderSelect() мы начинаем выделять первую позицию/ордер в списке, пытаться ее закрыть или удалить ордер, а потом с помощью цикла while этот процесс повторяется вновь и вновь, пока есть открытые позиции или отложенные ордера. Также цикл прерывается в случае ошибки.

Обязательно между торговыми операциями мы выдерживаем паузу в 10 секунд:

Sleep(10000);

Функция Sleep(int milliseconds) делает паузу в работе эксперта на milliseconds милисекунд (1 секунда = 1000 милисекунд).

После того, как все позиции закрыты и все отложенные ордера удалены, мы задаем время последней "сработки" советника текущим временем:

if (OrdersTotal()==0) LastTradeTime = CurTime();

В коде эксперта Вы видите еще одну незнакомую нам функцию - MarketInfo():

double MarketInfo(string symbol, int type)

С помощью этой функции можно получить различную информацию об инструменте symbol. Вид получаемой информации зависит от значения параметра type:

Константа

Значение

Описание

MODE_LOW

1

Минимальная цена за день

MODE_HIGH

2

Максимальная цена за день

MODE_TIME

5

Время поступления последней котировки

MODE_BID

9

Последний Bid

MODE_ASK

10

Последний Ask

MODE_POINT

11

Размер пункта в валюте котировки

MODE_DIGITS

12

Количество цифр после десятичного точки в цене инструмента

MODE_SPREAD

13

Спрэд в пунктах

MODE_STOPLEVEL

14

Минимально допустимый уровень стоп-лосса/тейк-профита в пунктах

MODE_LOTSIZE

15

Размер контракта в базовой валюте инструмента

MODE_TICKVALUE

16

Размер минимального изменения цены инструмента в валюте котировки

MODE_TICKSIZE

17

Минимальный шаг изменения цены инструмента в пунктах

MODE_SWAPLONG

18

Сторидж для длинных позиций

MODE_SWAPSHORT

19

Сторидж для коротких позиций

MODE_STARTING

20

Дата начала торгов по инструменту (для фьючерсов)

MODE_EXPIRATION

21

Дата окончания торгов по инструменту (для фьючерсов)

MODE_TRADEALLOWED

22

Флаг разрешения торгов по данному инструменту

MODE_MINLOT

23

Минимальный размер лота

MODE_LOTSTEP

24

Шаг изменения размера лота

MODE_MAXLOT

25

Максимальный размер лота

В следующем выпуске я расскажу о функции OrderCloseBy().

OrderCloseBy() - закрытие одной позиции другой

В прошлых выпусках мы рассмотрели пример применения функции OrderClose(). В этом примере мы закрывали все открытые позиции и удаляли отложенные ордера в 22:00 пятницы по времени торговой платформы. Код эксперта был приведен в выпуске "Пример использования функции OrderClose()".

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

Давайте перепишем нашего эксперта с учетом этого совета. В этом нам поможет функция OrderCloseBy():

//+------------------------------------------------------------------+

//| Close everything on Friday.mq4 |

//| Copyright © 2006, Andrey Vedikhin |

//| http://www.vedikhin.ru |

//+------------------------------------------------------------------+

#property copyright "Copyright © 2006, Andrey Vedikhin"

#property link "http://www.vedikhin.ru"

//---- input parameters

extern int MyHour=22;

extern int MyMinute=00;

// 5 - Friday

#define MyDay 5

datetime LastTradeTime;

//+------------------------------------------------------------------+

//| expert initialization function |

//+------------------------------------------------------------------+

int init()

{

//----

// установим время последней торговой операции вчерашним днем

LastTradeTime = CurTime()-24*60*60;

//----

return(0);

}

//+------------------------------------------------------------------+

//| expert deinitialization function |

//+------------------------------------------------------------------+

int deinit()

{

//----

//----

return(0);

}

//+------------------------------------------------------------------+

//| expert start function |

//+------------------------------------------------------------------+

int start()

{

//----

// проверим, не закрывали ли мы все уже сегодня

// если да - выходим

if (TimeDayOfYear(CurTime())==TimeDayOfYear(LastTradeTime)) return(0);

// если не пятница - выходим

if (DayOfWeek()!=MyDay) return(0);

// проверим, не наступило ли время закрывать позиции

if (((TimeHour(CurTime())==MyHour)&&(TimeMinute(CurTime())>=MyMinute))

||(TimeHour(CurTime())>MyHour))

{

// закроем все позиции и удалим ордера

while (OrdersTotal()>0)

{

// выделим первую позицию или ордер в списке

if (!OrderSelect(0, SELECT_BY_POS))

// в случае неудачи выйти из цикла

break;

// если отложенный ордер, то удалить

if (OrderType()>OP_SELL)

{

if (!OrderDelete(OrderTicket()))

{

Print("Ошибка ", GetLastError()," при удалении отложенного ордера ",

OrderTicket());

break;

}

}

// если открытая позиция, то закрыть

else

{

// попробуем найти локированную позицию по этому же инструменту

int ticket0;

ticket0 = OrderTicket();

string symbol0;

symbol0 = OrderSymbol();

int ordertype0;

ordertype0 = OrderType();

int i;

for (i = 1; i<OrdersTotal(); i++)

{

if (!OrderSelect(i, SELECT_BY_POS))

// в случае неудачи выйти из цикла

break;

if ((symbol0==OrderSymbol()) && (OrderType()==((ordertype0+1)%2)) )

{

if (!OrderCloseBy(OrderTicket(), ticket0))

{

Print("Ошибка ", GetLastError()," при закрытии позиции ",

OrderTicket(), " позицией ", ticket0);

break;

}

else

{

i = -2;

break;

}

}

}

// если CloseBy успешна, то пауза и завершить итерацию цикла

if (i==-2)

{

Sleep(10000);

continue;

}

// заново выделим нулевую позицию

if (!OrderSelect(ticket0, SELECT_BY_TICKET))

// в случае неудачи выйти из цикла

break;

double price;

if (OrderType()==OP_SELL)

price = MarketInfo(OrderSymbol(), MODE_ASK);

else

price = MarketInfo(OrderSymbol(), MODE_BID);

if (!OrderClose(OrderTicket(), OrderLots(), price, 3))

{

Print("Ошибка ", GetLastError()," при закрытии позиции ",

OrderTicket());

break;

}

}

// пауза 10 секунд

Sleep(10000);

}

if (OrdersTotal()==0) LastTradeTime = CurTime();

}

//----

return(0);

}

//+------------------------------------------------------------------+

В следующем выпуске я объясню принцип работы этого эксперта и расскажу подробно о функции OrderCloseBy().

OrderCloseBy() - закрытие одной позиции другой (продолжение)

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

bool OrderCloseBy(int ticket, int opposite, color сolor=CLR_NONE)

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

Параметры функции OrderCloseBy():

  • ticket - тикер первой позиции;

  • opposite - тикер второй позиции;

  • сolor - цвет закрывающей стрелки на графике. Если параметр отсутствует или его значение равно CLR_NONE, то закрывающая стрелка не отображается на графике.

Разберем каждую новую строчку нашего эксперта:

// попробуем найти локированную позицию по этому же инструменту

int ticket0;

ticket0 = OrderTicket();

string symbol0;

symbol0 = OrderSymbol();

int ordertype0;

ordertype0 = OrderType();

int i;

for (i = 1; i<OrdersTotal(); i++)

{

if (!OrderSelect(i, SELECT_BY_POS))

// в случае неудачи выйти из цикла

break;

if ((symbol0==OrderSymbol()) && (OrderType()==((ordertype0+1)%2)) )

{

if (!OrderCloseBy(OrderTicket(), ticket0))

{

Print("Ошибка ", GetLastError()," при закрытии позиции ",

OrderTicket(), " позицией ", ticket0);

break;

}

else

{

i = -2;

break;

}

}

}

// если CloseBy успешна, то пауза и завершить итерацию цикла

if (i==-2)

{

Sleep(10000);

continue;

}

// заново выделим нулевую позицию

if (!OrderSelect(ticket0, SELECT_BY_TICKET))

// в случае неудачи выйти из цикла

break;

Вначале запомним номер тикера, инструмент и тип операции выделенной позиции (первой в списке открытых позиций и отложенных ордеров) в переменных ticket0, symbol0 и ordertype0 соответственно:

int ticket0;

ticket0 = OrderTicket();

string symbol0;

symbol0 = OrderSymbol();

int ordertype0;

ordertype0 = OrderType();

После этого в цикле переберем все оставшиеся позиции в поиске позиции, чтобы найти позицию по тому же инструменту, но открытую в другом направлении (OrderType()==((ordertype0+1)%2)):

int i;

for (i = 1; i<OrdersTotal(); i++)

{

if (!OrderSelect(i, SELECT_BY_POS))

// в случае неудачи выйти из цикла

break;

if ((symbol0==OrderSymbol()) && (OrderType()==((ordertype0+1)%2)) )

{

...

}

}

Если такую позицию мы нашли, то закроем ее и нашу первоначально выделенную позицию с помощью функции OrderCloseBy() и в случае успеха присвоим переменной i значение -2.

Далее мы проверяем были ли закрыты локированные позиции. Если "да", то делаем паузу и переходим к следующей итерации цикла while, т.е. смотрим следующую позицию/ордер - кандидатов на закрытие/удаление:

if (i==-2)

{

Sleep(10000);

continue;

}

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

if (!OrderSelect(ticket0, SELECT_BY_TICKET))

// в случае неудачи выйти из цикла

break;

Далее пытаемся закрыть эту позицию с помощью функции OrderClose().

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

Коды ошибок в советниках и функция GetLastError()

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

int GetLastError()

После вызова этой функции код последней ошибки обнуляется, поэтому если в промежутке между двумя вызовами функции GetLastError() не произошло новой ошибки, то второй раз функция вернет 0.

Функция GetLastError() возвращает числовой код ошибки. Если же Вы хотите получить словесное описание ошибки, то используйте функцию ErrorDescription(), описанную в файле stdlib.mqh:

string ErrorDescription(int error_code)

{

string error_string;

//----

switch(error_code)

{

//---- codes returned from trade server

case 0:

case 1: error_string="no error"; break;

case 2: error_string="common error"; break;

...

}

//----

return(error_string);

}

Коды ошибок, возвращаемые сервером:

Константа

Значение

Описание

 ERR_NO_ERROR

0

 Нет ошибки

 ERR_NO_RESULT

1

 Нет ошибки, но результат неизвестен

 ERR_COMMON_ERROR

2

 Общая ошибка

 ERR_INVALID_TRADE_PARAMETERS

3

 Неправильные параметры

 ERR_SERVER_BUSY

4

 Торговый сервер занят

 ERR_OLD_VERSION

5

 Старая версия клиентского терминала

 ERR_NO_CONNECTION

6

 Нет связи с торговым сервером

 ERR_NOT_ENOUGH_RIGHTS

7

 Недостаточно прав

 ERR_TOO_FREQUENT_REQUESTS

8

 Слишком частые запросы

 ERR_MALFUNCTIONAL_TRADE

9

 Недопустимая операция нарушающая функционирование сервера

 ERR_ACCOUNT_DISABLED

64

 Счет заблокирован

 ERR_INVALID_ACCOUNT

65

 Неправильный номер счета

 ERR_TRADE_TIMEOUT

128

 Истек срок ожидания совершения сделки

 ERR_INVALID_PRICE

129

 Неправильная цена

 ERR_INVALID_STOPS

130

 Неправильные стопы

 ERR_INVALID_TRADE_VOLUME

131

 Неправильный объем

 ERR_MARKET_CLOSED

132

 Рынок закрыт

 ERR_TRADE_DISABLED

133

 Торговля запрещена

 ERR_NOT_ENOUGH_MONEY

134

 Недостаточно денег для совершения операции

 ERR_PRICE_CHANGED

135

 Цена изменилась

 ERR_OFF_QUOTES

136

 Нет цен

 ERR_BROKER_BUSY

137

 Брокер занят

 ERR_REQUOTE

138

 Новые цены

 ERR_ORDER_LOCKED

139

 Ордер заблокирован и уже обрабатывается

 ERR_LONG_POSITIONS_ONLY_ALLOWED

140

 Разрешена только покупка

 ERR_TOO_MANY_REQUESTS

141

 Слишком много запросов

 ERR_TRADE_MODIFY_DENIED

145

 Модификация запрещена, так как ордер слишком близок к рынку

 ERR_TRADE_CONTEXT_BUSY

146

 Подсистема торговли занята

 ERR_TRADE_EXPIRATION_DENIED

147

 Использование даты истечения ордера запрещено брокером

 ERR_TRADE_TOO_MANY_ORDERS

148

 Количество открытых и отложенных ордеров достигло предела, установленного брокером

Коды ошибок выполнения, генерируемые клиентским терминалом при выполнении советника:

Константа

Значение

Описание

 ERR_NO_MQLERROR

4000

 Нет ошибки

 ERR_WRONG_FUNCTION_POINTER

4001

 Неправильный указатель функции

 ERR_ARRAY_INDEX_OUT_OF_RANGE

4002

 Индекс массива - вне диапазона

 ERR_NO_MEMORY_FOR_FUNCTION_CALL_STACK

4003

 Нет памяти для стека функций

 ERR_RECURSIVE_STACK_OVERFLOW

4004

 Переполнение стека после рекурсивного вызова

 ERR_NOT_ENOUGH_STACK_FOR_PARAMETER

4005

 На стеке нет памяти для передачи параметров

 ERR_NO_MEMORY_FOR_PARAMETER_STRING

4006

 Нет памяти для строкового параметра

 ERR_NO_MEMORY_FOR_TEMP_STRING

4007

 Нет памяти для временной строки

 ERR_NOT_INITIALIZED_STRING

4008

 Неинициализированная строка

 ERR_NOT_INITIALIZED_ARRAYSTRING

4009

 Неинициализированная строка в массиве

 ERR_NO_MEMORY_FOR_ARRAYSTRING

4010

 Нет памяти для строкового массива

 ERR_TOO_LONG_STRING

4011

 Слишком длинная строка

 ERR_REMAINDER_FROM_ZERO_DIVIDE

4012

 Остаток от деления на ноль

 ERR_ZERO_DIVIDE

4013

 Деление на ноль

 ERR_UNKNOWN_COMMAND

4014

 Неизвестная команда

 ERR_WRONG_JUMP

4015

 Неправильный переход

 ERR_NOT_INITIALIZED_ARRAY

4016

 Неинициализированный массив

 ERR_DLL_CALLS_NOT_ALLOWED

4017

 Вызовы DLL не разрешены

 ERR_CANNOT_LOAD_LIBRARY

4018

 Невозможно загрузить библиотеку

 ERR_CANNOT_CALL_FUNCTION

4019

 Невозможно вызвать функцию

 ERR_EXTERNAL_EXPERT_CALLS_NOT_ALLOWED

4020

 Вызовы внешних библиотечных функций не разрешены

 ERR_NOT_ENOUGH_MEMORY_FOR_RETURNED_STRING

4021

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

 ERR_SYSTEM_BUSY

4022

 Система занята

 ERR_INVALID_FUNCTION_PARAMETERS_COUNT

4050

 Неправильное количество параметров функции

 ERR_INVALID_FUNCTION_PARAMETER_VALUE

4051

 Недопустимое значение параметра функции

 ERR_STRING_FUNCTION_INTERNAL_ERROR

4052

 Внутренняя ошибка строковой функции

 ERR_SOME_ARRAY_ERROR

4053

 Ошибка массива

 ERR_INCORRECT_SERIES_ARRAY_USING

4054

 Неправильное использование массива-таймсерии

 ERR_CUSTOM_INDICATOR_ERROR

4055

 Ошибка пользовательского индикатора

 ERR_INCOMPATIBLE_ARRAYS

4056

 Массивы несовместимы

 ERR_GLOBAL_VARIABLES_PROCESSING_ERROR

4057

 Ошибка обработки глобальныех переменных

 ERR_GLOBAL_VARIABLE_NOT_FOUND

4058

 Глобальная переменная не обнаружена

 ERR_FUNCTION_NOT_ALLOWED_IN_TESTING_MODE

4059

 Функция не разрешена в тестовом режиме

 ERR_FUNCTION_NOT_CONFIRMED

4060

 Функция не подтверждена

 ERR_SEND_MAIL_ERROR

4061

 Ошибка отправки почты

 ERR_STRING_PARAMETER_EXPECTED

4062

 Ожидается параметр типа string

 ERR_INTEGER_PARAMETER_EXPECTED

4063

 Ожидается параметр типа integer

 ERR_DOUBLE_PARAMETER_EXPECTED

4064

 Ожидается параметр типа double

 ERR_ARRAY_AS_PARAMETER_EXPECTED

4065

 В качестве параметра ожидается массив

 ERR_HISTORY_WILL_UPDATED

4066

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

 ERR_TRADE_ERROR

4067

 Ошибка при выполнении торговой операции

 ERR_END_OF_FILE

4099

 Конец файла

 ERR_SOME_FILE_ERROR

4100

 Ошибка при работе с файлом

 ERR_WRONG_FILE_NAME

4101

 Неправильное имя файла

 ERR_TOO_MANY_OPENED_FILES

4102

 Слишком много открытых файлов

 ERR_CANNOT_OPEN_FILE

4103

 Невозможно открыть файл

 ERR_INCOMPATIBLE_ACCESS_TO_FILE

4104

 Несовместимый режим доступа к файлу

 ERR_NO_ORDER_SELECTED

4105

 Ни один ордер не выбран

 ERR_UNKNOWN_SYMBOL

4106

 Неизвестный символ

 ERR_INVALID_PRICE_PARAM

4107

 Неправильный параметр цены для торговой функции

 ERR_INVALID_TICKET

4108

 Неверный номер тикета

 ERR_TRADE_NOT_ALLOWED

4109

 Торговля не разрешена

 ERR_LONGS_NOT_ALLOWED

4110

 Длинные позиции не разрешены

 ERR_SHORTS_NOT_ALLOWED

4111

 Короткие позиции не разрешены

 ERR_OBJECT_ALREADY_EXISTS

4200

 Объект уже существует

 ERR_UNKNOWN_OBJECT_PROPERTY

4201

 Запрошено неизвестное свойство объекта

 ERR_OBJECT_DOES_NOT_EXIST

4202

 Объект не существует

 ERR_UNKNOWN_OBJECT_TYPE

4203

 Неизвестный тип объекта

 ERR_NO_OBJECT_NAME

4204

 Нет имени объекта

 ERR_OBJECT_COORDINATES_ERROR

4205

 Ошибка координат объекта

 ERR_NO_SPECIFIED_SUBWINDOW

4206

 Не найдено указанное подокно

 ERR_SOME_OBJECT_ERROR

4207

 Ошибка при работе с объектом

В следующем выпуске я расскажу о том, как проверить, разрешено ли торговать эксперту. Я расскажу о функциях IsExpertEnabled(), IsTradeContextBusy() и IsTradeAllowed().

Как убедиться, что советник может торговать

В прошлых выпусках я рассказал о различных торговых функциях, с помощью которых можно открывать/закрывать позиции, выставлять/модифицировать/удалять ордера: OrderSend(), OrderModify(), OrderDelete(), OrderClose() и OrderClosyBy().

В каждом случае мы исходили из того, что советнику разрешено торговать. Но в реальности это не всегда так.

В этом выпуске я расскажу о трех функциях: IsExpertEnabled(), IsTradeContextBusy() и IsTradeAllowed().

Функция IsExpertEnabled()

bool IsExpertEnabled()

Функция IsExpertEnabled() возвращает true, если экспертам разрешено совершать сделки и управлять ордерами. Дело в том, что на уровне настроек клиентского терминала можно запретить ВСЕМ экспертам торговать. Это можно сделать, например, с помощью команды главного меню: Сервис -> Настройки. В появившемся окне выбираем вкладку "Советники", в которой ставим или убираем галочку напротив "Включить советники ...".

Более простой способ отключать/включать советников - кнопка .

Если же на уровне настроек терминала работа экспертов запрещена, то функция IsExpertEnabled() вернет false.

Функция IsTradeContextBusy()

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

bool IsTradeContextBusy()

Функция IsTradeContextBusy() позволит Вам заблаговременно выявить ситуацию, когда другой эксперт уже пытается торговать (она вернет в этом случае значение true), и избежать нерезультативного вызова торговой функции (которая все равно завершиться с ошибкой). Обычно в таких случаях лучше подождать немного до того момента, когда другой эксперт закончит торговать (тогда функция IsTradeContextBusy() вернет значение false) и только после этого вызывать Вашу торговую функцию.

Функция IsTradeAllowed()

Функция IsTradeAllowed() объединяет все обе предыдущие функции. Она вернет true только в том случае, если торговля экспертов разрешена и торговый поток свободен для торговли, т.е. никакой другой эксперт не совершает в данный момент торговых операций.

bool IsTradeAllowed()

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

Как избежать ошибок, если несколько экспертов торгуют одновременно

Как я уже говорил в прошлом выпуске, в каждый момент времени только один эксперт может торговать. Если в этот момент второй эксперт попытается совершить торговую операцию, то торговая функция вернет ошибку ERR_TRADE_CONTEXT_BUSY (146).

Что же делать, если у Вас торгует одновременно несколько экспертов?

Напишем функцию, которая возвращает:

  • 0, если торговый поток свободен;

  • 1, если работа эксперта была остановлена (см. причины ниже);

  • 2, если торговля экспертов запрещена на уровне настроек клиентского терминала.

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

Напомню возможные причины остановки эксперта:

  • завершение работы MetaTrader 4,

  • закрытие графика,

  • удаление эксперта с графика,

  • изменение инструмента или периода графика,

  • удачная перекомпиляции программы в MetaEditor,

  • изменение параметров эксперта,

  • переключение на другой счет.

Исходный код функции, которая решает поставленную задачу:

//+-------------------------------------------------------------------+

//| Функция WaitUntilTradingIsAllowed возвращает:|

//| 0 - если торговый поток свободен |

//| 1 - если работа эксперта была остановлена |

//| 2 - если торговля экспертов запрещена на |

//| уровне настроекклиентского терминала |

//+-------------------------------------------------------------------+

int WaitUntilTradingIsAllowed()

{

// Если торговля разрешена, то вернем 0 и выйдем

if (IsTradeAllowed()) return(0);

// В цикле проверяем, как изменилась ситуация

while (!IsStopped())

{

// Если торговля экспертов запрещена на уровне настроек

// клиентского терминала, то выйдем и вернем 2

if (!IsExpertEnabled()) return(2);

// Если торговый поток освободился, то обновим данные

// о текущих курсах и вернем 0

if (!IsTradeContextBusy())

{

RefreshRates();

return(0);

}

// Если дошли до момента, то ситуация не изменилась

// Поэтому делаем паузу в 0.1 секунду

Sleep(100);

}

// Т.к. вышли из цикла, то работа эксперта была остановлена

// Вернем 1

return(1);

}

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