Market Simulation: Position View (IV)
Introduction
Hello and welcome everyone to another article in the series on how to create a replication/simulation system.
In the previous article, Market Simulation: Position View (III), we showed how important it is to know when and why the ZOrder property should be set for objects. Up to this point, I have been quite patient and have not shown exactly what we are going to do. After reading the previous articles, you may think that all this is very nice, but entirely impractical. Well, the easy part is over. Now we will do something without which we cannot continue implementing the position indicator. We need to bring together four existing applications in order to continue implementing the position indicator. However, dear reader, you should not worry about pending orders yet. How we will work with them will become clear in the future. So we need to move forward step by step.
At this stage, the most important step is not dealing with pending orders, but ensuring proper position management. Keep the following in mind: we already have an Expert Advisor that can open and close positions at market price. We also have the Mouse Study indicator, which already allows us to carry out simple chart studies and drawings, although you can modify it to create a more complex study. We also have Chart Trade, which allows orders to be sent to the Expert Advisor to indicate whether to buy or sell, as well as the volume to trade. But these three applications still need one more component for us to have a viable system. That missing application is precisely the indicator we are now implementing.
At the current stage of development, the position indicator is little more than an interesting application. But if we combine it with the three other applications that have already been created, the whole system becomes much more interesting. However, you may be wondering: how are we going to do this? Are we going to use the C_IndicatorPosition class in the Expert Advisor? No, we are not going to do that. We will keep the position indicator separate from the Expert Advisor. This way, you can add it to your own Expert Advisor.
Of course, you will need to make some adjustments so that your Expert Advisor can use the position indicator. But believe me, this will be much easier than creating several Expert Advisors with the same operational capabilities. Any changes to the position indicator will immediately be reflected in all Expert Advisors that use it. This is unlike a situation where each Expert Advisor would have its own position indicator.
The idea I am proposing, and the demonstration of how to implement it, is nothing new. In fact, this is exactly what allows different programs to use shared components. It is somewhat similar to computer games that use DirectX. When DirectX receives an update, all games and programs that use the DirectX library are also updated. Now imagine if each application had to be updated manually whenever the DirectX library was improved. That would be completely impractical. So this model should not be seen as a potential problem. It can be seen as a solution that, over time, will allow us to create increasingly useful and secure applications. Any fix or improvement in one application will affect the entire chain of applications used in MetaTrader 5. So let us begin.
Let us recall the current state of the Expert Advisor.
Before making any changes to the code, we need to look at what the main Expert Advisor code looks like. This is because quite a long time has passed since the last changes were made. So, in the code below, you can see the current state of the Expert Advisor implementation:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.84" 07. #property link "https://www.mql5.com/pt/articles/12598" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Order System\C_Orders.mqh> 10. //+------------------------------------------------------------------+ 11. enum eTypeContract {MINI, FULL}; 12. //+------------------------------------------------------------------+ 13. input eTypeContract user00 = MINI; //Cross order in contract 14. //+------------------------------------------------------------------+ 15. C_Orders *Orders; 16. long GL_ID; 17. //+------------------------------------------------------------------+ 18. int OnInit() 19. { 20. GL_ID = 0; 21. Orders = new C_Orders(0xC0DEDAFE78514269); 22. 23. return INIT_SUCCEEDED; 24. } 25. //+------------------------------------------------------------------+ 26. void OnTick() {} 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 31. switch (id) 32. { 33. case CHARTEVENT_CHART_CHANGE: 34. if (GL_ID > 0) break; else GL_ID = ChartID(); 35. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 36. EventChartCustom(GL_ID, evEA_At_ChartTrade, user00, 0, ""); 37. break; 38. } 39. } 40. //+------------------------------------------------------------------+ 41. void OnDeinit(const int reason) 42. { 43. switch (reason) 44. { 45. case REASON_REMOVE: 46. case REASON_INITFAILED: 47. EventChartCustom(GL_ID, evEA_At_ChartTrade, -1, 0, ""); 48. break; 49. } 50. 51. delete Orders; 52. } 53. //+------------------------------------------------------------------+
Original Expert Advisor code
Wow! Was the last time this code was changed really article 84? Yes, no changes have been made since then. But now it is time to return to working on it. Before making any changes to this code, we need to understand the following. Notice that line 13 contains the only form of interaction with the user or trader. This interaction is intended to tell all the other applications that will interact with the Expert Advisor in one way or another whether it will work with a full contract or a mini contract.
For those who do not trade on B3 (the Brazilian stock exchange), this setting will not make much sense. But regardless of what you trade, you will need the Chart Trade indicator on the chart, as well as this Expert Advisor. Once the Expert Advisor is added to the chart, it will send a message to all applications inside MetaTrader 5. I have already explained this earlier in this same series of articles. If you do not understand what this is about, try reading the previous articles to gain a deeper understanding of the subject. Understanding this messaging issue will be of primary importance later on. Without this understanding, you will not be able to follow the material in the next articles.
Great. Once this message has been sent, all applications will know what the Expert Advisor expects from them. This Expert Advisor will not receive commands directly from the operator or user. It will only receive and send commands via messages. So do not expect to see linear and perfectly ordered code. There is quite a bit of event-driven interaction here.
Fine. But where does the position indicator appear in all this? How can we use it if the Expert Advisor does not know that the indicator is present on the chart? In fact, the Expert Advisor, like the other applications, does not know about the presence of the others. They all simply expect the trader to add them to the chart so that they can perform their tasks correctly. But there is one important detail here. Since each position indicator will correspond to only one specific position, we cannot attach it to the chart before a position appears for that symbol. Unlike other indicators, this position indicator will be added and removed automatically by the Expert Advisor. The idea is that the trader should not interfere in this process. The Expert Advisor itself will take care of it.
However, I will not embed the position indicator in the Expert Advisor. The idea is that the indicator can be modified or improved for use by all Expert Advisors without having to recompile them. But in order for everything to work in full harmony, some changes will be required. At the same time, dear reader, you should pay close attention to what will be explained. Otherwise, the entire system will fail and will not provide the data that should be displayed on the chart. Another detail is that, for now, you do not need to worry about the details of the price lines. The first thing we need to do now is create a way for the Expert Advisor and the position indicator to interact. After that, we will deal with the interaction between the position indicator and the mouse indicator, which will be responsible for managing the price lines.
Starting to implement the interaction
Fine, let us begin by making some changes to the main code of the position indicator. The reason is that it will no longer be — or rather, should no longer be — added to the chart by the trader or user. The Expert Advisor will do that. But since we want the Expert Advisor to know which indicator represents each position, we need to change a few things in both the indicator code and the Expert Advisor code.
So, now that we understand what we need to do, let us start with the new position indicator code. You can see it below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property description "This should preferably be used together with an Expert Advisor." 05. #property description "For more details see the same article." 06. #property link "https://www.mql5.com/pt/articles/13168" 07. #property version "1.116" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #define def_ShortName "Position View" 12. //+------------------------------------------------------------------+ 13. #include <Market Replay\Order System\C_IndicatorPosition.mqh> 14. //+------------------------------------------------------------------+ 15. input ulong user00 = 0; //For Expert Advisor use 16. input color user01 = clrRoyalBlue; //Color Line Price 17. input color user02 = clrForestGreen; //Color Line Take Profit 18. input color user03 = clrFireBrick; //Color Line Stop Loss 19. //+------------------------------------------------------------------+ 20. C_IndicatorPosition *Positions = NULL; 21. //+------------------------------------------------------------------+ 22. int OnInit() 23. { 24. IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName); 25. Positions = new C_IndicatorPosition(user01, user02, user03); 26. if (!Positions.CheckCatch(user00)) 27. { 28. ChartIndicatorDelete(ChartID(), 0, def_ShortName); 29. return INIT_FAILED; 30. } 31. 32. return INIT_SUCCEEDED; 33. } 34. //+------------------------------------------------------------------+ 35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 36. { 37. return rates_total; 38. } 39. //+------------------------------------------------------------------+ 40. void OnDeinit(const int reason) 41. { 42. delete Positions; 43. } 44. //+------------------------------------------------------------------+
Position View indicator code
Notice that the code has undergone minor changes. These changes are easy to understand, so they do not deserve much attention. But you should carefully note that now, in line 15, data is expected from the Expert Advisor. As a trader, you could even enter this data yourself. But I advise you not to change this information. You can change the colors if you are careful and do not make them the same, since at the moment we use them to check which horizontal line each color represents. In any case, whenever possible, avoid changing the data that should come from the Expert Advisor. In particular, this data will be used by the Expert Advisor to exchange information with the indicator when both components are on the chart.
Please note that in line 26 we pass a parameter to the check function. Previously, this parameter did not exist. Let us look at the C_IndicatorPosition class code, which is shown in full below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_SufixTake "Take" 05. #define def_SufixStop "Stop" 06. //+------------------------------------------------------------------+ 07. #include "..\Auxiliar\C_Terminal.mqh" 08. //+------------------------------------------------------------------+ 09. class C_IndicatorPosition : private C_Terminal 10. { 11. private : 12. struct st00 13. { 14. ulong ticket; 15. color corPrice, corTake, corStop; 16. }m_Infos; 17. //+------------------------------------------------------------------+ 18. void CreateLineInfos(const string szObjName, const double price, const color cor, const string szDescription = "\n") 19. { 20. if (price <= 0) return; 21. CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == m_Infos.corPrice ? ePriorityNull : (ePriorityOrders + (cor == m_Infos.corStop)))); 22. ObjectSetDouble(GetInfoTerminal().ID, szObjName, OBJPROP_PRICE, price); 23. ObjectSetString(GetInfoTerminal().ID, szObjName, OBJPROP_TEXT, szDescription); 24. ObjectSetString(GetInfoTerminal().ID, szObjName, OBJPROP_TOOLTIP, szDescription); 25. ObjectSetInteger(GetInfoTerminal().ID, szObjName, OBJPROP_SELECTABLE, cor != m_Infos.corPrice); 26. } 27. //+------------------------------------------------------------------+ 28. public : 29. //+------------------------------------------------------------------+ 30. C_IndicatorPosition(color corPrice, color corTake, color corStop) 31. :C_Terminal() 32. { 33. ZeroMemory(m_Infos); 34. m_Infos.corPrice = corPrice; 35. m_Infos.corTake = corTake; 36. m_Infos.corStop = corStop; 37. } 38. //+------------------------------------------------------------------+ 39. ~C_IndicatorPosition() 40. { 41. if (m_Infos.ticket != 0) 42. ObjectsDeleteAll(GetInfoTerminal().ID, IntegerToString(m_Infos.ticket)); 43. } 44. //+------------------------------------------------------------------+ 45. bool CheckCatch(ulong ticket) 46. { 47. string szName = IntegerToString(m_Infos.ticket = ticket); 48. 49. if (!PositionSelectByTicket(m_Infos.ticket)) return false; 50. if (ObjectFind(GetInfoTerminal().ID, szName) >= 0) 51. { 52. m_Infos.ticket = 0; 53. return false; 54. } 55. IndicatorSetString(INDICATOR_SHORTNAME, szName); 56. CreateLineInfos(szName, PositionGetDouble(POSITION_PRICE_OPEN), m_Infos.corPrice, "Position opening price."); 57. CreateLineInfos(szName + def_SufixTake, PositionGetDouble(POSITION_TP), m_Infos.corTake, "Take Profit point."); 58. CreateLineInfos(szName + def_SufixStop, PositionGetDouble(POSITION_SL), m_Infos.corStop, "Stop Loss point."); 59. 60. return true; 61. } 62. //+------------------------------------------------------------------+ 63. }; 64. //+------------------------------------------------------------------+ 65. #undef def_SufixTake 66. #undef def_SufixStop 67. //+------------------------------------------------------------------+
C_IndicatorPosition.mqh
Although the code has changed somewhat, the changes are not very complex, so they do not deserve special attention. Nevertheless, as mentioned earlier, the check function now takes an argument. Let us see what has changed in this function. To do that, go to line 45, where the CheckCatch function is defined.
Notice that as soon as the function starts, we see line 47. At this stage, we convert the data that the Expert Advisor will pass to us so it can be used in the names of the objects being created. Then, in line 49, we check whether the position exists. Well, it is worth explaining why we perform this check again, since the Expert Advisor will tell us and guarantee that the position exists. Fine. The point is that I have no intention of making this position indicator an integral part of the Expert Advisor. Therefore, if a trader or even a user tries to add this indicator to the chart, we need to confirm that the position exists. Please note that in this case we are not retrieving this parameter from the indicator, as we did in the previous article.
Once the existence of the position has been confirmed, a new check is performed. This is done in line 50. In this case, however, the check is used to ensure that there is only one indicator instance for each position on the chart. There are other ways to perform the same check. Here, however, we check for the presence of a line with a specific name. If everything goes well, the indicator will work as shown in the previous article. We can therefore move on to the Expert Advisor code to understand how the system will behave when the Expert Advisor is added to the chart. The new Expert Advisor code can be seen below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.116" 07. #property link "https://www.mql5.com/pt/articles/13168" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Order System\C_Orders.mqh> 10. //+------------------------------------------------------------------+ 11. enum eTypeContract {MINI, FULL}; 12. //+------------------------------------------------------------------+ 13. input eTypeContract user00 = MINI; //Cross order in contract 14. //+------------------------------------------------------------------+ 15. C_Orders *Orders; 16. long GL_ID; 17. //+------------------------------------------------------------------+ 18. int OnInit() 19. { 20. GL_ID = 0; 21. Orders = new C_Orders(0xC0DEDAFE78514269); 22. 23. return INIT_SUCCEEDED; 24. } 25. //+------------------------------------------------------------------+ 26. void OnTick() {} 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 31. switch (id) 32. { 33. case CHARTEVENT_CHART_CHANGE: 34. if (GL_ID > 0) break; 35. else 36. { 37. GL_ID = ChartID(); 38. for (int handle, count = PositionsTotal() - 1; count >= 0; count--) 39. { 40. handle = iCustom(_Symbol, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", PositionGetTicket(count)); 41. ChartIndicatorAdd(GL_ID, 0, handle); 42. IndicatorRelease(handle); 43. } 44. } 45. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 46. EventChartCustom(GL_ID, evEA_At_ChartTrade, user00, 0, ""); 47. break; 48. } 49. } 50. //+------------------------------------------------------------------+ 51. void OnDeinit(const int reason) 52. { 53. switch (reason) 54. { 55. case REASON_REMOVE: 56. case REASON_INITFAILED: 57. EventChartCustom(GL_ID, evEA_At_ChartTrade, -1, 0, ""); 58. break; 59. } 60. 61. delete Orders; 62. } 63. //+------------------------------------------------------------------+
Expert Advisor.mq5
Notice that the code has changed very little compared with the code shown at the beginning of this article. The real reason there are so few changes is to show the reader that it does not take much effort for the system to work correctly. Please note that all changes, or rather additions to the code, are located between lines 38 and 43. But this is already enough for the Expert Advisor to at least establish a connection with the position indicator. In fact, what we are doing here used to be done inside the indicator code. In this code, one position indicator will be attached to the chart for each open position.
Nevertheless, pay attention to something important. We track all open positions, but — and this is important — we do not filter them. We simply add indicators to the chart on which the Expert Advisor is running. But a position may correspond to a symbol different from the one expected to be displayed on that chart. Fine, you might think this was my mistake, but no, I did it deliberately to show that this mistake is quite common. And by the time people notice it, an unreliable mechanism is already being used.
Notice that the problem is not that we use the _Symbol variable available in MQL5 to access the name of the chart symbol. The mistake is that we do not check whether the position returned by the PositionGetTicket function is a position that belongs to, or is somehow related to, the chart symbol, as was checked in the indicator code. This is a mistake you should always watch out for. During testing, it may seem to us that everything works correctly on a demo account. However, when moving to a real account, money can be lost because of careless programming or insufficient testing during implementation. Therefore, dear reader, if you are only learning or mastering new concepts, you should be careful when changing any code. Always thoroughly check the changes you make, and read the documentation carefully to clear up any doubts.
Fine. Now you are beginning to see that some aspects of programming are a little more complex than they may seem at first glance. Nevertheless, this is no reason to become discouraged and give up programming. The main thing is to adapt and find the right path. But you must be thinking that, in this case, it is enough to check whether the symbol of the position indicated by the ticket matches the one we expect to use. Right? To one extent or another, yes.
And I say this precisely because I do not know which market you trade. This was already mentioned at the beginning of the article. There are cases, mainly on B3 (the Brazilian exchange), where we have a full contract and a mini contract. We also have the history of the current contract. Some traders prefer to use historical data to trade. Others use the chart of the symbol they want to trade.
Note that although this may seem like a complicated situation, and you may wonder why anyone would complicate their life by trying to trade on a chart that differs from the chart of the specific contract or symbol, this is always the trader's choice. We cannot restrict the trader; our task as programmers is to meet the trader's needs.
This raises another question: how do we handle this situation if we have no way of knowing which symbol is correct? However, dear reader, the situation is not that complicated. Yes, we know where the data for the corresponding symbol is located. It is displayed in the Chart Trade indicator. And this is where the situation becomes truly interesting. The programmer has two options. The first option is to ask the Chart Trade panel which symbol is correct and whose positions should be displayed on the chart. Fine, you may think: "But why should I ask Chart Trade this question? If the data is displayed in the Chart Trade object, I just need to read the contents of that object."
You might think you should do exactly that, dear reader. The data is in the Chart Trade object, but accessing it will not be that simple. To obtain this data from Chart Trade, we would need to create a separate message, but that would significantly complicate the Expert Advisor code. However, there is another solution, which is much simpler, but we need to proceed correctly. Otherwise, we will break the encapsulation that was created and tested quite a long time ago. If you calmly think about it and analyze the code, you will notice that the Chart Trade panel obtains the symbol data from the C_Terminal class. More precisely, from the CurrentSymbol procedure, which is on line 47 of the C_Terminal class.
This is where the danger lies, especially for those who are just starting to learn programming. Many novice developers will try to access this procedure directly from the Expert Advisor. This is impossible because the procedure is in the protected section. So you might think: "I will move this procedure to the public area." That is a mistake. And the second mistake would be this: if you look at the procedure, you will notice that it does not return a value or accept any arguments. You then decide to change it so that it returns the data for the correct symbol, but by doing so, you completely break the procedure, because in the future this may change, allowing us to add more functionality. Therefore, the correct solution is NOT to touch the procedure on line 47 of the C_Terminal class.
So, as a beginner, you might imagine: "Well, this problem cannot be solved, because it is impossible to know which symbol will be used." But that is the wrong way to formulate the problem. Yes, there is a solution, and it is simple, elegant, and efficient. It will have no side effects on the code that has already been created and tested. All we need to do is go to the constructor of the C_Terminal class and make a change there. The source code can be seen in the following fragment:
106. //+------------------------------------------------------------------+ 107. C_Terminal(const long id = 0, const uchar sub = 0) 108. { 109. m_Infos.ID = (id == 0 ? ChartID() : id); 110. m_Mem.AccountLock = false; 111. m_Infos.SubWin = (int) sub; 112. CurrentSymbol(false); 113. ZeroMemory(m_Mouse); 114. m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
Original fragment of the C_Terminal.mqh file
The necessary changes are shown below:
106. //+------------------------------------------------------------------+ 107. C_Terminal(const long id = 0, const uchar sub = 0, const bool bFull = false) 108. { 109. m_Infos.ID = (id == 0 ? ChartID() : id); 110. m_Mem.AccountLock = false; 111. m_Infos.SubWin = (int) sub; 112. CurrentSymbol(bFull); 113. ZeroMemory(m_Mouse); 114. m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
Modified fragment of the C_Terminal.mqh file
Did you notice the difference between the two pieces of code? It is very subtle, simple, and elegant. Now we can use the C_Terminal class to obtain the symbol name directly in the Expert Advisor. Or we can pass the data to the position indicator so that it performs this task for us. In either case, the Expert Advisor will now be able to tell the position indicator whether it wants to display full contracts or mini contracts. This is achieved by directly using the contract history. You might think this would create a problem because it could conflict with what is specified in Chart Trade, but in fact it does not. The data will match exactly what is specified in Chart Trade.
So, let us implement this solution. Since I do not want to run tests in the indicator, because its purpose is entirely different, we will test the data directly in the Expert Advisor. Thus, the corrected Expert Advisor code can be seen below:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.116" 07. #property link "https://www.mql5.com/pt/articles/13168" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Order System\C_Orders.mqh> 10. #include <Market Replay\Auxiliar\C_Terminal.mqh> 11. //+------------------------------------------------------------------+ 12. enum eTypeContract {MINI, FULL}; 13. //+------------------------------------------------------------------+ 14. input eTypeContract user00 = MINI; //Cross order in contract 15. //+------------------------------------------------------------------+ 16. C_Orders *Orders = NULL; 17. C_Terminal *Terminal = NULL; 18. //+------------------------------------------------------------------+ 19. int OnInit() 20. { 21. Orders = new C_Orders(0xC0DEDAFE78514269); 22. 23. return INIT_SUCCEEDED; 24. } 25. //+------------------------------------------------------------------+ 26. void OnTick() {} 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 31. switch (id) 32. { 33. case CHARTEVENT_CHART_CHANGE: 34. if (Terminal != NULL) break; 35. else 36. { 37. ulong ul; 38. Terminal = new C_Terminal(0, 0, user00); 39. for (int handle, count = PositionsTotal() - 1; count >= 0; count--) 40. { 41. ul = PositionGetTicket(count); 42. if (PositionGetString(POSITION_SYMBOL) != (*Terminal).GetInfoTerminal().szSymbol) continue; 43. handle = iCustom(_Symbol, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", ul); 44. ChartIndicatorAdd((*Terminal).GetInfoTerminal().ID, 0, handle); 45. IndicatorRelease(handle); 46. } 47. } 48. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 49. if (Terminal != NULL) 50. EventChartCustom((*Terminal).GetInfoTerminal().ID, evEA_At_ChartTrade, user00, 0, ""); 51. break; 52. } 53. } 54. //+------------------------------------------------------------------+ 55. void OnDeinit(const int reason) 56. { 57. switch (reason) 58. { 59. case REASON_REMOVE: 60. case REASON_INITFAILED: 61. if (Terminal != NULL) 62. EventChartCustom((*Terminal).GetInfoTerminal().ID, evEA_At_ChartTrade, -1, 0, ""); 63. break; 64. } 65. 66. delete Orders; 67. delete Terminal; 68. } 69. //+------------------------------------------------------------------+
Expert Advisor.mq5
This is the beauty of programming. Compare the previous code with the other Expert Advisor code shown in this article, and notice how beautiful and fascinating programming is. By using a little knowledge of pointers in C/C++, here in MQL5 we were able to remove the global variable and leave only the class declarations. In reality, these would be pointers to class objects. But that is fine: MQL5 does not use pointers in the same way as C/C++. Nevertheless, we can use part of what is available in C/C++. If you do not understand what is happening in this Expert Advisor, do not worry, because I am here to help you understand this code.
Notice that in line 10 we included the C_Terminal.mqh header file, which we have just adapted to our needs. In line 17 we declare a global variable so that we can access the class. However, although this is not mandatory, I want to explicitly show that the pointer variable is initialized with the value NULL. If you do not specify this parameter, the compiler will do it implicitly. But it is also important to clarify exactly what initialization we are performing.
Fine. As soon as the OnInit function has been executed, MetaTrader 5 will trigger the CHART_CHANGE event. It will be intercepted by the OnChartEvent function on line 28, which will start event processing in the Expert Advisor. The first time this function is executed, the Terminal pointer will have the value NULL. Consequently, the condition on line 34 will evaluate to false, allowing line 35 to be executed. This is where the magic begins. Notice that the code here is very similar to the code we saw earlier in the Expert Advisor. However, in line 38 we call the constructor of the C_Terminal class, specifying whether we will use one contract type or the other.
As a result, the constructor will generate the correct symbol name. Thus, in line 41 we obtain the ticket value. We need to do this before calling other functions to obtain position data. This is mentioned in the MQL5 documentation; study it for more detailed information. After that, in line 42, we can check whether the name of the symbol to which the position belongs matches the one expected by the Expert Advisor. If it does not, we return to line 39. Otherwise, that is, if the symbol has the expected name, we call the position indicator, as was done earlier.
Now pay attention to lines 49 and 61. Answer this: why do I perform the check on these lines? Think for a moment before reading the answer I provide below. Fine, I hope you at least made an attempt before reading the answer. The reason these lines exist is precisely to increase the reliability of the Expert Advisor code. If you try to call any function or procedure of the C_Terminal class before the class has been properly initialized, checking the pointer value against NULL will prevent the Expert Advisor from simply generating an error in the MetaTrader 5 terminal. By performing this check, you ensure that the execution flow remains as expected.
Many C/C++ programmers, but mainly C programmers, usually do not perform these checks when using pointers. This is why many people believe that programs written in C contain many startup errors. However, such errors are not related to the language itself, but to how the program was written, or to the reason it was written that way. In some cases, programs that search for vulnerabilities in a system are even created deliberately in order to perform an action not intended by the system owner.
Final thoughts
In this article, we began bringing together various applications that were previously completely isolated from each other. Although Chart Trade, the mouse indicator, and the Expert Advisor already had a certain relationship, there was still no way to directly display on the chart the positions open on the trading server. A system of opposing orders is often used. From this point on, this becomes possible, opening up many opportunities for new ideas and implementations. Although we are only beginning to put the components into operation, we already have a direction to follow.
Yes, at first glance this system is very primitive and could have been presented in a more advanced state, but I did not consider that appropriate. It would have left many beginning programmers without a real understanding of what was happening in the code. Remember that changes happen gradually so that everyone can keep up with them. This material has a didactic purpose; it is not intended to demonstrate the only way to do something in MetaTrader 5. I hope all this material helps those who are truly seeking knowledge. In the next article, we will continue to further develop what we have seen here. See you soon!
| File | Description |
|---|---|
| Experts\Expert Advisor.mq5 | Demonstrates the interaction between Chart Trade and the Expert Advisor (Mouse Study is required for interaction). |
| Indicators\Chart Trade.mq5 | Creates a window for configuring the order to be sent (Mouse Study is required for interaction). |
| Indicators\Market Replay.mq5 | Creates controls for interacting with the replication/simulation service (Mouse Study is required for interaction). |
| Indicators\Mouse Study.mq5 | Provides interaction between graphical controls and the user (this is required both for working with the replication/simulation system and in the real market). |
| Indicators\Order Indicator.mq5 | Responsible for indicating market orders, enabling interaction with them and control over them. |
| Indicators\Position View.mq5 | Responsible for indicating market positions, enabling interaction with them and control over them. |
| Services\Market Replay.mq5 | Creates and maintains the market replication/simulation service (the main file of the entire system). |
Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/13168
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Beyond the Clock (Part 4): Efficacy of Bars on Trending and Mean-Reversion Strategies
From Basic to Intermediate: FileSave and FileLoad
Building a Synthetic Custom Symbol in MQL5 Using Multi-Symbol Price Averaging
Trading Robot Based on a GPT Language Model
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use