Ошибка 130 mql4

Помогите, ошибка 130 при попытке открыть Sell Помогите, советник все время выдает ошибку 130 при попытке открыть Sell. При этом при попытке открыть Buy, выдал ошибку 130 один раз и после ни разу не выдавал, сколько я не присоединял советника к графику. Вот и получается, что все Buy он открывает, а Sell не может. […]

Содержание

  1. Помогите, ошибка 130 при попытке открыть Sell
  2. OrderSend Error 130 — What to Do?
  3. Market orders
  4. MQL4 solution to OrderSend Error 130 with market orders
  5. Pending orders
  6. MQL4 solution to OrderSend Error 130 with pending orders
  7. why do dolls die the power of passivity and the embodied interplay between disability and sex dolls?
  8. Video Tutorial
  9. The silent killer
  10. 1) StopLoss & TakeProfit are prices
  11. 2) 4-digits vs 5-digits
  12. 3) ECN brokers
  13. Conclusion

Помогите, ошибка 130 при попытке открыть Sell

Помогите, советник все время выдает ошибку 130 при попытке открыть Sell. При этом при попытке открыть Buy, выдал ошибку 130 один раз и после ни разу не выдавал, сколько я не присоединял советника к графику. Вот и получается, что все Buy он открывает, а Sell не может. Что это может быть.

while(true) // цикл закрытия ордера
<
if (Total==0 && Opn_B==true) // открытых ордеров нет +
< // критерий на открытие Buy
RefreshRates(); // обновление данных
SL=Bid — StopLoss*Point; // вычисление SL открываемого ордера
Alert(«Попытка открыть Buy. Ожидание ответа..»);
Ticket=OrderSend(Symb,OP_BUY,Lots,Ask,3,SL,Green); //открытие Buy
if (Ticket > 0) // получилось
<
Alert («Открыт ордер Buy «,Ticket);
return; // выход из функции start()
>
if (Fun_Error(GetLastError())==1) // обработка ошибок
continue; // повторная попытка
return; // выход из функции start()
>
if (Total==0 && Opn_S==true) // открытых ордеров нет +
< // критерий на открытие Sell
RefreshRates(); // обновление данных
SL=Ask + StopLoss*Point; // вычисление SL откр.
Alert(«Попытка открыть Sell. Ожидание ответа..»);
Ticket=OrderSend(Symb,OP_SELL,Lots,Bid,3,SL,Green); //открытие Sell
if (Ticket > 0) // получилось
<
Alert («Открыт ордер Sell «,Ticket);
return; // выход из функции start()
>
if (Fun_Error(GetLastError())==1) // обработка ошибок
continue; // повторная попытка
return; // выход из функции start()
>
break; // выход из функции while
>

Константа Значение Описание
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 Количество открытых и отложенных ордеров достигло предела, установленного брокером.

< // критерий на открытие Sell
RefreshRates(); // обновление данных
SL=Ask + StopLoss*Point; // вычисление SL откр.

Исправте на: SL=Bid + StopLoss*Point; // вычисление SL откр.

. Что это может быть.

ERR_INVALID_STOPS 130 Слишком близкие стопы или неправильно рассчитанные или ненормализованные цены в стопах (или в цене открытия отложенного ордера). Попытку можно повторять только в том случае, если ошибка произошла из-за устаревания цены. Необходимо после задержки от 5 секунд обновить данные при помощи функции RefreshRates и повторить попытку. Если ошибка не исчезает, необходимо прекратить все попытки торговых операций и изменить логику программы.

P.S. В Вашем варианте это или Слишком близкие стопы или ненормализованные цены в стопах или и то и другое.

См. также

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

В команде OrderSend после SL должно стоять значение для TP.

да, что тут скажешь . rtfm

То, что это неправильные стопы, я знаю. Я пробывал и нормализовывать цену, и RefreshRates есть, так на Buy то все нормально, а на Sell — ошибка 130 и все тут, хотя мой SL очень далек от StopLevel

Константа Значение Описание
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 Количество открытых и отложенных ордеров достигло предела, установленного брокером.

< // критерий на открытие Sell
RefreshRates(); // обновление данных
SL=Ask + StopLoss*Point; // вычисление SL откр.

Исправте на: SL=Bid + StopLoss*Point; // вычисление SL откр.

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

Источник

OrderSend Error 130 — What to Do?

The expert advisors that work on one broker can stop working on another; the problem with them often lies in the OrderSend Error 130. If you see Error 130 in the log of the Experts or Journal tabs in your MetaTrader platform when your expert advisor should be opening a position, then that means that the or levels are set too close to the current market price. In the MQL4 documentation, this error is called ERR_INVALID_STOPS (Invalid stops). Some Forex brokers set the minimum distance between the current price and the / levels to prevent scalping or abusing the quote delays. That isn’t a real problem for the majority of expert advisors that aren’t used for scalping. To prevent this error from occurring, you need to change the expert advisor’s code.

First, you might want to know what the minimum stop level is set in your broker’s MetaTrader server. Adding this line of code will output the current minimum stop level for the currency pair of the chart where you run the EA:

One thing you should be wary of is that a stop level value of zero doesn’t mean that your broker doesn’t set any minimum stop distance. It could also mean that the broker uses some external system for dynamic management of their stop level. In this case, it may be variable and undetectable via MQL4.

Market orders

When opening a market order, you won’t be able to set a or level that is closer than MarketInfo(Symbol(), MODE_STOPLEVEL) to the current price.

MQL4 solution to OrderSend Error 130 with market orders

If your EA calculates stops and dynamically, below is the solution to prevent OrderSend Error 130 from occurring.

Declare a global variable for the minimum stop level; e.g.:

In the OnInit() function (or init() in older versions of MT4) of your expert advisor, define the minimum stop level:

Next time your or in points is calculated, just make sure that they aren’t less than StopLevel :

To check with actual stop-loss and take-profit price levels, a difference between them and the current Bid price for Buy orders or a difference between them and the current Ask price for Sell orders should be checked.

For Sell orders:

Don’t forget to refresh the current market rates with a call to the RefreshRates() function before adding the SL/TP distance to the current market rates or before comparing calculated stop-loss and take-profit levels to the current market rates.

Some brokers (ECN ones) don’t allow expert advisors to set or levels on market orders in the OrderSend() function even if it is greater than their MODE_STOPLEVEL value. In this case, you will have to change your EA to send market orders without SL and TP and then use OrderModify() function to set and for the open position. Alternatively, you can also switch the EA to using pending orders only.

Pending orders

For pending orders (stop or limit), MetaTrader 4 offers the following restrictions in regards to stop level:

Buy Limit — the distances between the current Ask and the Entry, between the Entry and the Stop-Loss, and between the Entry and Take-Profit should all be greater or equal to the stop level.

Sell Limit — the distances between the current Bid and the Entry, between the Entry and the Stop-Loss, and between the Entry and Take-Profit should all be greater or equal to the stop level.

Buy Stop — the distances between the current Ask and the Entry, between the Entry and the Stop-Loss, and between the Entry and Take-Profit should all be greater or equal to the stop level. Actually, the conditions are the same conditions as for the Buy Limit order, but the Entry level is located above the current Ask in Buy Stop.

Sell Stop — the distances between the current Bid and the Entry, between the Entry and the Stop-Loss, and between the Entry and Take-Profit should all be greater or equal to the stop level. Actually, the conditions are the same conditions as for the Sell Limit order, but the Entry level is located below the current Bid in Sell Stop.

MQL4 solution to OrderSend Error 130 with pending orders

Here are examples of MQL4 code checks to make sure your Entry, Stop-Loss, and Take-Profit levels for MT4 pending orders comply with the broker’s stop level restriction.

Источник

why do dolls die the power of passivity and the embodied interplay between disability and sex dolls?

By popular demand, proven strategies on how to beat every algorithmic trader’s worst nightmare – Error 130

The OrderSend Error 130 appears in MetaTrader 4 when an Expert Advisor can’t execute a marker order as expected. Also known as the Invalid Stop (ERR_INVALID_STOPS) in MQL jargon, the Error 130 happens when the TakeProfit and StopLoss levels are set to close to the current market price.

Where does this error come from? What does it mean for your Expert Advisor? How can you find the part of your code that is causing the error? We tackle all this and more…

Video Tutorial

Alright! Let’s go ahead and send some orders with OrderSend. The video below is available if you prefer watching instead of reading

To start off, a formal definition from our friend, MQL4 Documentation:

That’s right! That is all you get from MetaQuotes. And the rest… Go figure!

Ordersend Error 130 is briefly mentioned in other sections of the documentation. However, there is no thorough guide to what “Invalid Stops” actually means and how to deal with this, perhaps, most common problem in Forex programming.

But not a worry! That’s why I have written this article. Let’s get through this together!

The silent killer

So… you launched your expert advisor and… nothing happens. No BUY orders, no SELL orders, no pending orders, not even error messages in the logs…. Just silence. You decide to wait a few hours / days / weeks, and nothing really changes – the charts go up and down, but you don’t see any profit. This can go on forever…

The real reason is simple – you’re actually getting ERR_INVALID_STOPS (which is the correct technical term for the issue), but you can’t see it. That’s because 130 is a silent killer. A cold-blooded murderer of your brain and inner calm 🙂

There is no way to pick up this error through expert advisor logs or even terminal logs. The only way to catch it is by adding the right failsafe mechanisms into your code. Here’s an example you can adapt to your code:
int ticket;
ticket = OrderSend(«EURUSD», OP_BUY, 1.0, Ask, 10, StopLossLevel, TakeProfitLevel, «My 1st Order!»);

if(ticket Invalid stops is the real name for the culprit we are dealing with today. So what does invalid stops in MetaTrader 4 actually mean?

  • For a market order (BUY or SELL) invalid stops means that the StopLoss and/or TakeProfit you requested were not possible to set for your order. Therefore, since a request cannot be fulfilled only partially, the order was not executed at all
  • For a pending order (BUY STOP, BUY LIMIT, SELL STOP, or SELL LIMIT) invalid stops means that either (1) there were issues with the SL/TP (same as above) OR (2) the issue was with the entry price which you specified for the order itself

As we can see, the issue is always with one (or many) of the prices that your Forex Robot specified in its request to the trade server. Now that we know our enemy – let’s beat it!

1) StopLoss & TakeProfit are prices

There are several possible causes of ERR_INVALID_STOPS, and one of the more frequent ones among beginners is specifying the StopLoss and TakeProfit in pips rather than actual price levels. Like this:
OrderSend(EURUSD, OP_BUY, 0.1, 1.1606, 10, 20, 40);
This person tried to set a StopLoss of 20 pips and a TakeProfit of 40 pips. Big NO-NO….. The correct and only way of specifying your SL and TP is through price levels:
OrderSend(EURUSD, OP_BUY, 0.1, 1.1606, 10, 1.1585, 1.1645);
By the way, here we assumed that the current ASK price is 1.1606 and current BID price is 1.1605 (i.e. 1 pip spread).

2) 4-digits vs 5-digits

Another reason you could be getting ERR_INVALID_STOPS is if you are setting the input parameters of your EA in Pips (4-digit points) when the Robot is anticipating 5-digit points. Let’s look at an example:
extern int StopLoss = 20;
extern int TakeProfit = 40;

OrderSend(EURUSD, OP_BUY, 0.1, Ask, 10, Bid-StopLoss*Point(), Bid+TakeProfit*Point());

This code will work fine on a 4-digit broker, however will fail on a 5-digit broker. The reason is that on a 4-digit broker, Point() equals to 0.0001, whereas on a 5-digit broker Point() equals to 0.00001.

Basically, with no additional adjustments, on a 5-digit broker the EA will be attempting to set the StopLoss and TakeProfit at only 2 and 4 pips away from the Bid price respectively!

That’s why in the case of a 5-digit broker you have to increase your StopLoss and TakeProfit parameters tenfold. Like this:
extern int StopLoss = 200;
extern int TakeProfit = 400;

OrderSend(EURUSD, OP_BUY, 0.1, Ask, 10, Bid-StopLoss*Point(), Bid+TakeProfit*Point());
However, be careful! Some EA’s already have modules that will detect the number of digits after the decimal and will automatically adjust your input parameters for you. In these situations multiplying inputs by 10 can actually lead to erroneous performance.

Note: I plan on posting a separate article where we will discuss how to create our own modules to detect the number of digits after the decimal

3) ECN brokers

ECN accounts have their own specifics. One of them is – when trading through a ECN broker you will not be able to set a StopLoss and/or TakeProfit with your Market Order (BUY or SELL). If you try to do this – you will get Error 130.

However, of course, you do need to set a StopLoss (and maybe TakeProfit) for your order, and this must be done as soon as possible after the order has been executed. Try this code:
int MarketOrderSend(string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment, int magic)
<
int ticket;

ticket = OrderSend(symbol, cmd, volume, price, slippage, 0, 0, NULL, magic);
if(ticket void OnTick()
<
//.
OrderSend(EURUSD, OP_BUY, 0.1, ND(Ask), 10, ND(Bid-StopLoss*Point()), ND(Bid+TakeProfit*Point()));

double ND(double val)
<
return(NormalizeDouble(val, Digits));
>
This neat little trick allows you to normalize (in simple terms – Round) any prices that you are inputting into the OrderSend() function. This way you cut off all ‘negligible’ digits after the decimal point.

Conclusion

Today we saw that there may be multiple (at least 5) causes to error 130. Though this is quite a few, the underlying issues are all trivial and can be corrected in a matter of minutes.

Therefore, Error 130 should not be feared! If you have encountered this culprit, it’s just a matter of going through the list above, finding the situation that applies to you and applying the prescribed solution.

Hope you found this article useful!

Let me know if you have any questions by using the comments section below.

Источник

Открытие и установка ордеров

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

Функция 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 — название функции. Функция возвращает номер тикета (ticket — уникальный порядковый
номер ордера), который назначен ордеру торговым сервером или -1 в случае, если
торговый приказ был отклонён севером или клиентским терминалом. Для получения информации
о причине отклонения торгового приказа необходимо использовать функцию GetLastError()
(в дальнейшем изложении мы рассмотрим несколько наиболее распространённых ошибок).

symbol — наименование финансового инструмента, с которым проводится торговая операция.
Каждому финансовому инструменту поставлено в соответствие значение строковой переменной.
Например, для валютной пары Eur/Usd это значение &quot;EURUSD&quot;. Если открытие
ордера проводится по заранее известному финансовому инструменту, то этот параметр
можно указать в явном виде: &quot;EURUSD&quot;, &quot;EURGBP&quot; и т.д. Если
же предполагается использование советника в окне любого другого финансового инструмента,
то можно использовать стандартную функцию Symbol(). Эта функция возвращает строковое
значение, соответствующее названию того финансового инструмента, в окне которого
исполняется советник или скрипт.

cmd — тип операции. Тип операций может быть указан в виде предопределённой константы
или её значения и в соответствии с типом торговой операции.

volume — количество лотов. Для рыночных ордеров всегда проверяется достаточность свободных средств на счёте. Для отложенных ордеров
количество лотов не ограничено.

price — цена открытия. Устанавливается в соответствии с требованиями и ограничениями,
принятыми для проведения торговых операций (см. Характеристики ордеров). Если заявленной цены для открытия рыночных ордеров не было в ценовом потоке или
она сильно устарела, то такой торговый приказ отклоняется; если же цена устарела,
но присутствует в ценовом потоке и при этом отклонение от текущей цены находится
в пределах значения slippage, то такой торговый приказ будет принят клиентским
терминалом и отправлен на торговый сервер.

slippage — максимально допустимое отклонение заявленной цены открытия ордера от рыночной
цены для рыночных ордеров (пунктов). В случае установки отложенного ордера параметр
не обрабатывается.

stoploss — заявленная цена закрытия, определяющая предельный уровень убыточности. Устанавливается
в соответствии с ограничениями, принятыми для проведения торговых операций (см.
Характеристики ордеров, Требования и ограничения торговых операций).

takeprofit — заявленная цена закрытия, определяющая предельный уровень прибыльности. Устанавливается
в соответствии с ограничениями, принятыми для проведения торговых операций (см.
Характеристики ордеров, Требования и ограничения торговых операций).

comment — текст комментария ордера. Последняя часть комментария может быть изменена торговым
сервером.

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

expiration — срок истечения отложенного ордера. При наступлении этого срока отложенный ордер
будет автоматически закрыт на торговом сервере. На некоторых торговых серверах
может быть установлен запрет на применение срока истечения отложенных ордеров.
В этом случае при попытке задать ненулевое значение параметра торговый приказ будет
отклонён.

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

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

Открытие рыночных ордеров

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

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

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

cmd — для примера будем открывать ордер Buy, поэтому необходимо указать параметр OP_BUY;

volume — можно указать любое допустимое значение; мы будем открывать ордер небольшого размера, пусть это значение
будет 0.1;

price— цена открытия для ордера Buy — это цена Ask;

slippage — обычно это значение указывают в размере от 0 до 3 пунктов. Укажем значение 2;

stoploss — стоп-приказы могут быть выставлены не ближе минимальной дистанции, обычно 5 пунктов
(см. Требования и ограничения торговых операций); выставим стоп-приказы на расстоянии 15 пунктов от цены закрытия, а именно Bid
— 15*Point;

takeprofit — выставим стоп-приказы на расстоянии 15 пунктов от цены закрытия, а именно Bid +
15*Point;

Простейший скрипт simpleopen.mq4, предназначенный для открытия ордера Buy, выглядит так:

//--------------------------------------------------------------------
// simpleopen.mq4 
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------------
int start()                                  // Спец. функция start()
  {                                          // Открытие BUY
   OrderSend(Symbol(),OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point);
   return;                                   // Выход из start()
  }
//--------------------------------------------------------------------

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

1. Пользователь прикрепил скрипт к окну финансового инструмента — перетянул название
скрипта мышью из окна &quot;Навигатор&quot; клиентского терминала в окно финансового инструмента,
по которому он хочет открыть рыночный ордер Buy стоимостью 0.1 лота со стоп-приказами,
удалёнными от рыночного курса на 15 пунктов.

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

3. В рамках исполнения специальной функции start() управление передаётся в строку
вызова функции открытия ордера:

   OrderSend(Symbol(),OP_BUY,0.1,Ask,2,Bid-15*Point,Bid+15*Point);

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

3.1. Мы прикрепили скрипт в окно финансового инструмента Eur/Usd. В этом случае стандартная
функция Symbol() вернёт строковое значение EURUSD.

3.2. Пусть на момент обращения Ask =1.2852 и Bid =1.2850.

3.3. Значение StopLoss в этом случае будет равно 1.2850-15*0.0001 = 1.2835, а TakeProfit
= 1.2865.

4. Исполнение функции OrderSend():

4.1. Функция сформировала торговый приказ на открытие ордера и передала этот приказ
клиентскому терминалу.

4.2. Одновременно с торговым приказом функция передала клиентскому терминалу управление
и, таким образом, исполнение программы приостановилось.

4.3. Клиентский терминал произвёл проверку полученного торгового приказа, не обнаружил
некорректных параметров и отправил торговый приказ на сервер.

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

4.5. Сервер исполнил торговый приказ, произведя транзакцию в своей базе данных,
и отправил сведения об исполненном приказе торговому терминалу.

4.6. Торговый терминал получил информацию о том, что последний торговый приказ исполнен,
отразил это событие в окне терминала и в окне финансового инструмента и вернул
управление программе.

4.7. Получив управление, программа продолжила свою работу с того места, откуда ранее
управление передавалось в клиентский терминал (и куда впоследствии вернулось).

Обратите внимание, в период с п.4.2 по п.4.7 в программе не производилось никаких
действий — программа находилась в режиме ожидания ответа сервера.

5. Управление в программе передаётся следующему оператору — return.

6. Исполнение оператора return приводит к выходу из функции start() и, как следствие,
к окончанию исполнения программы (напомним, что скрипты после исполнения заканчивают
работу) — управление возвращается клиентскому терминалу.

Таким образом, скрипт выполнил своё предназначение: открыт ордер Buy с заданными
параметрами. Использование скриптов очень удобно, если необходимо выполнить небольшую
разовую операцию; в данном случае использование скрипта вполне оправданно. В соответствии
с п. 4.6. трейдер может наблюдать ордер на экране.


Рис. 81. Ордер выставлен скриптом
simpleopen.mq4.

Рассмотренный выше порядок событий не всегда проходит так гладко. Возможны случаи,
при которых исполнение торгового приказа будет отклонено клиентским терминалом
или сервером. Попробуем немного поэкспериментировать, например, изменим название финансового инструмента: укажем явно &quot;GBPUSD&quot;
(это вполне допустимо). Получится программа с ограниченной областью
использования:

int start()                               // Спец. функция start
  {                                       // Открытие BUY
   OrderSend("GBPUSD",OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point);
   return;                                // Выход из start()
  }

Запустим скрипт в том же окне финансового инструмента Eur/Usd. По замыслу, скрипт
должен открыть ордер в окне Gbp/Usd. Однако после присоединения скрипта в окно
Eur/Usd ожидаемого открытия ордера в окне Gbp/Usd не происходит.

Недостатком подобных программ является их функциональная ограниченность. В данном
случае, после присоединения скрипта в окно финансового инструмента пользователь
просто ждёт открытия ордера. Но открытия нет. Пользователь находится в неведении
относительно причины сложившейся ситуации: то ли причина в том, что в коде программы
имеется алгоритмическая ошибка, то ли торговый приказ &quot;потерялся&quot; на
пути к серверу, то ли торговый приказ давно отклонён клиентским терминалом (а пользователь
ждёт), то ли имеется другая причина.

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

Обработка ошибок

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

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

Функция GetLastError()

int GetLastError()

Функция возвращает код последней ошибки, после чего значение специальной переменной
last_error, в которой хранится код последней ошибки, обнуляется. Последующий вызов
GetLastError() вернет значение 0.

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

Ошибка 130. Неправильные стопы

Последний рассмотренный скрипт не производит анализ ошибок, поэтому пользователь
остаётся в неведении относительно результатов исполнения функции открытия ордера.
В простом варианте использования функции GetLastError() можно проанализировать
ошибку и просто сообщить о ней пользователю. Если скрипт confined.mq4
запустить на исполнение в окне Eur/Usd, то возникнет ошибка.

//--------------------------------------------------------------------
// confined.mq4 
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------------
int start()                                     // Спец. функция start
  {                                             // Открытие BUY
   OrderSend("GBPUSD",OP_BUY,0.1,Ask,2,Bid-15*Point,Bid+15*Point);
   Alert (GetLastError());                      // Сообщение об ошибке
   return;                                      // Выход из start()
  }
//--------------------------------------------------------------------

В этом скрипте добавлена всего одна, но очень содержательная строка:

   Alert (GetLastError());                      // Сообщение об ошибке

Функция GetLastError( ) возвращает код последней ошибки, а с помощью Alert() это
значение выводится на экран. После присоединения скрипта
confined.mq4 в окно финансового инструмента Eur/Usd скрипт исполнится, в результате чего пользователь
увидит следующее сообщение:


Рис. 82. Код ошибки, полученный при исполнении скрипта
confined.mq4 в окне Eur/Usd.

В приложении представлены Коды ошибок, которые могут возникать при исполнении программы. В данном
случае возникла ошибка 130 — неправильные стопы. Это значит, что значения формальных
параметров, используемых в функции OrderSend(), не удовлетворяют ограничениям,
указанным в Требования и ограничения торговых операций. При внимательном рассмотрении становится очевидной и причина возникшей ошибки:
текущие значения рыночной цены Bid и Ask берутся из того окна финансового инструмента,
в которое присоединён скрипт, а именно, из окна Eur/Usd. А используются эти значения
для формирования торгового приказа по финансовому инструменту Gbp/Usd. В результате,
при текущей цене Gbp/Usd Ask = 1.9655, значение TakeProfit для вновь открываемого
рыночного ордера оказывается равным (для Eur/Usd Bid =1.2930) 1.2930+15*0.0001=1.
2945, что значительно ниже предельно допустимого значения, т.е ошибочно.

В данном случае возникла алгоритмическая ошибка. Для того чтобы её
исправить, необходимо использовать
правильные значения цен по финансовому инструменту. Получить эти значения можно с помощью функции MarketInfo(). Скрипт improved.mq4
для открытия рыночного ордера по Gbp/Usd может быть запущен в окне любого
финансового инструмента:

//--------------------------------------------------------------------
// improved.mq4 
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------------
int start()                                     // Спец. функция start
  {
   double bid   =MarketInfo("GBPUSD",MODE_BID); // Запрос значения Bid
   double ask   =MarketInfo("GBPUSD",MODE_ASK); // Запрос значения Ask
   double point =MarketInfo("GBPUSD",MODE_POINT);//Запрос Point
   // Открытие BUY
   OrderSend("GBPUSD",OP_BUY,0.1,ask,2,bid-15*Point,bid+15*Point);
   Alert (GetLastError());                      // Сообщение об ошибке
   return;                                      // Выход из start()
  }
//--------------------------------------------------------------------

При исполнении этого скрипта рассматриваемая ошибка не возникает, поэтому в результате его исполнения будет
выведено соответствующее сообщение: 0 (ноль). Это значит, что функция GetLastError()
вернула значение 0, т.е. при исполнении торгового приказа клиентским терминалом
ошибок не выявлено.

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

Ошибка 129. Неправильная цена

В ряде случаев возникает банальная ошибка — в качестве цены открытия указывается
не то значение двухсторонней котировки. Как известно (см. Требования и ограничения торговых операций), рыночные ордера Buy открываются ценой Ask. Вот что получится, если
в скрипте mistaken.mq4 ошибочно указать Bid:

//--------------------------------------------------------------------
// mistaken.mq4 
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------------
int start()                                     // Спец. функция start
  {                                             // Открытие BUY
   OrderSend(Symbol(),OP_BUY,0.1,Bid,2,Bid-15*Point,Bid+15*Point);
   Alert (GetLastError());                      // Сообщение об ошибке
   return;                                      // Выход из start()
  }
//--------------------------------------------------------------------

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


Рис. 83. Ошибка 129 (неправильная цена) при исполнении
mistaken.mq4.

Ошибка 134. Недостаточно денег для совершения операции

Аналогичный результат (ошибка 134) будет получен и в случае, если свободных средств
не хватает для открытия ордера. Размер свободных средств, необходимых для открытия 1 лота на покупку для каждого финансового инструмента можно узнать с помощью функции MarketInfo(Имя_инструмента, MODE_MARGINREQUIRED).

Размер стандартного лота в разных дилинговых центрах для одного финансового инструмента
может отличаться.

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

Таблица 3. Варианты соотношения стоимости лотов и 1 пункта (валюта депозита- доллар США).

Дилинговый центр 1 Дилинговый центр 2 Дилинговый центр 3
Buy Sell 1pt Buy Sell 1pt Buy Sell 1pt
EUR/USD 1296.40 1296.20 10.00 1296.50 1296.20 10.00 1000.00 1000.00 10.00
GBP/USD 1966.20 1966.00 10.00 1376.48 1376.20 7.50 1000.00 1000.00 10.00
AUD/USD 784.40 784.20 10.00 1569.20 1568.40 20.00 1000.00 1000.00 10.00
USD/JPY 1000.00 1000.00 8.29 1000.00 1000.00 8.29 1000.00 1000.00 8.29
USD/CHF 1000.00 1000.00 8.02 1000.00 1000.00 8.02 1000.00 1000.00 8.02
EUR/CHF 1296.40 1296.20 8.02 1296.35 1296. 35 8.02 1000.00 1000.00 8.02

Цены даны по состоянию на 16.12.2007г.

Кратко рассмотрим распространённые методы расчёта стоимости 1 лота и 1 пункта.

Дилинговый центр 1 (наиболее распространённый вариант)

Для тех валютных инструментов, у которых в знаменателе указан USD, стоимость 1 лота
равна текущей цене соответствующей двухсторонней котировки, умноженной на 1000,
стоимость 1 пункта равна $10.

Для тех валютных инструментов, у которых USD указан в числителе, стоимость 1 лота
равна $1000.00, а стоимость 1 пункта обратно пропорциональна текущей котировке
и равна 1/(Bid). Например, для USD/CHF при Bid= 1.2466 стоимость 1 пункта = 1/1.
2466 = 8.02.

Для кросс-курсов стоимость 1 лота оценивается так, как оценивается стоимость валюты,
указанной в числителе, а стоимость 1 пункта так, как для валюты, указанной в знаменателе.
Например, для EUR/CHF стоимость 1 лота = 129.40 (как для EUR/USD), а стоимость 1
лота = 8.02 (как для USD/CHF).

Дилинговый центр 2

В некоторых дилинговых центрах при соблюдении того же общего порядка стоимостные
показатели для некоторых финансовых инструментов могут отличаться. Например, стоимость
лота и стоимость 1 пункта может быть пропорционально увеличена или уменьшена. Например,
для GBP/USD этот коэффициент составляет 0.75, а для AUD/USD составляет 2.0. Такое
представление стоимостных показателей не приводит ни к каким экономическим последствиям;
в таких случаях нужно просто учитывать эту особенность при расчёте стоимости ордера.
Обратите внимание также на особенность стоимости лотов для покупки и продажи активов
кросс-курсов — они совпадают.

Дилинговый центр 3

Существуют также такие дилинговые центры, которые устанавливают стоимость 1 лота
для любого финансового инструмента равной $1000.00. В то же время стоимость 1
пункта остаётся пропорциональной текущим ценам. Этим подразумевается, что для каждого
финансового инструмента устанавливается своё кредитное плечо.

Стоимость 1 пункта для всех финансовых инструментов, котируемых не по отношению
к USD, постоянно изменяется пропорционально стоимости той валюты, которая указана
в знаменателе.

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

Свободные средства

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

Рассмотрим пример. Пусть Баланс = 5000.00, в терминале нет открытых ордеров. Откроем
ордер Buy стоимостью 1 лот в дилинговом центре 3. В дилинговом центре 3 установлено
следующее правило:

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

В окне терминала отразится информация об открытом ордере. Обратите внимание,
залог составляет 1000.00, прибыль по ордеру -30.00, таким образом, количество свободных
средств составляет 5000-1000-30=3970.00:


Рис. 84. Ордер Buy в окне терминала.

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


Рис. 85. Ордер Buy и Sell в окне терминала.

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


Рис. 86. Ордер Buy и Sell в окне терминала.

В случае если будет открыт ещё один ордер Sell на 0.1 лота (стоимостью 100.00),
то меньшая суммарная стоимость однонаправленных ордеров составляет 700.00 + 100.
00 = 800.00. Таким образом, сумма залога (в сравнении с вариантом, когда открыт
только один ордер Buy) уменьшается на 800.00. В сравнении с вариантом, представленным
на рис. 86, залог уменьшается, а свободные средства увеличиваются на 100.00 (см.
рис. 87).


Рис. 87. Ордер Buy и Sell в окне терминала.

Количество свободных средств, отображаемых на рис. 86 и 87, отличаются более
чем на 100.00, т.к. при изменении текущего курса изменилось суммарное значение
прибыли по открытым ордерам (разница составляет 8.00).

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

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

Например, если в дилинговом центре 2 по USD/JPY ранее был открыт ордер Buy стоимостью
4 лота, то при открытии ордера Sell стоимостью 4 лота сумма залога и сумма свободных
средств не изменится.


Рис. 88. Наличие разнонаправленных ордеров не высвобождает залог.

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

Для того чтобы при первом знакомстве с условиями, предлагаемыми дилинговым центром,
выяснить размер свободных средств, требуемых для открытия ордера объемом в 1 лот, можно воспользоваться нехитрым скриптом
conditions.mq4:

//--------------------------------------------------------------------
// conditions.mq4 
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------------
int start()                                      // Спец.функция start
  {
   Alert(Symbol(),"  Sell = ",AccountFreeMargin()// При продаже
         -AccountFreeMarginCheck(Symbol(),OP_SELL,1));
   Alert(Symbol(),"  Buy = ",AccountFreeMargin() // При покупке
         -AccountFreeMarginCheck(Symbol(),OP_BUY,1));
   return;                                       // Выход из start()
  }
//--------------------------------------------------------------------

Здесь выражение

AccountFreeMargin() - AccountFreeMarginCheck(Symbol(),OP_SELL,1)

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

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


Рис. 89. Стоимость 1 лота для различных финансовых инструментов, полученная
с помощью conditions.mq4.

Если скрипт conditions.mq4 запустить на исполнение в окне финансового инструмента, по которому открыты рыночные
ордера, то можно получить и другие значения в зависимости от того, каким методом
расчёта пользуется тот или иной дилинговый центр.

Другие ошибки и функция MarketInfo()

Существуют и другие ограничения, связанные с определением значений параметров функции
открытия ордера OrderSend(). Это — максимальный и минимальный шаг изменения стоимости
ордера, максимальный и минимальный размер стоимости ордера и пр. Использование
функции MarketInfo() позволяет получить различную информацию по финансовым инструментам,
которые отражены в окне &quot;Обзор рынка&quot; клиентского терминала.

Функция MarketInfo()

double MarketInfo(string symbol, int type)

Функция возвращает различную информацию о финансовых инструментах, перечисленных
в окне &quot;Обзор рынка&quot;. Часть информации о текущем финансовом инструменте
хранится в предопределенных переменных.

Параметры:

symbol — символ инструмента;

type — идентификатор запроса, определяющий тип возвращаемой информации. Может быть любым
из значений идентификаторов запроса (см. Идентификаторы функции MarketInfo).

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

Для того чтобы программа работала устойчиво, с минимальным количеством отклонённых
торговых приказов, перед исполнением функции OrderSend() необходимо обновлять используемые программой параметры
информационного окружения с помощью функций MarketInfo()
и RefreshRates().

Пример простого скрипта, открывающего ордер Buy, стоимостью 35% от суммы свободных
средств, с некоторыми заданными значениями стоп-приказов (openbuy.mq4).

//--------------------------------------------------------------------
// openbuy.mq4 
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------- 1 --
int start()                                     // Спец.функция start
  {
   int Dist_SL =10;                             // Заданный SL (pt)
   int Dist_TP =3;                              // Заданный TP (pt)
   double Prots=0.35;                           // Процент своб. ср.
   string Symb=Symbol();                        // Финанс. инструмент
//--------------------------------------------------------------- 2 --
   while(true)                                  // Цикл открытия орд.
     {
      int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Мин. дистанция
      double Min_Lot=MarketInfo(Symb,MODE_MINLOT);// Мин. размер лота
      double Step   =MarketInfo(Symb,MODE_LOTSTEP);//Шаг изменен лотов
      double Free   =AccountFreeMargin();       // Свободн средства
      double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);//Стоим.1 лота
      //--------------------------------------------------------- 3 --
      double Lot=MathFloor(Free*ProtsOne_LotStep)*Step;// Лоты
      if (Lot &lt; Min_Lot)                        // Если меньше допуст
        {
         Alert(" Не хватает денег на ", Min_Lot," лотов");
         break;                                 // Выход из цикла
        }
      //--------------------------------------------------------- 4 --
      if (Dist_SL &lt; Min_Dist)                   // Если меньше допуст.
        {
         Dist_SL=Min_Dist;                      // Установим допуст.
         Alert(" Увеличена дистанция SL = ",Dist_SL," pt");
        }
      double SL=Bid - Dist_SL*Point;            // Заявленная цена SL
      //--------------------------------------------------------- 5 --
      if (Dist_TP &lt; Min_Dist)                   // Если меньше допуст.
        {
         Dist_TP=Min_Dist;                      // Установим допуст.
         Alert(" Увеличена дистанция TP = ",Dist_TP," pt");
        }
      double TP=Bid + Dist_TP*Point;            // Заявленная цена ТР
      //--------------------------------------------------------- 6 --
      Alert("Торговый приказ отправлен на сервер. Ожидание ответа..");
      int ticket=OrderSend(Symb, OP_BUY, Lot, Ask, 2, SL, TP);
      //--------------------------------------------------------- 7 --
      if (ticket&gt;0)                             // Получилось :)
        {
         Alert ("Открыт ордер Buy ",ticket);
         break;                                 // Выход из цикла
        }
      //--------------------------------------------------------- 8 --
      int Error=GetLastError();                 // Не получилось :(
      switch(Error)                             // Преодолимые ошибки
        {
         case 135:Alert("Цена изменилась. Пробуем ещё раз..");
            RefreshRates();                     // Обновим данные
            continue;                           // На след. итерацию
         case 136:Alert("Нет цен. Ждём новый тик..");
            while(RefreshRates()==false)        // До нового тика
               Sleep(1);                        // Задержка в цикле
            continue;                           // На след. итерацию
         case 146:Alert("Подсистема торговли занята. Пробуем ещё..");
            Sleep(500);                         // Простое решение
            RefreshRates();                     // Обновим данные
            continue;                           // На след. итерацию
        }
      switch(Error)                             // Критические ошибки
        {
         case 2 : Alert("Общая ошибка.");
            break;                              // Выход из switch
         case 5 : Alert("Старая версия клиентского терминала.");
            break;                              // Выход из switch
         case 64: Alert("Счет заблокирован.");
            break;                              // Выход из switch
         case 133:Alert("Торговля запрещена");
            break;                              // Выход из switch
         default: Alert("Возникла ошибка ",Error);// Другие варианты   
        }
      break;                                    // Выход из цикла
     }
//--------------------------------------------------------------- 9 --
   Alert ("Скрипт закончил работу -----------------------------");
   return;                                      // Выход из start()
  }
//-------------------------------------------------------------- 10 --

Скрипт состоит из одной специальной функции start() (блоки 1-10). В блоке 1-2 заданы
значения, при которых должен быть открыт ордер. Блок 2-9 представляет цикл while
(), в котором производятся необходимые вычисления. Наличие этого цикла предполагает
возможность нескольких попыток открытия ордера. В блоке 2-3 обновляются переменные
окружения. В блоках 3-4-5-6 вычисляются значение количества лотов и заявленные
цены стоп-приказов. В блоке 7-8-9 выполняется обработка ошибок. В блоке 9-10 печатается
сообщение о завершении работы скрипта.

Рассмотрим некоторые особенности программного кода. Легко заметить, что формирование
торгового приказа осуществляется в блоке 6-7. В предшествующем блоке 3-4 вычисляется
количество лотов. Учитывается вариант, при котором выделенных свободных средств
не хватит для открытия ордера даже на минимальное количество лотов. Поэтому в блоке
3-4, после опубликования сообщения о нехватке денег, осуществляется выход из цикла
2-9 с помощью оператора break. Управление передаётся в блок 9-10, и скрипт заканчивает
работу. Сообщение в блоке 9 не обязательно должно присутствовать. Здесь оно указано
только для того, чтобы при опробовании скрипта на демо-счёте было легче сориентироваться
— когда программа закончила работу, а когда возникла пауза, связанная с задержками
в сети или на сервере.

Если для открытия ордера свободных средств хватает, то управление передаётся в блок
4-5 и далее в блок 5-6. В этих блоках не предусмотрен выход из цикла. Этим полагается,
что для любой минимальной дистанции, установленной брокером, найдутся соответствующие
значения стоп-приказов. В блоке 1-2 намеренно указано значение 3 пункта для TP.
Подавляющее число брокеров устанавливают минимальную дистанцию 5 пунктов. В блоке
5-6 будет обнаружено, что заданное значение меньше допустимого, и установлено такое
значение цены стоп-приказа, которое не противоречит ограничению.

Далее управление передаётся в блок 6-7 для открытия ордера. В первой строке этого
блока публикуется сообщение и лишь во второй строке формируется торговый приказ.
Возникает вопрос: почему мы заявляем о формировании торгового приказа ещё до того,
как он сформирован? Ведь можно было бы сначала отдать приказ, а потом сообщить
об этом пользователю. Ответ на этот вопрос непосредственно связан с технологией
отправки приказа клиентскому терминалу и далее на сервер (см. рис. 66). В нашем
случае торговый приказ формируется в функции OrderSend(), указанной в правой части
оператора присваивания. Собственно торговый приказ создаётся и отправляется на
сервер в самой функции, а исполнение операции присвоения в операторе присваивания
будет выполнено уже после того, как сервер вернёт какой-либо ответ о судьбе данного
торгового приказа. Таким образом, единственная возможность сообщить пользователю
о начале событий, связанных с торговым приказом, — это указать сообщение перед
оператором присваивания, в правой части которого указана торговая функция.

Рано
или поздно клиентский терминал вернёт управление в программу, будет исполнен оператор
присваивания в блоке 6-7, в результате чего переменная ticket получит какое-то
значение, и управление будет передано далее — в блок анализа ошибок 7-8-9.

Если ордер открыт на сервере, то переменной ticket будет присвоен номер открытого
ордера. В этом случае скрипт выполнил своё предназначение и далее работу программы
необходимо прекратить. В блоке 7-8 используется оператор break для выхода из цикла
while(). Управление передаётся в блок 9-10 (за пределы цикла) и программа заканчивает
работу.

Если же попытка открыть ордер не увенчалась успехом, то управление передаётся в
блок 8-9 для анализа ошибок. Здесь рассматривается 2 вида ошибок — те, которые
позволяют ещё надеяться на успешное открытие и те, появление которых однозначно
указывает на необходимость прекратить исполнение программы. Переменной Error присваивается
код последней ошибки, в данном случае той ошибки, которая была возвращена сервером
или клиентским терминалом при исполнении торговой функции OrderSend().

В первом операторе switch блока 8-9 рассматриваются преодолимые ошибки. Каждая из
ошибок этой группы обрабатывается по-своему. Например, если изменилась цена (ошибка
135), то достаточно просто обновить параметры окружения с помощью RefreshRates()
и повторить попытку открытия ордера. Если же возникла ошибка &quot;нет цен&quot; (ошибка 136),
то нет смысла сразу снова отправлять торговый приказ на сервер. В этом случае необходимо
дождаться нового тика (на сервере в этот период тоже нет цен) и только после этого
произвести ещё одну попытку открытия ордера. Поэтому в блоке обработки ошибки 136
имеется цикл задержки, который будет прерван в результате поступления нового тика.
Выход из оператора switch() осуществляется с помощью оператора continue, который
прекращает текущую итерацию цикла while () и начинает новую.

Ошибки непреодолимой силы обрабатываются иначе. Если такая ошибка возникла, то программа
просто сообщит о ней пользователю и прекратит работу. Для этой цели в программе
используется оператор break (последний в блоке 8-9), прекращающий цикл while (),
в результате чего программа заканчивает работу.

Отдельно нужно заметить, что в данном примере намеренно не рассматриваются все без
исключения ошибки. В данном случае мы не ставим перед собой цель предоставить в
распоряжение пользователя готовую программу. Очень важно, чтобы программист сам
проанализировал другие варианты ошибок и самостоятельно принял решение о том, какие
ещё ошибки и как необходимо обработать в программе. В то же время, некоторые ошибки
не должны обрабатываться, потому что сама программа построена таким образом, что
не предполагает возможности для возникновения некоторых ошибок, например, в данном
случае — для ошибок 129 и 130.

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

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

Обратите внимание на код в блоке 4-5:

//--------------------------------------------------------------- 4 --
      if (Dist_SL&lt;Min_Dist)                     // Если меньше допуст.
        {
         Dist_SL=Min_Dist;                      // Установим допуст.
         Alert(" Увеличена дистанция SL = ",Dist_SL," pt");
        }
      double SL = Bid - Dist_SL*Point;          // Заявленная цена SL
//--------------------------------------------------------------- 5 --

В результате вычислений в теле оператора if () переменная Dist_SL может получить
новое значение. Предположим, что обычно минимальная дистанция составляет 5 пунктов.
Предположим, что при первом исполнении (на быстром рынке) это значение установлено
на сервере равным 20 пунктов. Переменная Min_Dist получит значение 20.

      int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Мин. дистанция

Предположим также, что сформированный торговый приказ был отклонён в результате
ошибки 136. Программа будет отслеживать новый тик в блоке 8-9. За это время на
сервере снова может быть изменено значение минимальной дистанции, на этот раз,
например,
уменьшено до 10 пунктов. С момента поступления нового тика управление будет передано
на новый цикл, будет вычислено новое значение переменной Min_Dist, равное 10. Однако
значение переменной Dist_SL останется неизменным, равным 20 (блок операторов 4-5
написан так, что значение Dist_SL может только увеличиваться). Чтобы исключить
эту алгоритмическую ошибку, блок 4-5 необходимо составить таким образом, чтобы
изменялось только то значение, которое зависит от ситуации, — в данном случае значение SL,
а значение Dist_SL не изменялось, например, так:

//--------------------------------------------------------------- 4 --
      double SL = Bid - Dist_SL*Point;          // Заявленная цена SL
      if (Dist_SL&lt;Min_Dist)                     // Если меньше допуст.
        {
         SL = Bid - Min_Dist*Point;             // Заявленная цена SL
         Alert(" Увеличена дистанция SL = ",Min_Dist," pt");
        }
//--------------------------------------------------------------- 5 --

Аналогичное изменение кода следует выполнить в блоке 5-6 и для другого
стоп-приказа.

Выставление отложенных ордеров

Программирование выставления отложенных ордеров принципиально ничем не отличается
от предназначенного для выставления рыночных.

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

Функция WindowPriceOnDropped()

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

double WindowPriceOnDropped()

Функция возвращает значение цены в точке графика, на которой был брошен эксперт
или скрипт. Значение будет верным только в случае, если эксперт или скрипт перемещены
с помощью мыши (технология &quot;drag and drop&quot;). Для пользовательских индикаторов
это значение не определено.

Пример простого скрипта, устанавливающего ордер BuyStop, стоимостью 35% от суммы
свободных средств, с некоторыми заданными значениями стоп-приказов (openbuystop.mq4).

//--------------------------------------------------------------------
// openbuystop.mq4 
// Предназначен для использования в качестве примера в учебнике MQL4.
//--------------------------------------------------------------- 1 --
int start()                                     // Спец.функция start
  {
   int Dist_SL =10;                             // Заданный SL (pt)
   int Dist_TP =3;                              // Заданный TP (pt)
   double Prots=0.35;                           // Процент своб. ср.
   string Symb=Symbol();                        // Финанс. инструмент
   double Win_Price=WindowPriceOnDropped();     // Здесь брошен скрипт
   Alert("Мышкой задана цена Price = ",Win_Price);// Задано мышей
//--------------------------------------------------------------- 2 --
   while(true)                                  // Цикл открытия орд.
     {
      int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Мин. дистанция
      double Min_Lot=MarketInfo(Symb,MODE_MINLOT);// Мин. размер лота
      double Free   =AccountFreeMargin();       // Свободн средства
      double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);//Стоим.лота
      double Lot=MathFloor(Free*ProtsOne_LotMin_Lot)*Min_Lot;// Лоты
      //--------------------------------------------------------- 3 --
      double Price=Win_Price;                   // Цена задана мышей
      if (NormalizeDouble(Price,Digits)&lt;        // Если меньше допуст.
         NormalizeDouble(Ask+Min_Dist*Point,Digits))
        {                                       // Только для BuyStop!
         Price=Ask+Min_Dist*Point;              // Ближе нельзя
         Alert("Изменена заявленная цена: Price = ",Price);
        }
      //--------------------------------------------------------- 4 --
      double SL=Price - Dist_SL*Point;          // Заявленная цена SL
      if (Dist_SL &lt; Min_Dist)                   // Если меньше допуст.
        {
         SL=Price - Min_Dist*Point;             // Заявленная цена SL
         Alert(" Увеличена дистанция SL = ",Min_Dist," pt");
        }
      //--------------------------------------------------------- 5 --
      double TP=Price + Dist_TP*Point;          // Заявленная цена ТР
      if (Dist_TP &lt; Min_Dist)                   // Если меньше допуст.
        {
         TP=Price + Min_Dist*Point;             // Заявленная цена TP
         Alert(" Увеличена дистанция TP = ",Min_Dist," pt");
        }
      //--------------------------------------------------------- 6 --
      Alert("Торговый приказ отправлен на сервер. Ожидание ответа..");
      int ticket=OrderSend(Symb, OP_BUYSTOP, Lot, Price, 0, SL, TP);
      //--------------------------------------------------------- 7 --
      if (ticket&gt;0)                             // Получилось :)
        {
         Alert ("Установлен ордер BuyStop ",ticket);
         break;                                 // Выход из цикла
        }
      //--------------------------------------------------------- 8 --
      int Error=GetLastError();                 // Не получилось :(
      switch(Error)                             // Преодолимые ошибки
        {
         case 129:Alert("Неправильная цена. Пробуем ещё раз..");
            RefreshRates();                     // Обновим данные
            continue;                           // На след. итерацию
         case 135:Alert("Цена изменилась. Пробуем ещё раз..");
            RefreshRates();                     // Обновим данные
            continue;                           // На след. итерацию
         case 146:Alert("Подсистема торговли занята. Пробуем ещё..");
            Sleep(500);                         // Простое решение
            RefreshRates();                     // Обновим данные
            continue;                           // На след. итерацию
        }
      switch(Error)                             // Критические ошибки
        {
         case 2 : Alert("Общая ошибка.");
            break;                              // Выход из switch
         case 5 : Alert("Старая версия клиентского терминала.");
            break;                              // Выход из switch
         case 64: Alert("Счет заблокирован.");
            break;                              // Выход из switch
         case 133:Alert("Торговля запрещена");
            break;                              // Выход из switch
         default: Alert("Возникла ошибка ",Error);// Другие варианты   
        }
      break;                                    // Выход из цикла
     }
//--------------------------------------------------------------- 9 --
   Alert ("Скрипт закончил работу -----------------------------");
   return;                                      // Выход из start()
  }
//-------------------------------------------------------------- 10 --

Структура скрипта openbuystop.mq4 построена так же, как в скрипте
openbuy.mq4, поэтому нет необходимости описывать его подробно. Остановимся только на основных
отличиях этих программ.

Цена, на уровне которой скрипт был прикреплён к окну финансового инструмента, определяется
в строке:

   double Win_Price=WindowPriceOnDropped();     // Здесь брошен скрипт

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

Нетрудно увидеть, что в скрипте
openbuystop.mq4 отсутствует проверка достаточности свободных средств для открытия ордера, но появилась
проверка значения цены открытия ордера (блок 3-4). Если вычисленное значение переменной
Price не удовлетворяет требованиям установки отложенного Stop ордера (см. Характеристики ордеров, Требования и ограничения торговых операций), то производится перерасчёт этого значения.

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

Разумные ограничения

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

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

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

ordersend-error-130

By popular demand, proven strategies on how to beat every algorithmic trader’s worst nightmare – Error 130

The OrderSend Error 130 appears in MetaTrader 4 when an Expert Advisor can’t execute a marker order as expected. Also known as the Invalid Stop (ERR_INVALID_STOPS) in MQL jargon, the Error 130 happens when the TakeProfit and StopLoss levels are set to close to the current market price.

Where does this error come from? What does it mean for your Expert Advisor? How can you find the part of your code that is causing the error? We tackle all this and more…

Video Tutorial

Alright! Let’s go ahead and send some orders with OrderSend. The video below is available if you prefer watching instead of reading

To start off, a formal definition from our friend, MQL4 Documentation:

ERR_INVALID_STOPS

That’s right! That is all you get from MetaQuotes. And the rest… Go figure!

Ordersend Error 130 is briefly mentioned in other sections of the documentation. However, there is no thorough guide to what “Invalid Stops” actually means and how to deal with this, perhaps, most common problem in Forex programming.

But not a worry! That’s why I have written this article. Let’s get through this together!

The silent killer

So… you launched your expert advisor and… nothing happens. No BUY orders, no SELL orders, no pending orders, not even error messages in the logs…. Just silence. You decide to wait a few hours / days / weeks, and nothing really changes – the charts go up and down, but you don’t see any profit. This can go on forever…

The real reason is simple – you’re actually getting ERR_INVALID_STOPS (which is the correct technical term for the issue), but you can’t see it. That’s because 130 is a silent killer. A cold-blooded murderer of your brain and inner calm 🙂

There is no way to pick up this error through expert advisor logs or even terminal logs. The only way to catch it is by adding the right failsafe mechanisms into your code. Here’s an example you can adapt to your code:
int ticket;
ticket = OrderSend("EURUSD", OP_BUY, 1.0, Ask, 10, StopLossLevel, TakeProfitLevel, "My 1st Order!");

if(ticket < 0)
{
Alert(“OrderSend Error: “, GetLastError());
}
else
{
Alert(“Order Sent Successfully, Ticket # is: ” + string(ticket));
}
What we are doing here is taking the ticket number and that OrderSend() returns and checking if it is less than zero. If yes, then that is a signal from MetaTrader 4 telling us that there was a problem with the request.

The error code is then printed out onto the screen using Alert() and the built-in GetLastError() function. This code will give a pop-up window like in the image up at the top of this article.

Note: you can use Print() instead of Alert() to redirect the message straight to the EA’s log instead of displaying it on the screen.

Core of Ordersend Error 130

Invalid stops is the real name for the culprit we are dealing with today. So what does invalid stops in MetaTrader 4 actually mean?

  • For a market order (BUY or SELL) invalid stops means that the StopLoss and/or TakeProfit you requested were not possible to set for your order. Therefore, since a request cannot be fulfilled only partially, the order was not executed at all
  • For a pending order (BUY STOP, BUY LIMIT, SELL STOP, or SELL LIMIT) invalid stops means that either (1) there were issues with the SL/TP (same as above) OR (2) the issue was with the entry price which you specified for the order itself

As we can see, the issue is always with one (or many) of the prices that your Forex Robot specified in its request to the trade server. Now that we know our enemy – let’s beat it!

1) StopLoss & TakeProfit are prices

There are several possible causes of ERR_INVALID_STOPS, and one of the more frequent ones among beginners is specifying the StopLoss and TakeProfit in pips rather than actual price levels. Like this:
OrderSend(EURUSD, OP_BUY, 0.1, 1.1606, 10, 20, 40);
This person tried to set a StopLoss of 20 pips and a TakeProfit of 40 pips. Big NO-NO….. The correct and only way of specifying your SL and TP is through price levels:
OrderSend(EURUSD, OP_BUY, 0.1, 1.1606, 10, 1.1585, 1.1645);
By the way, here we assumed that the current ASK price is 1.1606 and current BID price is 1.1605 (i.e. 1 pip spread).

2) 4-digits vs 5-digits

Another reason you could be getting ERR_INVALID_STOPS is if you are setting the input parameters of your EA in Pips (4-digit points) when the Robot is anticipating 5-digit points. Let’s look at an example:
extern int StopLoss = 20;
extern int TakeProfit = 40;

//…

OrderSend(EURUSD, OP_BUY, 0.1, Ask, 10, Bid-StopLoss*Point(), Bid+TakeProfit*Point());

This code will work fine on a 4-digit broker, however will fail on a 5-digit broker. The reason is that on a 4-digit broker, Point() equals to 0.0001, whereas on a 5-digit broker Point() equals to 0.00001.

Basically, with no additional adjustments, on a 5-digit broker the EA will be attempting to set the StopLoss and TakeProfit at only 2 and 4 pips away from the Bid price respectively!

That’s why in the case of a 5-digit broker you have to increase your StopLoss and TakeProfit parameters tenfold. Like this:
extern int StopLoss = 200;
extern int TakeProfit = 400;

//…

OrderSend(EURUSD, OP_BUY, 0.1, Ask, 10, Bid-StopLoss*Point(), Bid+TakeProfit*Point());
However, be careful! Some EA’s already have modules that will detect the number of digits after the decimal and will automatically adjust your input parameters for you. In these situations multiplying inputs by 10 can actually lead to erroneous performance.

Note: I plan on posting a separate article where we will discuss how to create our own modules to detect the number of digits after the decimal

3) ECN brokers

ECN accounts have their own specifics. One of them is – when trading through a ECN broker you will not be able to set a StopLoss and/or TakeProfit with your Market Order (BUY or SELL). If you try to do this – you will get Error 130.

However, of course, you do need to set a StopLoss (and maybe TakeProfit) for your order, and this must be done as soon as possible after the order has been executed. Try this code:
int MarketOrderSend(string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment, int magic)
{
int ticket;

ticket = OrderSend(symbol, cmd, volume, price, slippage, 0, 0, NULL, magic);
if(ticket <= 0) Alert(“OrderSend Error: “, GetLastError());
else
{
bool res = OrderModify(ticket, 0, stoploss, takeprofit, 0);
if(!res) { Alert(“OrderModify Error: “, GetLastError());
Alert(“IMPORTANT: ORDER #”, ticket, ” HAS NO STOPLOSS AND TAKEPROFIT”);}
}
return(ticket);
}
You can add this function to your code (at the very end) and then use it instead of OrderSend() in your main code. This function adds an extra step in the process of sending a Market Order.

First, it send the request to execute a market order stripping out the StopLoss and TakeProfit. Next, it modifies the newly opened market order by adding the desired SL and TP.

There is, of course, a risk that the order will be executed, but the modification will fail. However, in that case the function will promptly notify the trader that the StopLoss and TakeProfit have not been set.

Feel free to modify this function to suit your needs and trading style.

4) Stop-Levels

Stop-Levels are a mechanism for brokers to protect themselves from certain volatility and liquidity related risks. In simple terms, you will not be able to set your StopLoss or TakeProfit OR any pending order closer than a predetermined number of Pips to the current market price.

To find out what the Stop Level is for a specific currency pair you need to press CTRL+U on your keyboard, select the desired currency pair and click the “Properties” button as shown on the illustration below:

forex-symbol-properties

In this example the Stop Level for AUDUSD is 3 Pips. This means that you will not be able to set the StopLoss for your order closer than 3 Pips to the price at which the order will be opened.

This also means that any pending order will have to be set at least 3 Pips away from the current market price.

If you Robot tries to break these rules and set a StopLoss / TakeProfit or Pending Order within the Stop Level range, then it will get Error 130 “Invalid Stops”. So just be mindful of the Stop Level of the currency where your EA’s are trading – don’t specify excessively small StopLoss and TakeProfit parameters.

It is also worth noting that more exotic currency pairs can have much more significant Stop Levels. Fore example, for AUDNZD the Stop Level with the same broker as in the above example is 20 Pips. For GBPSEK (British Pound vs Swedish Krone) – it’s 100 Pips.

5) Normalization of doubles

With some brokers you will find that for an unknown reason the Ask and Bid prices are passed onto the trader with additional negligible digits after the decimal. For example:

Instead of 1.1606 the broker would give you 1.160600001

Now this phenomenon has no effect on manual trading, moreover since the MT4 terminal is hardwired to display a certain number of digits after the decimal point (either 4 or 5) – you will not be able to notice any difference at all!

However, these ‘negligible’ digits after the decimal can have a dramatic effect on Expert Advisors causing……… that’s right! Our old friend, OrderSend Error 130!

Here’s a strategy that I personally use to protect my Robots from this issue:
void OnTick()
{
//...
OrderSend(EURUSD, OP_BUY, 0.1, ND(Ask), 10, ND(Bid-StopLoss*Point()), ND(Bid+TakeProfit*Point()));

}

double ND(double val)
{
return(NormalizeDouble(val, Digits));
}
This neat little trick allows you to normalize (in simple terms – Round) any prices that you are inputting into the OrderSend() function. This way you cut off all ‘negligible’ digits after the decimal point.

Conclusion

Today we saw that there may be multiple (at least 5) causes to error 130. Though this is quite a few, the underlying issues are all trivial and can be corrected in a matter of minutes.

Therefore, Error 130 should not be feared! If you have encountered this culprit, it’s just a matter of going through the list above, finding the situation that applies to you and applying the prescribed solution.

Hope you found this article useful!

Let me know if you have any questions by using the comments section below.

Happy trading,

Kirill

P.S: if you liked what you read in this article, here you can find the full course:

Algorithmic Trading Course

What are you waiting for?

START LEARNING FOREX TODAY!

I’m trying to modify an order, but I keep Error modifying order!, error#130. I’m using an ECN broker, so I need to modify the order to set a stoploss/takeprofit.
What I am doing wrong?

int digits = MarketInfo( Symbol(), MODE_DIGITS );
if (      digits == 2 || digits == 3 ) pipdigits = 0.01;
else if ( digits == 4 || digits == 5 ) pipdigits = 0.0001;

selltakeprofit = Ask + ( takeprofit * pipdigits );
sellstoploss   = Ask - ( stoploss   * pipdigits );

ticket = OrderSend( Symbol(), OP_SELL, lotsize, Ask, 100, 0, 0, 0, 0, 0, CLR_NONE );
if ( ticket < 0 )
    {
       Print( "venda Order send failed with error #", GetLastError() );
       Print( "stop loss = ",                         sellstoploss );
     }
else
    {
       Print( "Order send sucesso!!" );
       Print( "Balance = ",                           AccountBalance() );
       Print( "Equity = ",                            AccountEquity() );

       bool res = OrderModify( ticket, 0, sellstoploss, selltakeprofit, 0 );

       if ( res == false )
         {
             Print( "Error modifying order!, error#", GetLastError() );
             Print( "sellstoploss ",                  sellstoploss );
             Print( "selltakeprofit ",                selltakeprofit );
             Print( "StopLevel ",                     StopLevel );
             Print( "Ask ",                           Ask );
          }
      else
        {
             Print( "Order modified successfully" );
         }
     }

Stanislav Kralin's user avatar

asked Dec 2, 2014 at 2:33

Filipe Ferminiano's user avatar

Filipe FerminianoFilipe Ferminiano

8,13425 gold badges99 silver badges172 bronze badges

Error #130 is ERR_INVALID_STOPS.

The most likely problem is that

a) the stoploss level you are inputting is too close to the order open price. This is dictated by

MarketInfo( Symbol(), MODE_STOPLEVEL ) // returns a min allowed distance [pts]

else

b) because you have not normalized the stoploss level with NormalizeDouble().

See below for a buy order example. In your example, i.e. for a sell order, note that you should be opening the order at the Bid price, not Ask as you have. Note also that the stoploss and takeprofit are usually calculated relative to the bid price, as the bid is what is displayed on your charts, unfortunately you just have to take the spread loss in stride.

Only other minor problem is that you input no colour for the last parameter in OrderModify(). Unlike in OrderSend(), these are not initialized by default in the function definition, so you should pass them really.

//--- get minimum stop level
   double minstoplevel = MarketInfo( Symbol(), MODE_STOPLEVEL );
   Print( "Minimum Stop Level=", minstoplevel, " points" );
   double price = Ask;
//--- calculated SL and TP prices must be normalized
   double stoploss   = NormalizeDouble( Bid - minstoplevel * Point, Digits );
   double takeprofit = NormalizeDouble( Bid + minstoplevel * Point, Digits );
//--- place market order to buy 1 lot
   int ticket = OrderSend( Symbol(), OP_BUY, 1, price, 3, stoploss, takeprofit, "My order", 16384, 0, clrGreen );

user3666197's user avatar

answered Dec 3, 2014 at 8:43

whitebloodcell's user avatar

whitebloodcellwhitebloodcell

2981 gold badge4 silver badges10 bronze badges

1

OrderModify() call may collide with not one, but two constraints

The first, being a trivial one — one cannot put SL/TP closer than your Broker allows via a MODE_STOPLEVEL defined distance.

The second, being a less visible one — one cannot change { SL | TP } in case a Broker defined freezing distance is visited by a respective XTO price ( an eXecute-Trade-Operation price, being { Ask for Short.SL & Short.TP | Bid for Long.TP & Long.SL } )

MarketInfo( Symbol(), MODE_STOPLEVEL ) // returns a min allowed distance [pts]

MarketInfo( Symbol(), MODE_FREEZELEVEL ) // returns a freezing distance [pts]

OrderSend() may be constrained on some ECN/STP account types

Another quite common condition set on STP/ECN systems ( introduced by the Broker’s inhouse Risk Management policy ) is that one is not allowed to setup TP/SL right at the OrderSend(), but has to leave these blank and upon a positive confirmation of the OrderSend(), submit a separate OrderModify() instruction for the given OrderTicketNumber to add ex-post the TP and/or SL price-level(s)

A NormalizeDouble()-whenever-possible practice is not separately commented here, as MetaQuotes Inc. publishes this as a must-do.


A recommended practice

Carefully review your Broker’s Terms & Conditions and consult with your Account Manager the complete mix of Broker-side policies that apply to your type of Trading Account.

answered Dec 13, 2014 at 18:55

user3666197's user avatar

When you execute a buy trade, your price is the Ask, your stoploss and takeprofit are reference to the opposite trade, as when closing you’re subject to the Bid price.

using this simple rule, when you buy your stoploss and takeprofit will be:

   double stoploss   = NormalizeDouble( Bid - minstoplevel * Point, Digits );
   double takeprofit = NormalizeDouble( Bid + minstoplevel * Point, Digits );

   int    ticket     = OrderSend( Symbol(),
                                  OP_BUY,
                                  lots,
                                  price,
                                  slippage,
                                  stoploss,
                                  takeprofit
                                  );

the opposite, when you sell:

   double stoploss   = NormalizeDouble( Ask + minstoplevel * Point, Digits );    
   double takeprofit = NormalizeDouble( Ask - minstoplevel * Point, Digits );

   int    ticket     = OrderSend( Symbol(),
                                  OP_SELL,
                                  lots,
                                  price,
                                  slippage,
                                  stoploss,
                                  takeprofit
                                  );

user3666197's user avatar

answered Nov 25, 2016 at 11:58

Ramzy's user avatar

RamzyRamzy

1171 silver badge13 bronze badges

Hello some ECN Brokers doesn’t allow send orders with SL and TP, So first send the Order without SL and TP , then Modify it and asign it SL and TP.

answered Jul 23, 2018 at 12:15

Simo's user avatar

SimoSimo

112 bronze badges

Actually the real issue is that your new stop loss price although for buy side is bigger than current stop loss, you still have to check if your new stop loss is actually smaller than current Bid price. If not you will get that Order Modify 130 error. I hope I make sense. And the opposite applies for the Sell side.

answered Jun 29, 2020 at 3:57

Henri Fanda's user avatar

A ) Fully comply with the MQL4 OrderSend() syntax requirements

int    anOrderTKT;                                // DECLARE int
double anOrderLotSIZE;                            // DECLARE double
string anOrderCommentSTRING;                      // DECLARE string

anOrderTKT = OrderSend( _Symbol,                  // CPU-glitch, is faster than calling Symbol(),
                        OP_SELL,                  // XTO.Type
                        anOrderLotSIZE,           // XTO.Size       [LT]s
                        Bid,                      // XTO.EntryPRICE { OP_BUY: Ask | OP_SELL: Bid }
                        100,                      // XTO.SLIPPAGE   [PT]s
                        0,                        // XTO.SL_PRICE
                        0,                        // XTO.TP_PRICE,
                        anOrderCommentSTRING,     // XTO.Comment
                        0,                        // XTO.MagNUM#
                        0,                        // XTO.PendingOrderEXPIRE
                        CLR_NONE                  // GUI.MarkerCOLOR
                        );                        // ==> { EMPTY | aTkt# }

Your code fails at setting a correct SHORT trade Entry-Price, as it shall read rather Bid, not Ask ( this error is hidden as it is effectively masked-out by a rather cosmic distance of 100 points in a tolerable slippage distance from the said price ).

Your code fails at assigning int ( 0 ) in place, where string is expected.

B) Error 130: == «invalid stops»

You shall verify with your Broker a few details:

  1. Does their Terms & Conditions allow to OrderSend() one-stop-instruction, incl, TP & SL, or does the Broker T&C require to first open a trade-position & only after that happens to allow an OrderModify() instruction to setup TP & SL price-levels?
  2. In either case, check your Broker T&C settings for STOPLEVEL & FREEZELEVEL distances, within which Broker rejects any TP & SL setup(s) or modification(s) thereof.

C) A good practice is not to assign into extern iterator-variables

While this is not a root-cause for your trouble, do get accustomed with an industry best practices, one of which is not to assign any value to a declared extern. Rather declare your own variable, that you control scope & assignments thereof, but leave extern(s) un-touched from your code side.

A ) Fully comply with the MQL4 OrderSend() syntax requirements

int    anOrderTKT;                                // DECLARE int
double anOrderLotSIZE;                            // DECLARE double
string anOrderCommentSTRING;                      // DECLARE string

anOrderTKT = OrderSend( _Symbol,                  // CPU-glitch, is faster than calling Symbol(),
                        OP_SELL,                  // XTO.Type
                        anOrderLotSIZE,           // XTO.Size       [LT]s
                        Bid,                      // XTO.EntryPRICE { OP_BUY: Ask | OP_SELL: Bid }
                        100,                      // XTO.SLIPPAGE   [PT]s
                        0,                        // XTO.SL_PRICE
                        0,                        // XTO.TP_PRICE,
                        anOrderCommentSTRING,     // XTO.Comment
                        0,                        // XTO.MagNUM#
                        0,                        // XTO.PendingOrderEXPIRE
                        CLR_NONE                  // GUI.MarkerCOLOR
                        );                        // ==> { EMPTY | aTkt# }

Your code fails at setting a correct SHORT trade Entry-Price, as it shall read rather Bid, not Ask ( this error is hidden as it is effectively masked-out by a rather cosmic distance of 100 points in a tolerable slippage distance from the said price ).

Your code fails at assigning int ( 0 ) in place, where string is expected.

B) Error 130: == «invalid stops»

You shall verify with your Broker a few details:

  1. Does their Terms & Conditions allow to OrderSend() one-stop-instruction, incl, TP & SL, or does the Broker T&C require to first open a trade-position & only after that happens to allow an OrderModify() instruction to setup TP & SL price-levels?
  2. In either case, check your Broker T&C settings for STOPLEVEL & FREEZELEVEL distances, within which Broker rejects any TP & SL setup(s) or modification(s) thereof.

C) A good practice is not to assign into extern iterator-variables

While this is not a root-cause for your trouble, do get accustomed with an industry best practices, one of which is not to assign any value to a declared extern. Rather declare your own variable, that you control scope & assignments thereof, but leave extern(s) un-touched from your code side.

Что значит 2010.02.01 17:00 MyExpert GBPUSD,M15: OrderModify error 130
почему обычно эта ошибка возникает?

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

Коды ошибок

GetLastError() — функция, возвращающая коды ошибок. Кодовые константы ошибок определены в файле stderror.mqh. Для вывода текстовых сообщений следует использовать функцию ErrorDescription(), определенную в файле stdlib.mqh.

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

Значение Описание
0 Нет ошибки
1 Нет ошибки, но результат неизвестен
2 Общая ошибка
3 Неправильные параметры
4 Торговый сервер занят
5 Старая версия клиентского терминала
6 Нет связи с торговым сервером
7 Недостаточно прав
8 Слишком частые запросы
9 Недопустимая операция нарушающая функционирование сервера
64 Счет заблокирован
65 Неправильный номер счета
128 Истек срок ожидания совершения сделки
129 Неправильная цена
130 Неправильные стопы
131 Неправильный объем
132 Рынок закрыт
133 Торговля запрещена
134 Недостаточно денег для совершения операции
135 Цена изменилась
136 Нет цен
137 Брокер занят
138 Новые цены
139 Ордер заблокирован и уже обрабатывается
140 Разрешена только покупка
141 Слишком много запросов
145 Модификация запрещена, так как ордер слишком близок к рынку
146 Подсистема торговли занята
147 Использование даты истечения ордера запрещено брокером
148 Количество открытых и отложенных ордеров достигло предела, установленного брокером.

Коды ошибок выполнения MQL4 программы:

Значение Описание
4000 Нет ошибки
4001 Неправильный указатель функции
4002 Индекс массива — вне диапазона
4003 Нет памяти для стека функций
4004 Переполнение стека после рекурсивного вызова
4005 На стеке нет памяти для передачи параметров
4006 Нет памяти для строкового параметра
4007 Нет памяти для временной строки
4008 Неинициализированная строка
4009 Неинициализированная строка в массиве
4010 Нет памяти для строкового массива
4011 Слишком длинная строка
4012 Остаток от деления на ноль
4013 Деление на ноль
4014 Неизвестная команда
4015 Неправильный переход
4016 Неинициализированный массив
4017 Вызовы DLL не разрешены
4018 Невозможно загрузить библиотеку
4019 Невозможно вызвать функцию
4020 Вызовы внешних библиотечных функций не разрешены
4021 Недостаточно памяти для строки, возвращаемой из функции
4022 Система занята
4050 Неправильное количество параметров функции
4051 Недопустимое значение параметра функции
4052 Внутренняя ошибка строковой функции
4053 Ошибка массива
4054 Неправильное использование массива-таймсерии
4055 Ошибка пользовательского индикатора
4056 Массивы несовместимы
4057 Ошибка обработки глобальныех переменных
4058 Глобальная переменная не обнаружена
4059 Функция не разрешена в тестовом режиме
4060 Функция не подтверждена
4061 Ошибка отправки почты
4062 Ожидается параметр типа string
4063 Ожидается параметр типа integer
4064 Ожидается параметр типа double
4065 В качестве параметра ожидается массив
4066 Запрошенные исторические данные в состоянии обновления
4067 Ошибка при выполнении торговой операции
4099 Конец файла
4100 Ошибка при работе с файлом
4101 Неправильное имя файла
4102 Слишком много открытых файлов
4103 Невозможно открыть файл
4104 Несовместимый режим доступа к файлу
4105 Ни один ордер не выбран
4106 Неизвестный символ
4107 Неправильный параметр цены для торговой функции
4108 Неверный номер тикета
4109 Торговля не разрешена
4110 Длинные позиции не разрешены
4111 Короткие позиции не разрешены
4200 Объект уже существует
4201 Запрошено неизвестное свойство объекта
4202 Объект не существует
4203 Неизвестный тип объекта
4204 Нет имени объекта
4205 Ошибка координат объекта
4206 Не найдено указанное подокно
4207 Ошибка при работе с объектом

Если Вы хотите изучать язык MQL или вам понравилась данная публикация — Вы можете подписаться на получение новых материалов сайта mql4you.ru по

RSS

или по e-mail:

Другие публикации рубрики «FAQ по MQL и Metatrader»:

  • Вопрос №9 «Как заставить работать советник при выключенном компьютере?»
  • Вопрос №8 «Как рассчитать размер лота в зависимости от размера стоплосса?»
  • Можно ли настроить нестандартный тайм-фрейм в MT4? — Вопрос №7
  • Вопрос №6 «Как уменьшить размер папки с терминалом MT4?»
  • Вопрос №4 — «Выключение компьютера из MQL»
  • Вопрос №3
  • Вопрос №2
  • Вопрос №1
  • В рубриках: FAQ по MQL и Metatrader
  • Понравилась статья? Поделить с друзьями:
  • Ошибка 13 планар 44д 4 квт
  • Ошибка 13 опель синтра
  • Ошибка 13 опель вектра а c20ne
  • Ошибка 13 на триколор после простоя как исправить ошибку
  • Ошибка 13 на триколор как исправить ошибку самостоятельно