2009.10.27 22:49:11 2009.04.01 03:23 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50975000 sl 1.32780000 tp 1.51157000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 03:23 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 03:15 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50908000 sl 1.32713000 tp 1.51090000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 03:15 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 03:10 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50853000 sl 1.32658000 tp 1.51035000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 03:10 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 03:10 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50843000 sl 1.32648000 tp 1.51025000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 03:10 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 03:10 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50848000 sl 1.32653000 tp 1.51030000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 03:10 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 03:09 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50803000 sl 1.32608000 tp 1.50985000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 03:09 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 02:59 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50668000 sl 1.32472000 tp 1.50850000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 02:59 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50749000 sl 1.32553000 tp 1.50931000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50754000 sl 1.32558000 tp 1.50936000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50759000 sl 1.32563000 tp 1.50941000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50763000 sl 1.32567000 tp 1.50945000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend error 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend()- Ошибка OP_BUY. op 1.50768000 sl 1.32572000 tp 1.50950000 lot 0.11000000 free margin 164043.29600000 err 131
2009.10.27 22:49:11 2009.04.01 02:58 (777) YODJ EURCHF,M1: OrderSend error 131
такая воот ошибка, не могу понять откуда берется….
нормалайз все значения, деньги есть (в приведенном логе видно), вообщем что то я в тупике :)) какие объемы? все выставленно нормально -(
Содержание
- Не полное закрытие ордера.
- Handling OrderSend Error 131 in MetaTrader 4
- OrderSend: Error code 131
- Помогите исправить ошибку OrderModify error 1 и OrderSend error 131!
Не полное закрытие ордера.
Господа, я знаю что значит эта ошибка. я не пойму почему она выскакивает.
Нельзя роботом закрыть часть открытой позиции? При этом оставить отрытой часть не меньше минимального лота?
приведенные вами примеры сводятся к тому, что робот посылает не верный лот, или меньше минимально, или больше максимального или не нормализованный, т.е. с большим количеством знаков после запятой . в моем случае лот нормализован, больше или равен минимальному и меньше лота открытого ордера который хочу частично покрыть.
сделайте для начала закрытие просто 0,1 лота.
затем проверьте как закрывает просто объем по MODE_MINLOT.
Если получится. то работайте с тем, что надо N*. и ищите ошибку в распринтовке.
Господа, я знаю что значит эта ошибка. я не пойму почему она выскакивает.
Нельзя роботом закрыть часть открытой позиции? При этом оставить отрытой часть не меньше минимального лота?
приведенные вами примеры сводятся к тому, что робот посылает не верный лот, или меньше минимально, или больше максимального или не нормализованный, т.е. с большим количеством знаков после запятой. в моем случае лот нормализован, больше или равен минимальному и меньше лота открытого ордера который хочу частично покрыть.
void _OrderCloseByTrend(int _OrderCloseType) <
int _closeType=-1,
i,
_ticket=-1,
_points=0;
double _summ=0.0,
_closePrice=0,
_loss=0.0,
_lots=0.0;
if (_OrderCloseType==OP_BUY) <
_closeType=OP_SELL;
_closePrice=Ask;
> // End of if (_OrderCloseTyp==OP_BUY)
if (_OrderCloseType==OP_SELL) <
_closeType=OP_BUY;
_closePrice=Bid;
> // End of if (_OrderCloseType==OP_SELL)
Alert(«Закрывае прибыльные «+_TypeToStr(_closeType)+» ордера!»);
if (OrdersTotal()>0) <
for (i=OrdersTotal()-1; i>=0; i—) <
if (OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) <
if (OrderType()==_closeType && OrderProfit()>0) <
_summ=_summ+OrderProfit();
while (!OrderClose(OrderTicket(),OrderLots(),_closePrice,0,0)) <
RefreshRates();
if (_closeType==OP_BUY) _closePrice=Bid;
if (_closeType==OP_SELL) _closePrice=Ask;
>// End of while
>// End of if (OrderType()==_closeType && OrderProfit()>0)
>// End of if (OrderSelect(i,SELECT_BY_POS,MODE_TRADES)
>// End of for
>// End of if (OrdersTotal()>0)
Alert(«Закрывае убыточные «+_TypeToStr(_closeType)+» ордера!»);
while (OrdersTotal()>0 && _summ>0) <
_ticket=-1;
for (i=OrdersTotal()-1; i>=0; i—) < // Ищем самый убыточный ордер
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) <
if(OrderType()==_closeType && OrderProfit() 0)
else <
_lots=OrderLots();
> // End of else
_summ=_summ+OrderProfit();
Alert(«Пытаемся закрыть ордер — «,_ticket,», Объемом — «,_lots);
while (!OrderClose(_ticket,_lots,_closePrice,0,0)) <
RefreshRates();
if (_closeType==OP_BUY) _closePrice=Bid;
if (_closeType==OP_SELL) _closePrice=Ask;
> // End of while
> // End of if (OrderSelect(_ticket, SELECT_BY_TICKET)
>//End of while
return;
>
Источник
OrderSend Error 131 is a very frequent problem that is usually encountered when testing MT4 expert advisors. What causes this error? It is called ERR_INVALID_TRADE_VOLUME in the MetaTrader 4 documentation. It means that your expert advisor (EA) is trying to send an order with invalid trade volume. At the absolute majority of the MT4 brokers, setting some EA to open an order with 0.123 lots volume will generate this error. But sometimes, it is generated when an EA created for mini or micro accounts is used on a standard account. If you stumble upon an OrderSend Error 131 during your strategy testing, you can quickly find the wrong settings of your EA. To do so, find the standard OnInit() (or init() in older versions of MT4) function inside your EA’s code and insert these lines of code there:
The output will be valid for the current chart’s trading symbol and for the trading account type you are currently logged in. The first line will tell you how many units there are in one lot of the current trading symbol (100,000 would mean a lot). Remember that in your expert advisor’s log, this line will be first from the bottom. The second line will tell you the minimum amount of lots you can trade (this is the most usual error; you probably just need to fix the amount of lots your EA trades from 0.1 to 1). The third one will give the step size for the trade volume in lots. The fourth line will tell you the maximum number of lots that your EA can trade.
For example, a Standard trading account at Exness Forex broker generates the following information when the above-mentioned lines of code are executed on a EUR/USD chart:
This means that 1 lot comprises 100,000 units (a standard size), the minimum trade volume is 0.01 lot (so, you can trade starting from $0.10 per pip in a currency pair), the minimum trade volume step is also 0.01 lot (you can trade 0.33, 0.4 or 1.25 lot volumes, but you cannot send orders with 0.333 lot size), and the maximum volume you can use to open a position is 200 lots.
You can incorporate the MarketInfo() function at a more complex level into your EA, so that it could automatically check the allowed values and auto-correct the EA’s trade parameters. But if you don’t want to code much, you can just use the code above to find out the right values and correct the settings manually.
You can discuss your personal struggles with the OrderSend Error 131 problem on our forum if you are having trouble solving this issue on your own.
If you want to get news of the most recent updates to our guides or anything else related to Forex trading, you can subscribe to our monthly newsletter.
Источник
OrderSend: Error code 131
You should backtest the EA with a larger lot size that exceeds the margin limit,
or the broker lot size limit. Then, of course you will get an error,
— because the validation process performs that stage.
So, now you can set a new function to handle it automatically.
You can use this function to normalize volume:
You have to check if the returned value isn’t zero (it means that your requested volume is less than the minimum allowed volume).
I am using this code for Money Management, it uses NormalizeDouble for round lots and minimum lot size (0.01):
So I think MQL5 system doesn’t admit less than 0.1 lot, is it true?
You can use this function to normalize volume:
You have to check if the returned value isn’t zero (it means that your requested volume is less than the minimum allowed volume).
Yohana Parmi :
131 | Invalid trade volume. |
You should backtest the EA with a larger lot size that exceeds the margin limit,
or the broker lot size limit. Then, of course you will get an error,
— because the validation process performs that stage.
So, now you can set a new function to handle it automatically.
I tried this and doesn’t give that error anymore.
Now it gives a new error code 134, I tried this:
And now error 131 again.
I tried this and doesn’t give that error anymore.
Now it gives a new error code 134, I tried this:
And now error 131 again.
«If a check shows that there are insufficient funds to perform a trade operation, it is necessary to output an error message to the log instead of calling the OrderSend() function».
Just added that error message and passed the validation. Thanks to all.
Источник
Помогите исправить ошибку OrderModify error 1 и OrderSend error 131!
Советник по принципу Мартингейла, вход по сигналам индикатора CCI, eurusd, M5. Результат теста не плохой! Но..
После теста советника выдает ошибку: OrderModify error 1 и OrderSend error 131. Лот используется 1.0
Ничего не получается! Помогите наглядно поправить код советника или правильно вписать функцию отправки запроса на модификацию уровней и проверку корректности объема. Спасибо!
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
#property copyright «»
#property link «»
#include
#include
//ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
extern string MM_set = «Money Managment settings» ;
extern double Lot = 0.01 ; // разер лота для начала торгов
extern int lotdecimal = 2 ; // сколько знаков после запятой в лоте рассчитывать 0 — нормальные лоты (1), 1 — минилоты (0.1), 2 — микро (0.01)
extern double ExpoLot = 1.2 ; // на сколько умножать лот при выставлении следующего колена. пример: первый лот 0.1, серия: 0.16, 0.26, 0.43 .
bool DynamicPips = true ;
int DefaultPips= 0 ;
extern int Depth = 12 ;
extern int Grid = 2 ;
extern double slip = 2.0 ; // на сколько может отличаться цена в случае если ДЦ запросит реквоты (в последний момент немного поменяет цену)
extern double TakeProfit = 10.0 ; // по достижении скольких пунктов прибыли закрывать сделку
extern double Drop = 1500 ;
int PipStep= 0 ;
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
extern int MaxTrades = 10 ; // максимально количество одновременно открытых ордеров
extern bool UseEquityStop = FALSE ;
extern double TotalEquityRisk = 20.0 ;
extern int MagicNumber = 2222 ; // волшебное число (помогает советнику отличить свои ставки от чужих)
//extern bool UseTrailingStop = FALSE;
bool UseTimeOut = FALSE ; // использовать таймаут (закрывать сделки если они «висят» слишком долго)
double MaxTradeOpenHours = 48.0 ; // время таймаута сделок в часах (через сколько закрывать зависшие сделки)
extern string CCI_set = «CCI settings» ;
extern int CCI_Period = 21 ;
extern int CCI_Price = 2 ;
extern double CCI_Max = 300 ;
extern double CCI_Min = — 300 ;
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
double PriceTarget, StartEquity, BuyTarget, SellTarget;
double AveragePrice, SellLimit, BuyLimit;
double LastBuyPrice, LastSellPrice, Spread;
bool flag;
string EAName= «CCI Martin» ;
int timeprev = 0 , expiration;
int NumOfTrades = 0 ;
double iLots;
int cnt = 0 , total;
double Stopper = 0.0 ;
bool TradeNow = FALSE , LongTrade = FALSE , ShortTrade = FALSE ;
int ticket,Error;
bool NewOrdersPlaced = FALSE , NeedModifyOrder = False ;
double AccountEquityHighAmt, PrevEquity;
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
int init() <
Spread = MarketInfo ( Symbol (), MODE_SPREAD ) * Point ;
return ( 0 );
>
int deinit() <
return ( 0 );
>
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
int start()
<
double PrevCl;
double CurrCl;
//*защита от резкого движения*//
if (( iCCI ( NULL , 30 , 55 , 0 , 0 )>Drop && ShortTrade)||( iCCI ( NULL , 30 , 55 , 0 , 0 ) Print ( «Closed All due to TimeOut» );
>
if (timeprev == Time [ 0 ]) return ( 0 ); //new bar apear check
timeprev = Time [ 0 ];
if (DynamicPips)
<
double hival= High [ iHighest ( NULL , 0 , MODE_HIGH ,Depth, 1 )]; // calculate highest and lowest price from last bar to 24 bars ago
double loval= Low [ iLowest ( NULL , 0 , MODE_LOW ,Depth, 1 )]; // chart used for symbol and time period
PipStep= NormalizeDouble ((hival-loval)/Grid/ Point , 0 ); // calculate pips for spread between orders
Print ( «PipStep = » ,PipStep);
>
double CurrentPairProfit = CalculateProfit();
if (UseEquityStop)
if (CurrentPairProfit 0.0 && MathAbs (CurrentPairProfit) > TotalEquityRisk / 100.0 * AccountEquityHigh())
<
CloseThisSymbolAll();
Print ( «Closed All due to Stop Out» );
NewOrdersPlaced = FALSE ;
>
total = CountOfOrders(); //calculating open orders function
if (total > 0 && total Print ( «total > 0» );
for ( int i = 0 ; i OrdersTotal (); i++)
if ( OrderSelect ( i, SELECT_BY_POS , MODE_TRADES ))
if ( OrderSymbol () == Symbol () && OrderMagicNumber () == MagicNumber)
<
switch ( OrderType ())
<
case OP_BUY :
LongTrade = TRUE ;
ShortTrade = FALSE ;
break ;
case OP_SELL :
LongTrade = FALSE ;
ShortTrade = TRUE ;
break ;
>
break ;
>
LastBuyPrice = FindLastBuyPrice();
LastSellPrice = FindLastSellPrice();
if (LongTrade && LastBuyPrice — Ask >= PipStep * Point ) TradeNow = TRUE ;
if (ShortTrade && Bid — LastSellPrice >= PipStep * Point ) TradeNow = TRUE ;
>
else
<
Print ( «total = 0» );
ShortTrade = FALSE ;
LongTrade = FALSE ;
TradeNow = TRUE ;
StartEquity = AccountEquity ();
>
if (total > 0 )
if (TradeNow)
<
iLots = NormalizeDouble (Lot * MathPow (ExpoLot, total), lotdecimal);
if (ShortTrade)
<
ticket = SendMarketOrder( OP_SELL , iLots, 0 , 0 , MagicNumber, EAName + «-» + NumOfTrades + «-» + PipStep);
>
if (LongTrade)
<
ticket = SendMarketOrder( OP_BUY , iLots, 0 , 0 , MagicNumber, EAName + «-» + NumOfTrades + «-» + PipStep);
>
if (ticket > 0 )
<
TradeNow = FALSE ;
NewOrdersPlaced = TRUE ;
NeedModifyOrder = TRUE ;
>
else
return ( 0 );
>
if (TradeNow && total 1 ) <
Print ( «Open first order» );
ticket = 0 ;
PrevCl = iClose ( Symbol (), 0 , 2 );
CurrCl = iClose ( Symbol (), 0 , 1 );
if (PrevCl > CurrCl)
<
Print ( «Achieved condition SELL» );
if ( iCCI ( Symbol (), Period (), CCI_Period, CCI_Price, 1 ) Print ( «successful check» );
ticket = SendMarketOrder( OP_SELL , Lot, TakeProfit, 0 , MagicNumber, EAName + «-» + total);
>
>
if (PrevCl Print ( «Achieved condition BUY» );
if ( iCCI ( Symbol (), Period (), CCI_Period, CCI_Price, 1 ) >= CCI_Max)
<
Print ( «successful check» );
ticket = SendMarketOrder( OP_BUY , Lot, TakeProfit, 0 , MagicNumber, EAName + «-» + total);
>
>
if (ticket > 0 )
<
TradeNow = FALSE ;
NewOrdersPlaced = TRUE ;
NeedModifyOrder = False ;
>
else
return ( 0 );
>
if (ShortTrade)
<
PriceTarget = AveragePrice — TakeProfit * Point ;
NeedModifyOrder = TRUE ;
>
if (LongTrade)
<
PriceTarget = AveragePrice + TakeProfit * Point ;
NeedModifyOrder = TRUE ;
>
>
if (NewOrdersPlaced)
if (NeedModifyOrder)
<
Print ( «Modify all orders in the market» );
for ( int i1 = 0 ; i1 OrdersTotal (); i1++)
if ( OrderSelect (i1, SELECT_BY_POS , MODE_TRADES ))
if ( OrderSymbol () == Symbol () && OrderMagicNumber () == MagicNumber)
ModifyOrder(PriceTarget);
NewOrdersPlaced = FALSE ;
>
return ( 0 );
>
//ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
int CountOfOrders()
<
int count = 0 ;
for ( int i = 0 ; i OrdersTotal (); i++)
if ( OrderSelect (i, SELECT_BY_POS , MODE_TRADES ))
if (( OrderSymbol () == Symbol ()) && ( OrderMagicNumber () == MagicNumber))
if (( OrderType () == OP_SELL ) || ( OrderType () == OP_BUY ))
count++;
return (count);
>
void CloseThisSymbolAll() <
for ( int trade = OrdersTotal () — 1 ; trade >= 0 ; trade—) <
if (! OrderSelect (trade, SELECT_BY_POS , MODE_TRADES )) Print ( «error!» );
if ( OrderSymbol () == Symbol ()) <
if ( OrderSymbol () == Symbol () && OrderMagicNumber () == MagicNumber) <
if ( OrderType () == OP_BUY ) int ticket1= OrderClose ( OrderTicket (), OrderLots (), Bid , slip, Blue);
if ( OrderType () == OP_SELL ) ticket1= OrderClose ( OrderTicket (), OrderLots (), Ask , slip, Red);
>
Sleep ( 1000 );
>
>
>
//=========================================================================
int SendMarketOrder( int Type, double Lot1, int TP, int SL, int Magic, string Cmnt)
<
double Price, Take, Stop;
int Ticket, Slippage, Color, Err;
bool Delay = False ;
Print ( «SendMarketOrder function» );
while (! IsStopped ())
<
/*if(!IsExpertEnabled())
<
Error = ERR_TRADE_DISABLED;
Print(«EA trading is forbidden!»);
return(-1);
>
Print(«EA trading is allowed»);*/
if (! IsConnected ())
<
Error = ERR_NO_CONNECTION ;
Print ( «Communication is absent!» );
return (- 1 );
>
Print ( «Communication with the server is installed» );
if ( IsTradeContextBusy ())
<
Print ( «The trade stream is occupied!» );
Print ( «waiting 3 sec. » );
Sleep ( 3000 );
Delay = True ;
continue ;
>
Print ( «The trade stream is free» );
if (Delay)
<
Print ( «updating quotes» );
RefreshRates ();
Delay = False ;
>
else
<
Print ( «Delays weren’t» );
>
switch (Type)
<
case OP_BUY :
Print ( «initialize parameters for BUY-order» );
Price = NormalizeDouble ( Ask , Digits );
Take = IIFd(TP == 0 , 0 , NormalizeDouble ( Ask + TP * Point , Digits ));
Stop = IIFd(SL == 0 , 0 , NormalizeDouble ( Ask — SL * Point , Digits ));
Color = Blue;
break ;
case OP_SELL :
Print ( «iinitialize parameters for SELL-order» );
Price = NormalizeDouble ( Bid , Digits );
Take = IIFd(TP == 0 , 0 , NormalizeDouble ( Bid — TP * Point , Digits ));
Stop = IIFd(SL == 0 , 0 , NormalizeDouble ( Bid + SL * Point , Digits ));
Color = Red;
break ;
default :
Print ( «order type doesn’t conform to requirements.» );
return (- 1 );
>
Slippage = MarketInfo ( Symbol (), MODE_SPREAD );
Print ( «Slippage = » ,Slippage);
if ( IsTradeAllowed ())
<
Print ( «Trading is allowed, sending order. » );
Ticket = OrderSend ( Symbol (), Type, Lot1, Price, Slippage, Stop, Take, Cmnt, Magic, 0 , Color);
if (Ticket 0 )
<
Err = GetLastError ();
if (Err == 4 || /* SERVER_BUSY */
//Err == 130 || /* INVALID_STOPS */
Err == 135 || /* PRICE_CHANGED */
Err == 137 || /* BROKER_BUSY */
Err == 138 || /* REQUOTE */
Err == 146 || /* TRADE_CONTEXT_BUSY */
Err == 136 ) /* OFF_QUOTES */
<
Print ( «error» ) ;
Print ( «waitng 3 sec. » );
Sleep ( 3000 );
Delay = True ;
continue ;
>
else
<
Print ( «Critical error» );
Error = Err;
break ;
>
>
break ;
>
else
<
Print ( «EA trading is forbidden or trade stream is occupied!» );
Print ( «waitng 3 sec. » );
Sleep ( 3000 );
Delay = True ;
continue ;
>
>
Print ( «The order has been sent successfully. Тicket = » ,Ticket);
return (Ticket);
>
//==================================================================
double IIFd( bool condition, double ifTrue, double ifFalse)
<
if (condition) return (ifTrue); else return (ifFalse);
>
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
double CalculateProfit()
<
double Profit = 0 ;
for ( int i = 0 ; i OrdersTotal (); i++)
if ( OrderSelect (i, SELECT_BY_POS , MODE_TRADES ))
if (( OrderSymbol () == Symbol ()) && ( OrderMagicNumber () == MagicNumber))
if (( OrderType () == OP_BUY ) || ( OrderType () == OP_SELL ))
Profit += OrderProfit ();
return (Profit);
>
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
double AccountEquityHigh() <
if (CountOfOrders() == 0 ) AccountEquityHighAmt = AccountEquity ();
if (AccountEquityHighAmt else AccountEquityHighAmt = AccountEquity ();
PrevEquity = AccountEquity ();
return (AccountEquityHighAmt);
>
//нннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
double FindLastBuyPrice() <
double oldorderopenprice;
int oldticketnumber;
double unused = 0 ;
int ticketnumber = 0 ;
for (cnt = OrdersTotal () — 1 ; cnt >= 0 ; cnt—) <
if (! OrderSelect (cnt, SELECT_BY_POS , MODE_TRADES )) Print ( «error!» );
if ( OrderSymbol () != Symbol () || OrderMagicNumber () != MagicNumber) continue ;
if ( OrderSymbol () == Symbol () && OrderMagicNumber () == MagicNumber && OrderType () == OP_BUY ) <
oldticketnumber = OrderTicket ();
if (oldticketnumber > ticketnumber) <
oldorderopenprice = OrderOpenPrice ();
unused = oldorderopenprice;
ticketnumber = oldticketnumber;
>
>
>
return (oldorderopenprice);
>
double FindLastSellPrice() <
double oldorderopenprice;
int oldticketnumber;
double unused = 0 ;
int ticketnumber = 0 ;
for (cnt = OrdersTotal () — 1 ; cnt >= 0 ; cnt—) <
if (! OrderSelect (cnt, SELECT_BY_POS , MODE_TRADES )) Print ( «error» );
if ( OrderSymbol () != Symbol () || OrderMagicNumber () != MagicNumber) continue ;
if ( OrderSymbol () == Symbol () && OrderMagicNumber () == MagicNumber && OrderType () == OP_SELL ) <
oldticketnumber = OrderTicket ();
if (oldticketnumber > ticketnumber) <
oldorderopenprice = OrderOpenPrice ();
unused = oldorderopenprice;
ticketnumber = oldticketnumber;
>
>
>
return (oldorderopenprice);
>
//ннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннннн
double CalculateAveragePrice()
<
AveragePrice = 0 ;
double Count = 0 ;
for ( int i = 0 ; i OrdersTotal (); i++)
if ( OrderSelect (i, SELECT_BY_POS , MODE_TRADES ))
if ( OrderSymbol () == Symbol () && OrderMagicNumber () == MagicNumber)
if ( OrderType () == OP_BUY || OrderType () == OP_SELL )
<
AveragePrice += OrderOpenPrice () * OrderLots ();
Count += OrderLots ();
>
if (AveragePrice > 0 && Count > 0 )
return ( NormalizeDouble (AveragePrice / Count, Digits ));
else
return ( 0 );
>
bool ModifyOrder( double takeprofit)
<
while (! IsStopped ())
<
Print ( «ModifyOrder function» );
if ( IsTradeContextBusy ())
<
Print ( «The trade stream is occupied!» );
Sleep ( 3000 );
continue ;
>
Print ( «The trade stream is free» );
if (! IsTradeAllowed ())
<
Print ( «EA trading is forbidden or trade stream is occupied!» );
Sleep ( 3000 );
continue ;
>
Print ( «EA trading is allowed, order modify #» , OrderTicket ());
if (! OrderModify ( OrderTicket (), OrderOpenPrice (), 0 , NormalizeDouble (takeprofit, Digits ), 0 , Yellow))
<
Print ( «Unable to modify order» );
int Err = GetLastError ();
Print ( «error» );
break ;
//Sleep(1000);
//continue;
>
else
<
Print ( «Order modification completed successfully» );
break ;
>
>
return ( True );
>
Источник
OrderSend Error 131 is a very frequent problem that is usually encountered when testing MT4 expert advisors. What causes this error? It is called ERR_INVALID_TRADE_VOLUME
in the MetaTrader 4 documentation. It means that your expert advisor (EA) is trying to send an order with invalid trade volume. At the absolute majority of the MT4 brokers, setting some EA to open an order with 0.123 lots volume will generate this error. But sometimes, it is generated when an EA created for mini or micro accounts is used on a standard account. If you stumble upon an OrderSend Error 131 during your strategy testing, you can quickly find the wrong settings of your EA. To do so, find the standard OnInit()
(or init()
in older versions of MT4) function inside your EA’s code and insert these lines of code there:
The output will be valid for the current chart’s trading symbol and for the trading account type you are currently logged in. The first line will tell you how many units there are in one lot of the current trading symbol (100,000 would mean a standard-sized lot). Remember that in your expert advisor’s log, this line will be first from the bottom. The second line will tell you the minimum amount of lots you can trade (this is the most usual error; you probably just need to fix the amount of lots your EA trades from 0.1 to 1). The third one will give the step size for the trade volume in lots. The fourth line will tell you the maximum number of lots that your EA can trade.
For example, a Standard trading account at Exness Forex broker generates the following information when the above-mentioned lines of code are executed on a EUR/USD chart:
This means that 1 lot comprises 100,000 units (a standard size), the minimum trade volume is 0.01 lot (so, you can trade starting from $0.10 per pip in a dollar-based currency pair), the minimum trade volume step is also 0.01 lot (you can trade 0.33, 0.4 or 1.25 lot volumes, but you cannot send orders with 0.333 lot size), and the maximum volume you can use to open a position is 200 lots.
You can incorporate the MarketInfo()
function at a more complex level into your EA, so that it could automatically check the allowed values and auto-correct the EA’s trade parameters. But if you don’t want to code much, you can just use the code above to find out the right values and correct the settings manually.
You can discuss your personal struggles with the OrderSend Error 131 problem on our forum if you are having trouble solving this issue on your own.
What is MT4 Error 131?
The MT4 error 131 is an invalid volume OrderSend error code. It suggests that your Expert Advisor (EA) sends an order with an inappropriate trade volume to your broker’s server.
This error code may frequently appear during testing an MT4 EA, especially when there is an issue with the volume settings of your trading robot. Otherwise, it may happen due to a coding error of the expert advisor.
The biggest problem with the OrderSend error 131 is it entirely disables your trading bot from executing backtests and fresh orders. It may happen for both paid and open-source EAs, because most of the time, it occurs due to a mismatch of volume calculation methods between the EA and the serving brokers.
So, your trading platform showing an error 131 doesn’t necessarily mean you need to abandon your EA or the broker. If there is a problem with the bot’s programming, you might need a few edits with the coding. Otherwise, you may check the volume settings of your expert advisor and fix the issue by only making a few changes.
In this guide, we explain the possible scenarios of encountering the error 131. Also, we discuss a couple of solutions for getting rid of the invalid volume OrderSend problem from your MT4 platform.
Why does MT4 error 131 happen?
An MT4 error 131 may happen due to the following reasons:
The EA is using an inappropriate volume figure
Most EAs calculate a trading volume based on the risk percentage, which is entirely acceptable and fair. But the problem occurs when the calculated figure comes with more than two digits. For instance, if it is a 0.01 or 0.11 lot, then there is no problem. But if it sends an order with a volume like 0.111 or 0.245, it’ll show the error code 131.
Issues with the volume settings
How your broker recognizes a trading volume may differ depending on your trading account types. Suppose your EA is configured for a micro account, and you’re using it for a standard account. In this case, your broker may read a 0.01 lot as 0.001, considering the figure is less than the minimum order limit. As a result, the order becomes invalid, and the system shows the same OrderSend error.
How to fix MT4 error 131?
Time needed: 5 minutes.
How to fix MT4 Invalid Volume OrderSend errors
- Adjust the volume settings
If your broker fails to define the trading volume correctly, then insert the following codes to reset your volume settings:
MODE_LOTSIZE – It determines the number of units an EA will consider to count as one standard lot. Typically, a standard-size lot equals 100,000 units.
MODE_MINLOT – The minimum amount of trading volume you’ll use for executing an order. If your broker doesn’t accept 0.01 as the minimum lot size, consider replacing the value by 1.
MODE_LOTSTEP – It sets the step size of your trading volume.
MODE_MAXLOT – The maximum trading lot you can use for an order.
- Normalize the volume
Alternatively, you can try normalizing the volume configuration of the EA with the following lines of code:
This setting will keep the normalized volume lesser than the non-normalized value. For example, if the non-normalized volume is 0.1234, then the normalized volume will be 0.12.
Also, you may encounter the MT4 error 131 during backtesting an EA. In that case, simply go offline and then try backtesting again.
July 10th, 2008
OrderSend Error 131 is a very popular problem that is usually encountered when testing MT4 expert advisors.
What causes this error? It’s called ERR_INVALID_TRADE_VOLUME
in the MT4 code. That means that your expert advisor is trying to send
an order with invalid trade volume. On the absolute majority
of the MT4 brokers setting some EA to open an order 0.123 lots will
generate this error. But sometimes it’s generated when the EA, created
for mini or micro accounts, is used on the standard account. If you
stumble on OrderSend Error 131 during your testing, you can quickly find
out the wrong settings of your EA — find the standard init() function
inside your EA’s code and insert these lines of code there:
Print(MarketInfo(Symbol(), MODE_LOTSIZE));
Print(MarketInfo(Symbol(), MODE_MINLOT));
Print(MarketInfo(Symbol(), MODE_LOTSTEP));
Print(MarketInfo(Symbol(), MODE_MAXLOT));
The first line will give you the information regarding how many units
one lot holds when you trade in this account (100000 would mean a
standard-sized
lot). Remember, that in your expert advisor’s log this line will
be first starting from down to up, not vice versa. The second line will
tell you the minimum amount of lots you can trade (this is the most
usual error; you’ll probably just need to fix the amount of lots your
EA trades from 0.1 to 1). The third one will give the minimum step
for the trade volume in lots. The fourth line will tell you the maximum
amount of lots that your EA can trade.
For example, demo account at FXOpen generates this info when I insert those lines into the code:
2008.07.10 15:13:37 MACD Sample EURUSD, H1: 10000
2008.07.10 15:13:37 MACD Sample EURUSD, H1: 0.01
2008.07.10 15:13:37 MACD Sample EURUSD, H1: 0.01
2008.07.10 15:13:37 MACD Sample EURUSD, H1: 100000
That means that 1 lot is 100,000 units (a standard size), minimum
trade volume is 0.01 lot (so, one can trade starting from
$10 on 1 position in a
dollar-based
currency pair), minimum
trade volume step is also 0.01 lot (one can trade 0.33, 0.4 or 1.25 lot
volumes, but can’t send orders with 0.333 lot size) and the maximum
volume one can use to open a position is 10,000 lots.
You can incorporate the MarketInfo() function at a more complex level
into your EA, so it could automatically check the allowed values
and correct its settings. But if you don’t want to code much, you can
just use the code above to find out the right values and correct
the settings manually.
Открытие и установка ордеров
Формирование торговых приказов для открытия рыночных и установки отложенных ордеров
осуществляется с помощью функции 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 это значение "EURUSD". Если открытие
ордера проводится по заранее известному финансовому инструменту, то этот параметр
можно указать в явном виде: "EURUSD", "EURGBP" и т.д. Если
же предполагается использование советника в окне любого другого финансового инструмента,
то можно использовать стандартную функцию 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. Пользователь прикрепил скрипт к окну финансового инструмента — перетянул название
скрипта мышью из окна "Навигатор" клиентского терминала в окно финансового инструмента,
по которому он хочет открыть рыночный ордер 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.
Рассмотренный выше порядок событий не всегда проходит так гладко. Возможны случаи,
при которых исполнение торгового приказа будет отклонено клиентским терминалом
или сервером. Попробуем немного поэкспериментировать, например, изменим название финансового инструмента: укажем явно "GBPUSD"
(это вполне допустимо). Получится программа с ограниченной областью
использования:
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 не происходит.
Недостатком подобных программ является их функциональная ограниченность. В данном
случае, после присоединения скрипта в окно финансового инструмента пользователь
просто ждёт открытия ордера. Но открытия нет. Пользователь находится в неведении
относительно причины сложившейся ситуации: то ли причина в том, что в коде программы
имеется алгоритмическая ошибка, то ли торговый приказ "потерялся" на
пути к серверу, то ли торговый приказ давно отклонён клиентским терминалом (а пользователь
ждёт), то ли имеется другая причина.
Для того чтобы предоставить в распоряжение пользователя (и, что не менее важно,
в распоряжение программы) информацию о событиях, связанных с исполнением торгового
приказа, необходимо выполнить обработку ошибок.
Обработка ошибок
Очень важное свойство клиентского терминала состоит в том, что в случае, если во
время выполнения прикладной программы возникает ошибка, то клиентский терминал
не прекращает исполнение программы. Обычно ошибки возникают по причине несовершенства используемого
в прикладной программе алгоритма, а в некоторых случаях — в результате внешних
(по отношению к программе) причин. К внутренним причинам относятся любые нарушения
требований языка 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() позволяет получить различную информацию по финансовым инструментам,
которые отражены в окне "Обзор рынка" клиентского терминала.
Функция MarketInfo()
double MarketInfo(string symbol, int type)
Функция возвращает различную информацию о финансовых инструментах, перечисленных
в окне "Обзор рынка". Часть информации о текущем финансовом инструменте
хранится в предопределенных переменных.
Параметры:
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 < Min_Lot) // Если меньше допуст { Alert(" Не хватает денег на ", Min_Lot," лотов"); break; // Выход из цикла } //--------------------------------------------------------- 4 -- if (Dist_SL < Min_Dist) // Если меньше допуст. { Dist_SL=Min_Dist; // Установим допуст. Alert(" Увеличена дистанция SL = ",Dist_SL," pt"); } double SL=Bid - Dist_SL*Point; // Заявленная цена SL //--------------------------------------------------------- 5 -- if (Dist_TP < 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>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()
и повторить попытку открытия ордера. Если же возникла ошибка "нет цен" (ошибка 136),
то нет смысла сразу снова отправлять торговый приказ на сервер. В этом случае необходимо
дождаться нового тика (на сервере в этот период тоже нет цен) и только после этого
произвести ещё одну попытку открытия ордера. Поэтому в блоке обработки ошибки 136
имеется цикл задержки, который будет прерван в результате поступления нового тика.
Выход из оператора switch() осуществляется с помощью оператора continue, который
прекращает текущую итерацию цикла while () и начинает новую.
Ошибки непреодолимой силы обрабатываются иначе. Если такая ошибка возникла, то программа
просто сообщит о ней пользователю и прекратит работу. Для этой цели в программе
используется оператор break (последний в блоке 8-9), прекращающий цикл while (),
в результате чего программа заканчивает работу.
Отдельно нужно заметить, что в данном примере намеренно не рассматриваются все без
исключения ошибки. В данном случае мы не ставим перед собой цель предоставить в
распоряжение пользователя готовую программу. Очень важно, чтобы программист сам
проанализировал другие варианты ошибок и самостоятельно принял решение о том, какие
ещё ошибки и как необходимо обработать в программе. В то же время, некоторые ошибки
не должны обрабатываться, потому что сама программа построена таким образом, что
не предполагает возможности для возникновения некоторых ошибок, например, в данном
случае — для ошибок 129 и 130.
В представленном примере имеется также небольшая алгоритмическая ошибка, которая
не может быть обнаружена ни на этапе компиляции, ни в клиентском терминале, ни
на сервере.
Относитесь критически к любым программным кодам, невзирая на авторитеты. |
Обратите внимание на код в блоке 4-5:
//--------------------------------------------------------------- 4 -- if (Dist_SL<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<Min_Dist) // Если меньше допуст. { SL = Bid - Min_Dist*Point; // Заявленная цена SL Alert(" Увеличена дистанция SL = ",Min_Dist," pt"); } //--------------------------------------------------------------- 5 --
Аналогичное изменение кода следует выполнить в блоке 5-6 и для другого
стоп-приказа.
Выставление отложенных ордеров
Программирование выставления отложенных ордеров принципиально ничем не отличается
от предназначенного для выставления рыночных.
Обратить внимание следует лишь на тот факт, что проверка достаточности средств (необходимых
для преобразования отложенного ордера в рыночный) для отложенных ордеров не производится
ни клиентским терминалом, ни сервером и никак не ограничена. Возможно выставить
отложенный ордер, многократно превышающий по стоимости имеющиеся на счёте средства.
Такой ордер может находиться в торговле неопределённо долгое время. В момент, когда
рыночная цена достигнет уровня заявленной в отложенном ордере цены открытия, на
сервере будет произведена проверка. Если в этот момент средств на счёте достаточно
для открытия, то ордер будет преобразован в рыночный (открыт), если же нет — то
он будет удалён.
Функция WindowPriceOnDropped()
В MQL4 есть одна очень важная возможность — программно определить в окне финансового
инструмента координаты места, на которое был установлен эксперт или скрипт, если
они присоединены с помощью мыши. Например, используя функцию WindowPriceOnDropped()
можно получить значение прикрепления скрипта по оси ординат.
double WindowPriceOnDropped()
Функция возвращает значение цены в точке графика, на которой был брошен эксперт
или скрипт. Значение будет верным только в случае, если эксперт или скрипт перемещены
с помощью мыши (технология "drag and drop"). Для пользовательских индикаторов
это значение не определено.
Пример простого скрипта, устанавливающего ордер 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)< // Если меньше допуст. 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 < Min_Dist) // Если меньше допуст. { SL=Price - Min_Dist*Point; // Заявленная цена SL Alert(" Увеличена дистанция SL = ",Min_Dist," pt"); } //--------------------------------------------------------- 5 -- double TP=Price + Dist_TP*Point; // Заявленная цена ТР if (Dist_TP < 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>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 возникает только в том случае, если
в окне одного финансового инструмента одновременно работают несколько программ,
формирующих торговые приказы. По мнению автора, такая практика, хотя и допустима,
но не оправданна.
Гораздо эффективнее — создать и использовать одну торгующую программу, которая
учитывала бы все особенности торговли. Во время работы одной программы возможность
формирования одновременно нескольких торговых приказов вообще не возникает.
Кроме того, в такой программе можно более эффективно организовать весь алгоритм:
учитывать вероятность успешных торгов по различным финансовым инструментам и в
зависимости от этого правильно перераспределять денежные средства.
Для торговых операций более эффективно использовать полноценный эксперт, а скрипт
лучше применять для разовых расчётов и сообщения на экран некоторой полезной информации.
Вместе с тем, в случае если трейдер не использует эксперт для автоматической торговли,
применение скриптов оказывается более эффективным, чем управление ордерами с помощью
панели управления клиентского терминала.
Hi Petko,
Thanks for your reply, and for pointing out that the journal shows an error.. I looked up “mt4 ordersend error 131” and found out the following info:
Handling OrderSend Error 131 in MetaTrader 4
OrderSend Error 131 is a very popular problem that is usually encountered when testing MT4 expert advisors. What causes this error? It’s called ERR_INVALID_TRADE_VOLUME in the MT4 code. That means that your expert advisor is trying to send an order with invalid trade volume. On the absolute majority of the MT4 brokers setting some EA to open an order 0.123 lots will generate this error. But sometimes it’s generated when the EA, created for mini or micro accounts, is used on the standard account.
I am using a micro live account, could it be that the EA is programmed with the wrong lot size for the account I have? Or does the .json exported from MT4 file know all that stuff automatically?
Yes, I know my data is very limited.. it has been a huge struggle for me. My understanding is the the best data is the ACTUAL data from your broker, but here in the UK, not many brokers seem to offer MT4 that comes with data longer than a few months when you first install it.
My method to get around this, so far, has been to set up a VPS purely for the purpose of collecting data – I first started collecting it in January 2018, but very frustratingly (and I hold my hand up here for negligence), the VPS must have been rebooted by the hosting provider, possibly for maintenance or similar, some time in June 2018, because after a very busy summer/autumn with my other business, I checked the VPS in Jan 2019 to assess the data it had collected, only to find it had not been doing so since Jun 2018. Hence using the small amount of data I do have, starting in Jan 2019, and looking at the M1 timeframe, as that is obviously the highest count of bars I have for getting the largest data horizon.
I read about people using services like Tickstory, but I just don’t see how it can be as reliable as ACTUAL broker data, do you have any thoughts on that?…
In any case, after looking up the ordersend error 131, I altered the lot size for the strategy to 0.1 lot, and tried again in the MT4 Strategy Tester, and actually got much better results, please see below:
(PS: this isn’t a strategy at all developed for trading, it’s purely for checking that my workflow process is being set up ok)
You can see that it has 410 journal entries… the MT4 Strategy Tester results tab as 440 results, although the times and prices of the trades don’t match up very well at the start of the backtest, but they do at the end… why is that?
Is it because EA Studio is not calibrated to the same timezone as my MT4?
The graph is similar to the graph in EA Studio, although it shows better results than in EA Studio..
I don’t know what much of the report tab means, but I’m guessing that the completely red bar at the top isn’t good?..
Thanks,
Simon