Discussion of article "Library for easy and quick development of MetaTrader programs (part XXI): Trading classes - Base cross-platform trading object"

 

New article Library for easy and quick development of MetaTrader programs (part XXI): Trading classes - Base cross-platform trading object has been published:

In this article, we will start the development of the new library section - trading classes. Besides, we will consider the development of a unified base trading object for MetaTrader 5 and MetaTrader 4 platforms. When sending a request to the server, such a trading object implies that verified and correct trading request parameters are passed to it.

It is nice to have an easy access to various data at any time. However, that data is useless if we cannot apply it in trading. This means we need trading functionality along with the already existing one. 
This section will be relatively large, and we will do everything step by step.

  • We should be able to send any trading requests from any platform, be it MetaTrader 5 or MetaTrader 4, without even thinking about differences between them. Everything should be unified.
  • First, we need to verify trading requests in order not to load the server with deliberately erroneous requests.
  • We need to consider and correctly handle the return codes of the trade server. What does an EA do while sending a request to the server? It maintains the 'request-response' dialog with the server. Our task is to correctly arrange such a "communication channel", i.e. create the methods of handling trade server responses.
  • We need to create several options of handling server responses since sometimes we need to open a position "preferably at any cost". To do this, we need to arrange a repeated sending of a request to the server in case of a refusal to place an order — we can either adjust the trading request parameters or re-send it, or leave all the parameters intact but wait for the right moment when the request with these parameters is passed to send it immediately. Besides, we need to consider the price level in order not to re-send an order at a knowingly worse price.
    Sometimes, we need to send a trading request and continue work regardless of a request result.
  • Besides, we need to arrange the work with trading classes so that to avoid issues when placing a library-based program to MQL5 Market. The program should pass all the checks smoothly.
This is my current plan regarding trading classes.

Author: Artyom Trishkin

 

I read more than half of it very carefully. Further on, my consciousness began to switch off. The volume of the article is extremely large.

Anatoly Kozharsky always had a general scheme of the library at the end of his articles, by which one could get a clear idea of its structure. I have not seen such a scheme in your articles, and therefore, I feel like an "ant" crawling on the body of an "elephant". I am building a concise idea of the library, but reading your articles it is difficult to make it up. There is a lot of "chewed" information in the form of details, code explanations, references to other articles, but there is no integral, concise description and scheme of the library.

Of course, the level of implementation is very high. Everything is very professional, and it shows. But, I would question the need for so many "wrappers". For example: The " CTradeObj::SetOrder" method has the following wrappers:

//--- Sets pending order (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit
   bool                 PlaceBuyStop(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceBuyLimit(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceBuyStopLimit(const double volume,
                                     const string symbol,
                                     const double price_stop,
                                     const double price_limit,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- Sets pending order (1) SellStop, (2) SellLimit, (3) SellStopLimit
   bool                 PlaceSellStop(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceSellLimit(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceSellStopLimit(const double volume,
                                     const string symbol,
                                     const double price_stop,
                                     const double price_limit,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- 
                        

All of them are not much different, but they take up a lot of space. With such "deep wrapping" material and codes will grow like on "yeast" and the use of functionality will become more complicated in proportion to the growth of the number of entities, which will multiply exponentially.

No, I'm not against it. I guess that's what polymorphism is all about - when one method spawns many wrappers, each with its own specificity. The problem is that the solution structure with this approach grows too fast and ceases to be practically useful. We need to limit the growth of the material, otherwise it will become more and more difficult to understand and use.

 
Реter Konow:

I read more than half of it very carefully. Further on, my consciousness began to switch off. The volume of the article is extremely large.

Anatoly Kozharsky always had a general scheme of the library at the end of his articles, by which one could get a clear idea of its structure. I have not seen such a scheme in your articles, and therefore, I feel like an "ant" crawling on the body of an "elephant". I am building a concise idea of the library, but reading your articles it is difficult to make it up. There is a lot of "chewed" information in the form of details, code explanations, references to other articles, but there is no complete, concise description and scheme of the library.

Of course, the level of implementation is very high. Everything is very professional, and it shows. But, I would question the need for so many "wrappers". For example: The method " CTradeObj::SetOrder" has the following wrappers:

All of them are not much different, but they take up a lot of space. With such "deep wrapping" material and codes will grow like on "yeast" and the use of functionality will become more complicated in proportion to the growth of the number of entities, which will multiply exponentially.

No, I'm not against it. I guess that's what polymorphism is all about - when one method spawns many wrappers, each with its own specificity. The problem is that the solution structure with this approach grows too fast and ceases to be practically useful. We need to limit the growth of the material, otherwise it will become more and more difficult to understand and use.

Thank you for your assessment, Peter. You should use only what is available from CEngine, which in turn is available from the programme.

All the rest is the implementation of methods to which all the necessary parameters are passed. These methods are not necessary for practical use in your own programmes - they are necessary for the library itself. Therefore, for the end user there are always not so many methods, and this is shown in the test advisors.

Naturally, everything will be described. And the structure, and all the methods available to the user. And user functions will be created, hiding OOP-implementation, and not requiring to use explicit access to library objects.

But everything is ahead of us - we need to compose the main functionality, which can be described both structurally and tabularly so that the description is complete and does not require modifications.

With each subsequent article, the difference in methods will increase dramatically. This article is just the very beginning of work on trade classes. And, please look at OrderSend() for each type of orders to be sent - what parameters it requires. All these parameters are passed to methods. Of course, it would be possible to pass parameters through described structures as in indicators, but it would be unnecessary.
And you could make the user fill in the necessary structures, but it is easier and friendlier to pass the parameters directly to the method than to fill in the structure beforehand and then pass it to the method. It is done just for the convenience of use by the end user, not for the convenience of parsing the library components.
 
Artyom Trishkin:

Thank you for your assessment, Peter. It is necessary to use only what is available from CEngine, which in turn is available from the programme.

All the rest is the implementation of methods to which all the necessary parameters are passed. These methods are not necessary for practical use in your own programmes - they are necessary for the library itself. Therefore, for the end user there are always not so many methods, and this is shown in the test advisors.

Naturally, everything will be described. And the structure, and all the methods available to the user. And user functions will be created, hiding OOP-implementation, and not requiring to use explicit access to library objects.

But everything is ahead of us - we need to compose the main functionality, which can be described both structurally and tabularly so that the description is complete and does not require modifications.

With each subsequent article, the difference in methods will increase dramatically. This article is only the very beginning of work on trade classes.

In order not to be unsubstantiated, I would suggest the following measure to limit "wrapping":

Rewrite the " CTradeObj::SetOrder" method so that it receives one additional parameter - the type of order to be set. Further, the method itself will determine how to do it. It will initialise the required structure, set the required values and call the required methods. Thus, you will compress all its wrappers to one additional parameter sent to the " CTradeObj::SetOrder" method. It will receive PlaceBuyStop, PlaceSellStopLimit, or PlaceBuyLimit as a parameter and then - do the necessary work.

Of course, this option is more complicated to implement, but you will ensure "compression" of the library functionality and ease of its use.

Good luck.

 
Реter Konow:

In order not to be unsubstantiated, I would suggest the following measure to limit "wrapping":

The " CTradeObj::SetOrder" method should be rewritten so that it receives one additional parameter - the type of order to be set. Further, the method itself will determine how to do it. It will initialise the required structure, set the required values and call the required methods. Thus, you will compress all its wrappers to one additional parameter sent to the " CTradeObj::SetOrder" method. It will receive PlaceBuyStop, PlaceSellStopLimit, or PlaceBuyLimit as a parameter and then - do the necessary work.

Of course, this option is more complicated to implement, but you will ensure "compression" of the library functionality and ease of its use.

Good luck.

This method is not needed by the library user. It is needed by the library itself.

Peter. Try to open a position by setting only its type. Right in the terminal. And then see what else is required of you. The terminal will not decide for you what symbol to open it on, what lot to open it with, what stop-orders levels to set, etc.

 
Artyom Trishkin:

...

And, please look at OrderSend() for each type of orders sent - what parameters it requires. All these parameters are passed to methods. Of course, it would be possible to pass parameters through described structures as in indicators, but it would be unnecessary.

And you could make the user fill in the necessary structures, but it is easier and friendlier to pass the parameters directly to the method than to fill in the structure beforehand and then pass it to the method. This is done for the convenience of the end user, not for the convenience of parsing the library components.
From my point of view, the friendliest option would be just one wrapper for OrderSend(). This wrapper would accept parameters from the user and decide how to format the call. I would do it this way. But, it's up to you.
 
Реter Konow:
From my point of view, the friendliest option would be just one wrapper for OrderSend(). This wrapper would accept parameters from the user and decide how to format the call. I would do it this way. But, you know better.

I have it this way - methods accept parameters from the user.

 
Artyom Trishkin:

I have it done this way - methods accept parameters from the user.

Yes, I understand. This is such a "multi-layer" implementation of order processing. The problem is in the "thickness" of the wrappers. I got confused which methods refer to mechanisms and which methods refer to the user interface of the library. Sorry. If there were fewer wrappers, it would be easier to understand, but it's your choice.
 
Реter Konow:
Yeah, got it. This is such a "multi-layer" implementation of order processing. The problem is in the "thickness" of the wrappers. I got confused which methods refer to mechanisms and which methods refer to the user interface of the library. Sorry. If there were fewer wrappers, it would be easier to understand, but it's your choice.

You see, Peter, I try to give not just one working method, but many possibilities.

In this article, low-level access to trade functions is organised. All the trade object does is to correctly arrange the received parameters into the function of sending an order to the server. To send a trade order, you need to access the collection of symbols, get an object of the required symbol, and from this object get a trade object. Then work with the trade object. Here I made it easier for the user to fill the trade order - it is enough to pass the necessary parameters. But these parameters must be independently checked for correctness, because the trade object considers that all parameters passed to it are already correct.

This is not convenient. But there is such an access. Who needs it will check everything himself and send a trade order. There is no server response processing. This is the first thing that has been done.

The next article will describe a trading class, which will have the same set of trading methods with the same parameters, but now everything will be checked for correctness and returned to the programme if the parameters are not correct (or one of them, or there is no possibility to trade). This is the second stage - there are already two ways here - the first one, which already exists, and the second one - when the library itself checks the correctness of the parameters, and if they are all correct, then the order is sent, and if at least one of them is not correct, or there is no possibility for trading, then it simply returns to the control programme. Here a higher-level access with correctness checks will be organised.

But that is not all. We need to react to errors. And in the next article we will go even further - we will process the errors received in the parameters of trade orders (even before sending them to the server). After all, you can refuse to open a position in case of an error, and you can correct the parameters and send the request again. This is already the third level of work with trade objects.

And then there will be an even higher level approach - everything will work automatically in accordance with the required settings. And it will come to deferred requests - when the Expert Advisor will wait for the right moment to send the required order again, or will simply know in what situation it needs to send a request. All such pending requests will be in his piggy bank waiting for the right time to send the request.

Therefore, Peter, it is premature to judge anything yet.

You suggested to make a single access to trading functions, but I want to give a variety of methods - to everyone according to his needs. Although it is impossible to please everyone, of course.

 

Artyom Trishkin:

...

I liked your explanation, and in this regard, an internal contradiction arose.

1. On the one hand, giving users access to all levels of the library engine through the construction of a tiered "wrapper" structure, where instead of compressed algorithms are expanded relationships, and the algorithmic "framework" is scaled for easy perception, provides a comfortable understanding of the structure of solutions. It is beautiful, aesthetic, elegant. Of course, it is a matter of design and user friendliness. More precisely, friendliness to researchers, because users care more about results and efficiency. Now let's look at the solution from a different perspective.

2- Efficiency implies conciseness, a minimal set of entities and wrappers, with the shortest possible access to data. Everything should be close, so as not to "stretch" and not to transfer a "load" of parameters from wrapper to wrapper. Efficiency and deployed, tiered, elegant style with "wandering" between wrappers "squads" of parameters are alien and almost incongruous things.

//-----------------------------------

In view of this contradiction, I would suggest a compromise. Partially limit the growth of the number of wrappers by increasing the conciseness of solutions. In principle, one wrapper is enough for OrderSend() function. In an extreme case - several. But, in this case, the solution will be hard for "researchers" to understand, and access to "tiers" will become more complicated. Therefore, try to combine compressed, efficient blocks and "vines" of OOP-jungle, so loved by programmers. And you will find the golden mean.

 
Artyom Trishkin:

...

And then there will be an even higher level approach - everything will work in automatic mode according to the required settings. And it will come to deferred requests - when the Expert Advisor will wait for the right moment to send the required order again, or simply know in what situation it needs to send a request. All such pending requests will be in his piggy bank and will be waiting for the right moment to send the request.

Therefore, Peter, it is premature to judge anything.

You suggested to make a single access to trading functions, but I want to give a variety of methods - to everyone according to his needs. Although it is impossible to please everyone, of course.

Breaking the library into levels and allowing users to work on them is very good. But don't forget that quantity can ruin quality. The more methods a user gets, the more likely he will get confused. That's something to keep in mind.

Other than that, I agree. A layered approach to trading functions is a good thing.