Discussion of article "Library for easy and quick development of MetaTrader programs (part XXV): Handling errors returned by the trade server"
Exchange symbols, broker Otkritie, version 5.00 build 2190.
Sorry, there was a continuation of the discussion in part 23, but I got caught up and missed it. Anyway, I downloaded version 25 and the problem remains. In the discussion of the 23rd part you wrote that it is necessary to insert added lines in onInit'e after certain lines, but these lines highlighted by you in green I do not have in onInit'e neither in 23rd version nor in 25th version, here is the whole onInit :
//--- Setting global EA variables
prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
for(int i=0;i<TOTAL_BUTT;i++)
{
butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
}
lot=NormaliseLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
magic_number=InpMagic;
stoploss=InpStopLoss;
takeprofit=InpTakeProfit;
distance_pending=InpDistance;
distance_stoplimit=InpDistanceSL;
slippage=InpSlippage;
trailing_stop=InpTrailingStop*Point();
trailing_step=InpTrailingStep*Point();
trailing_start=InpTrailingStart;
stoploss_to_modify=InpStopLossModify;
takeprofit_to_modify=InpTakeProfitModify;
//--- Initialisation of the DoEasy library
OnInitDoEasy();
//--- Checking and deleting unreleased graphical objects of the Expert Advisor
if(IsPresentObects(prefix))
ObjectsDeleteAll(0,prefix);
//--- Create the button bar
if(!CreateButtons(InpButtShiftX,InpButtShiftY))
return INIT_FAILED;
//--- Set the state of the trailing activation button
ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//tr.SetCorrectTypeExpiration();
engine.TradingSetCorrectTypeExpiration();
engine.TradingSetCorrectTypeFilling();
//--- Checking playback of standard sound by macro substitution and custom sound by description
engine.PlaySoundByDescription(SND_OK);
Sleep(600);
engine.PlaySoundByDescription(TextByLanguage("The sound of a falling coin 2", "The sound of a falling coin 2"));
//---
return(INIT_SUCCEEDED);
Exchange symbols, broker Otkritie, version 5.00 build 2190.
Sorry, there was a continuation of the discussion in part 23, but I got caught up and missed it. Anyway, I downloaded version 25 and the problem remains. In the discussion of the 23rd part you wrote that it is necessary to insert added lines in onInit'e after certain lines, but these lines highlighted by you in green I do not have in onInit'e at all neither in the 23rd version nor in the 25th version, here is the whole onInit:
Are you using the test EA from article 25 - "TestDoEasyPart25.mq5" ?
Now I have inserted onInit from version 23 with the added functions I inserted, but I have used version 25 which also does not have the lines you have highlighted in green in version 23 and version 25 does not work as well as 23.
I don't understand which green highlighted lines you are talking about.
And please don't use a test EA from another article - each article comes with its own EA for testing - you should use it and report any errors you find in relation to the correct test EA, not from another article.
Ok, sorry, in relation to the last article:
orders are opened, but all pending orders are not, the log says 2019.11.21 12:59:20.689 2019.11.18 10:00:02 failed sell stop limit 2.00 Si-12.19 at 63972 (64022) sl: 64072 tp: 63972 [Invalid expiration] and 2019.11.21 12:59:20.689 2019.11.18 10:00:02 Trade attempt #3. Error : Invalid order expiry date in the request
Okay, sorry, in relation to the last article:
orders are opened, but all pending orders are not, the log says 2019.11.21 12:59:20.689 2019.11.18 10:00:02 failed sell stop limit 2.00 Si-12.19 at 63972 (64022) sl: 64072 tp: 63972 [Invalid expiration] and 2019.11.21 12:59:20.689 2019.11.18 10:00:02 Trade attempt #3. Error : Invalid order expiry date in the request
Okay, thanks, I'll look into it.
Can you attach here a screenshot with the specification of the symbol where you get the error?
Screen of Si-12.19 specification in the file.
In this version of the library, as well as in the next one, I missed checking the order fill types and expiry types. There will be corrections in the next (27) article.
For now, I can suggest that when sending a trade request, you should explicitly specify the expiration type. For example, to place a Sell Limit pending order, you need to add it in the test Expert Advisor:
//--- If the BUTT_SELL_LIMIT button is pressed: Set SellLimit else if(button==EnumToString(BUTT_SELL_LIMIT)) { //--- Set SellLimit order engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Pending SellLimit","Pending order SellLimit"),0,ORDER_TIME_DAY); }
And do the same in all lines that contain the call of methods for setting pending orders.
Artem, thanks for the work you've done, very thoughtful.
To be honest, I didn't quite understand the point of composite magic numbers....
Suppose I'm going to split trades in an EA into several groups with different magic numbers.
In the classical approach, I subsequently go through the orders and compare the magic numbers, distributing the result among the groups.
In the library, I understand that not much changes in this situation - just the allocation of these numbers can be systematised somehow? Or is there something else they will affect?
And then there's a particular layer of functionality that I categorically lack in your library: aggregation.
Very often we need to answer the question, what is the aggregate profit on a set of parameters (magik, symbol, order type). And the aggregate lot, for example. And the breakeven level. And the number of trades.
These aggregate values are needed everywhere and repeatedly, calculating them by sample in each new method is an obvious overkill.
It would be great to have a set of aggregations, configuring each with a set of sampling criteria (symbol, type, magik, group number) and provide access to them in CEngine - and update them once in Refresh.
If you have no plans for this kind of refinement, I could finalise and share..... but only we have significantly different code style, this piece would look alien.
Artem, thanks for the work you've done, very thoughtful.
I didn't quite get the point of the composite magiks, to be honest....
Suppose I'm going to split trades in an Expert Advisor into several groups with different magic numbers.
In the classical approach, I subsequently go through the orders and compare the magic numbers, distributing the result among the groups.
In the library, I understand that not much changes in this situation - just the allocation of these numbers can be systematised somehow? Or is there something else they will affect?
And there's also a certain layer of functionality that I categorically lack in your library: aggregation.
Very often we need to answer the question, what is the aggregate profit on a set of parameters (magik, symbol, order type). And the aggregate lot, for example. And the breakeven level. And the number of trades.
These aggregated values are needed everywhere and repeatedly, calculating them by sample in each new method is an obvious overkill.
It would be great to have a set of aggregations, configuring each with a set of sampling criteria (symbol, type, magik, group number) and provide access to them in CEngine - and update them once in Refresh.
If you have no plans for this kind of refinement, I could finalise and share..... but only we have significantly different code style, this piece would look alien.
About the information recorded in the Magic Number value:
you can use a different magik for each group to create different groups. For example, if the advisor's magik is 123, the magik of the first group will be 124, the magik of the second group is 125, the third group is 126, and so on.
The library offers a different way of creating different groups - the number of each subgroup is stored directly in the Magic Number value. Then the EA's magic number is also a group identifier, but it is placed in an independent group called MagicID - the EA's magic number identifier. And there are two more groups. Each of them has 15 subgroups. And each of the subgroups can have its own identifier.
This will givemore flexibility when working with groups.
For example: We want to move a grid of pending orders behind the price - we add them to group 1 in subgroup 1. Group 1 moves behind the price. Subgroup 1 moves along the MA. Now we want to move some of those orders that move behind the price (group 1) by Parabolic SAR. We give them subgroup 2. Then group 1 moves after the price, but subgroup 1 moves by MA, and subgroup 2 moves by Parabolic SAR.
Orders are triggered, turning into positions - you can set your own groups for modification of stoplosses, and your own subgroups in this group for modification by different values. Modification algorithms are written in subgroups.
In general - a flight of fancy. You can also use a simple magik, but you will have to think up the logic of tracking different groups yourself.
On the second question:
There is a class CSelect. It is available from the programme, and provides methods of selection and search, which you write about, from all existing collections: Account, Event, Order, Symbol.
You can select the objects of each collection into a list based on all criteria. In the created list, you can reselect by refining criteria, you can find the maximum and minimum values for a selection criterion.
However, there will be further user functions (much later) for quick and convenient access to all properties of all collections and search in them.
But for now - only through CSelect, and when you need it. The class is static, so access to its methods via ":::" For example, CSelect::ByOrderProperty().
Yes, by the way, there is an example of using it in the programme right in the test EA - for example, in its trailing functions:
//+------------------------------------------------------------------+ //| Trawl the furthest pending orders | //+------------------------------------------------------------------+ void TrailingOrders(void) { MqlTick tick; if(!SymbolInfoTick(Symbol(),tick)) return; double stop_level=StopLevel(Symbol(),2)*Point(); //--- Get the list of all set orders CArrayObj* list=engine.GetListMarketPendings(); //--- Select only orders by the current symbol from the list list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL); //--- Select only orders in the Buy direction from the list CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL); //--- Sort the list by distance from price in pips (by profit in pips) list_buy.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the order in the Buy direction with the largest distance int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT); if(index_buy>WRONG_VALUE) { COrder* buy=list_buy.At(index_buy); if(buy!=NULL) { //--- If the order is set below the price (BuyLimit) and it needs to be "lifted" behind the price if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT) { //--- Calculate the new order setting price and stop levels from the new price double price=NormalizeDouble(tick.ask-trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price is lower than the StopLevel distance, postponed from the Ask order setting price (the StopLevel distance is observed) if(price<tick.ask-stop_level) { //--- If the calculated price is higher than the trawl step postponed from the order setting price - modify the order setting price if(price>buy.PriceOpen()+trailing_step) { engine.ModifyOrder((ulong)buy.Ticket(),price,sl,tp,-1); } } } //--- If the order is set above the price (BuyStop and BuyStopLimit) and it should be "lowered" behind the price else { //--- Calculate the new order setting price and stop levels from the new price double price=NormalizeDouble(tick.ask+trailing_stop,Digits()); double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0); double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0); //--- If the calculated price is higher than the StopLevel distance, postponed from the Ask order setting price (the StopLevel distance is observed) if(price>tick.ask+stop_level) { //--- If the calculated price is lower than the trawl step postponed from the order setting price - modify the order setting price if(price<buy.PriceOpen()-trailing_step) { engine.ModifyOrder((ulong)buy.Ticket(),price,sl,tp,-1); } } } } } //--- Select only orders in Sell direction from the list CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL); //--- Sort the list by distance from price in pips (by profit in pips) list_sell.Sort(SORT_BY_ORDER_PROFIT_PT); //--- Get the index of the order in Sell direction with the largest distance int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT); if(index_sell>WRONG_VALUE) { COrder* sell=list_sell.At(index_sell); if(sell!=NULL) { //--- If the order is set above the price (SellLimit), and it should be "lowered" behind the price if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT) { //--- Calculate the new order setting price and stop levels from the new price double price=NormalizeDouble(tick.bid+trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price is higher than the StopLevel distance, postponed from the Bid order setting price (the StopLevel distance is observed) if(price>tick.bid+stop_level) { //--- If the calculated price is lower than the trawl step postponed from the order setting price - modify the order setting price if(price<sell.PriceOpen()-trailing_step) { engine.ModifyOrder((ulong)sell.Ticket(),price,sl,tp,-1); } } } //--- If the order is set below the price (SellStop and SellStopLimit) and it should be "lifted" behind the price else { //--- Calculate the new order setting price and stop levels from the new price double price=NormalizeDouble(tick.bid-trailing_stop,Digits()); double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0); double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0); //--- If the calculated price is lower than the StopLevel distance, postponed from the Bid order setting price (the StopLevel distance is observed) if(price<tick.bid-stop_level) { //--- If the calculated price is higher than the trawl step postponed from the order setting price - modify the order setting price if(price>sell.PriceOpen()+trailing_step) { engine.ModifyOrder((ulong)sell.Ticket(),price,sl,tp,-1); } } } } } } //+------------------------------------------------------------------+

- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
New article Library for easy and quick development of MetaTrader programs (part XXV): Handling errors returned by the trade server has been published:
After we send a trading order to the server, we need to check the error codes or the absence of errors. In this article, we will consider handling errors returned by the trade server and prepare for creating pending trading requests.
In the last MetaTrader 5 versions (starting from the build 2201), the tester features the ability to set parameters of a symbol the test is performed on. Thus, it is possible to set trading limitations on a symbol and test the library behavior when the symbol limitations are detected.
To call the symbol settings window, click the button to the right of the tested timeframe selection:
Allow opening long positions only for a symbol and set the volume limitation of simultaneously opened positions and placed pending orders in one direction to 0.5.
Thus, we will be able to use long positions only and have the maximum total volume of buy positions and orders of no more than 0.5 lot in the market. In other words, when opening a position with the lot of 0.1, we are able to open five positions only or place a single pending buy order and open four positions:
For more authenticity, we could disable auto closure of positions when a specified profit is exceeded. However, we can see that we were unable to open a short position and received the warning that only buy positions are allowed on a symbol. Further on, when trying to open a number of positions with their total volume exceeding 0.5 lots, we receive the message of the inability to open a position due to exceeding the maximum total volume of positions and orders in one direction.
Author: Artyom Trishkin