Формат функции:
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, то открывающая стрелка не отображается на графике.
В параметре cmd мы передаем тип приказа:
Таблица 1. Возможные значения параметра cmd функции OrderSend()
Константа | Значение | Описание |
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 |
Таким образом, для того, чтобы открыть позицию на продажу в качестве параметра 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.
Когда Вы открываете позициию (выставляете отложенный ордер), то ордера 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;
}
Пример советника
Задача.
Советник должен в определенное время (параметры 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);
}
//+------------------------------------------------------------------+
Прежде всего этот эксперт имеет несколько параметров:
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() не вызывалась. Думаю, что у моего читателя уже достаточно собственных знаний, чтобы самостоятельно подправить этот недостаток эксперта.