Market Simulation: Position View (VIII)
Introduction
Hello, dear friends! Welcome, everyone, to another article in our series on replication and simulation systems.
In the previous article, " Market Modeling: Position View (VII)," we examined how to implement a position indicator that allows you to close an open position directly from the chart by interacting with an object available on the chart. After completing and testing the first mechanism, we began making changes to ensure that take-profit and stop-loss levels could be removed for an open position. However, since the necessary changes required detailed explanations, in that same article I showed only the changes that needed to be made to the expert advisor; I still needed to show the changes that needed to be made to the position indicator.
The changes to the code itself are relatively minor. Nevertheless, as I observed the audience that had shown interest in this replication/simulation system, I noticed that many—if not the vast majority—did not have much experience in programming, let alone in the type of programming that I teach and use. Therefore, in my opinion, a slightly more detailed explanation is needed so that everyone can understand the code and, if they wish, modify it to solve specific problems. The entire code presented here is intended solely for educational purposes. So try to examine both the code itself and the explanation I will provide, because we are about to get down to some serious work.
Modification of the Position Indicator
Since I plan to cover a bit more in this article, let's start by reviewing what was left unresolved in the previous post. Thus, we move on directly to the C_IndicatorPosition class, the new code for which is shown below.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #include "C_ElementsTrade.mqh" 05. //+------------------------------------------------------------------+ 06. class C_IndicatorPosition 07. { 08. private : 09. struct st00 10. { 11. ulong ticket; 12. color corPrice, corTake, corStop; 13. string szShortName; 14. }m_Infos; 15. C_ElementsTrade *Open, 16. *Stop, 17. *Take; 18. //+------------------------------------------------------------------+ 19. public : 20. //+------------------------------------------------------------------+ 21. C_IndicatorPosition(color corPrice, color corTake, color corStop) 22. { 23. ZeroMemory(m_Infos); 24. m_Infos.corPrice = corPrice; 25. m_Infos.corTake = corTake; 26. m_Infos.corStop = corStop; 27. Open = Take = Stop = NULL; 28. } 29. //+------------------------------------------------------------------+ 30. ~C_IndicatorPosition() 31. { 32. delete Open; 33. delete Take; 34. delete Stop; 35. } 36. //+------------------------------------------------------------------+ 37. bool CheckCatch(ulong ticket) 38. { 39. m_Infos.szShortName = StringFormat("%I64u", m_Infos.ticket = ticket); 40. if (!PositionSelectByTicket(m_Infos.ticket)) return false; 41. if (ObjectFind(0, m_Infos.szShortName) >= 0) 42. { 43. m_Infos.ticket = 0; 44. return false; 45. } 46. IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName); 47. EventChartCustom(0, evUpdate_Position, ticket, 0, ""); 48. 49. return true; 50. } 51. //+------------------------------------------------------------------+ 52. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 53. { 54. double value; 55. 56. if (Open != NULL) (*Open).DispatchMessage(id, lparam, dparam, sparam); 57. if (Take != NULL) (*Take).DispatchMessage(id, lparam, dparam, sparam); 58. if (Stop != NULL) (*Stop).DispatchMessage(id, lparam, dparam, sparam); 59. switch (id) 60. { 61. case CHARTEVENT_CUSTOM + evUpdate_Position: 62. if (lparam != m_Infos.ticket) return; 63. if (!PositionSelectByTicket(m_Infos.ticket)) 64. { 65. ChartIndicatorDelete(0, 0, m_Infos.szShortName); 66. return; 67. }; 68. if (Open == NULL) Open = new C_ElementsTrade(m_Infos.ticket, evMsgClosePositionEA, m_Infos.corPrice, ePriorityNull, "Position opening price."); 69. if (Take == NULL) Take = new C_ElementsTrade(m_Infos.ticket, evMsgCloseTakeProfit, m_Infos.corTake, ePriorityOrders, "Take Profit point."); 70. if (Stop == NULL) Stop = new C_ElementsTrade(m_Infos.ticket, evMsgCloseStopLoss, m_Infos.corStop, (EnumPriority)(ePriorityOrders + 1), "Stop Loss point."); 71. (*Open).UpdatePrice(PositionGetDouble(POSITION_PRICE_OPEN)); 72. if ((value = PositionGetDouble(POSITION_TP)) > 0) (*Take).UpdatePrice(value); else 73. { 74. delete Take; 75. Take = NULL; 76. } 77. if ((value = PositionGetDouble(POSITION_SL)) > 0) (*Stop).UpdatePrice(value); else 78. { 79. delete Stop; 80. Stop = NULL; 81. } 82. break; 83. } 84. ChartRedraw(); 85. } 86. //+------------------------------------------------------------------+ 87. }; 88. //+------------------------------------------------------------------+
C_IndicatorPosition.mqh
Well, chances are you don't understand a thing about what happened here, because the code changed quite drastically literally overnight. This change was deliberate and timely. This is because we need to begin decoupling parts of the code so that in the future, when we move on to developing the pending orders module, we will not have to write a lot of new code or make it so dense that it would be difficult to extract reusable fragments. So, dear reader, don't be alarmed or worried when changes occur. These changes will take place as we separate out portions of the code that can be reused in the future.
Great—if you look at the code above, you will immediately notice in line 04 that we have included a new header file. We will discuss this toward the end of this article. Don't worry about that for now. Let's focus on this file. Thus, in line 15, we declare three pointers to the C_ElementsTrade class. These pointers give us controlled access to instances of the C_ElementsTrade class.
C_ElementsTrade. Note the following: I'm using three pointers because, in the C_ElementsTrade class, we'll create everything needed to display the position indicator correctly. Since several components will be common to the displayed indicators—such as the close button, the price level indicator line, and other elements we will add later—we can combine everything into a single class to simplify things. And when we reference the class that creates these graphical elements, we can easily reuse the same structure in different display variations. Or, rather, we can reuse the same set of visual elements for both the stop-loss and take-profit indicators—provided, of course, that we make a clear distinction in the code so that the trader can easily understand what each indicator is. We will not have to write code for a separate class just to set a stop-loss, and another one just for a take-profit. I am not sure if my explanation was clear. But once you see more of the code, you will understand what is going on.
So, the compiler usually initializes variables with an appropriate value. However, since we want to be sure of this, we initialize them in the constructor. This happens on line 27, where we specify that the pointers will be initialized to NULL. Please pay special attention to this detail, as it is important for you to know. It is also worth noting that in the destructor, we free the memory associated with the pointers, thereby returning it to the system. This is done between lines 32 and 34. But at this point, you might ask yourself, "Why use the `delete` command if we have not yet allocated memory using the `new` command?" "That does not make sense." Indeed, at first glance, it does not make sense. But you should not ignore variables that are pointers. Very often, an error occurs at some point while a program is running. This happens because the program performed some kind of operation involving pointers. And since our pointers will be declared in the C_IndicatorPosition class, when an object of that class is destroyed, we must ensure that the memory is freed correctly.
All right. That was a preliminary explanation. Now let's take a look at where additional changes were made to the source code. So, let's move on directly to line 52, where the implementation of the DispatchMessage procedure is located. At this point, if you are not paying close attention, you will get confused, because this is exactly where the magic happens. Note that the rest of the code remains virtually the same as before. In this procedure, line 52 contains several things that might confuse you, dear reader. The first thing that might cause a lot of confusion is the code that appears between lines 56 and 58. Why do I perform these checks before calling the `DispatchMessage` procedure for each of the elements we will add to the chart? The reason is very simple. When this procedure is called for the first time, on line 52 WE HAVE NO ELEMENTS ON THE CHART. Therefore, unless we check whether the pointers actually refer to something valid in memory, we risk dereferencing invalid pointers. However, since MQL5 appears to have some kind of security mechanism, attempting to run "junk" code will result in an error, causing the indicator to be forcefully removed from the chart. "All right, I get it. "Won’t we have to perform this check all the time?" That's right; it must be done every time the procedure described in line 52 is carried out. You will soon figure out why. Let's move on to the next section, where we begin handling the event that this procedure is waiting for. Processing of this event begins on line 61. Now pay very close attention, because the first thing we do is check, on line 62, whether the value of the lparam parameter matches the ticket that the indicator is tracking. If the values differ, we stop processing at this stage. If they match, we check in line 63 to see if that position exists on the server. If it no longer exists, in line 65 we remove the position indicator from the chart, and in line 66 we finish processing the message. If these initial checks are successful, in line 68 we check whether the object representing the opening price has already been created. This check will be true only the first time we execute the call. In all other cases, it will be false. Since this is the first call, we will use the `new` operator to call the constructor. It is important that you understand this. The reason is that you can instruct the constructor to create a specific structure for the elements. Therefore, by using the same class, you can create one indicator for take-profit, another for stop-loss, and yet another for the opening price. That is exactly what we do between lines 68 and 70. Please note that in any case, we initialize the pointers, except for those that have already been initialized. We can further improve this system to avoid unnecessary calls. But we will see that later. First, it is important that you understand how everything works.
After initializing all three pointers in lines 68 and 70, in line 71 we instruct the C_ElementsTrade class—to which the OPEN pointer refers—to update the price. You might think or imagine that this is silly and that we could just do it directly in the constructor. Actually, we could. Of course, provided that we were certain it was a HEDGING account. This is because, if we use a NETTING account, the price may differ, since the server provides us with a value that corresponds to the average price of the position. That is why I do not pass the value directly to the constructor of the C_ElementsTrade class. In the future, we will do it in a different way. Do not lose hope, dear reader. Let's take it one step at a time. Understand this simpler code first so that you can understand the more complex one later.
All right. Now we are getting to a really interesting part of this DispatchMessage procedure. What I explain here also applies to the code in lines 77–81. It is not difficult. Just pay attention, and you will easily understand what is going on. First, we retrieve the value we need and assign it to the `value` variable . Next, we check whether this value is greater than zero. If that is the case, then there is a protective level—whether it is a take-profit or a stop-loss. In any case, if the value exists, we will pass it to the corresponding C_ElementsTrade object. In other words, we will do something similar to what was done in line 71. However, there are some nuances regarding the update of the opening price. If the value being checked is zero, this means that the corresponding expected level has not been set. Therefore, the elements representing the price should be removed from the chart. You might have expected to see this here, in the C_IndicatorPosition class. However, upon examining the code, it appears that there is no part of it where objects are removed from the chart. But yes, it is here. We do this in line 74, since that is where we call the corresponding destructor. Since executing the `delete` command does not set the pointer to `NULL` but merely frees the memory in use, we need to explicitly tell the pointer that it is now a null pointer. This is done on line 75. Please take note of this. The address referenced by the pointer may contain "junk." Since the code needs to check whether the pointer's value is zero or not, we need to explicitly set it to zero. Not because this is something you should always do, but because the code will not finish executing, and during checks in lines 56 through 58, the pointer might point to "junk." This will cause the code to malfunction as soon as you remove the corresponding protective level on the server, whether it is a stop-loss or take-profit value.
Great. Up until now, I have focused only on explaining the part of the code related to the C_IndicatorPosition class. The explanation will not be complete until we look at the C_ElementsTrade class. It is this class that does all the work for us. To keep things properly organized, we will cover this in a new section.
Start of the C_ElementsTrade Class
Well, everything we have covered so far may seem complicated and confusing to many people. If this is your case, do not lose hope. Go back a few articles and start going through everything at a more leisurely pace, since I try to present the material in the simplest and most understandable way possible. I know that much of what I am showing seems extremely complicated and hard to understand. This is because you did not expect what is shown here to actually be possible, since practically no one has ever demonstrated anything like this before.
In any case, let's continue. Now we will start looking at the new code, which we will be working with quite a lot. From now on, you will not see changes to the code we discussed earlier as often. From now on, our focus will be specifically on this level. All right. The new class we are going to create is shown below, along with its complete code:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameHLine m_Info.szPrefixName + "#HLINE" 05. #define def_NameBtnClose m_Info.szPrefixName + "#CLOSE" 06. //+------------------------------------------------------------------+ 07. #define def_PathBtns "Images\\Market Replay\\Orders\\" 08. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp" 09. #resource "\\" + def_Btn_Close; 10. //+------------------------------------------------------------------+ 11. #include "..\Auxiliar\C_Terminal.mqh" 12. //+------------------------------------------------------------------+ 13. class C_ElementsTrade : private C_Terminal 14. { 15. private : 16. //+------------------------------------------------------------------+ 17. struct st00 18. { 19. ulong ticket; 20. string szPrefixName; 21. EnumEvents ev; 22. double price; 23. }m_Info; 24. //+------------------------------------------------------------------+ 25. void UpdateViewPort(void) 26. { 27. int x, y; 28. 29. ChartTimePriceToXY(0, 0, 0, m_Info.price, x, y); 30. ObjectSetDouble(0, def_NameHLine, OBJPROP_PRICE, m_Info.price); 31. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, 130); 32. ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y); 33. } 34. //+------------------------------------------------------------------+ 35. public : 36. //+------------------------------------------------------------------+ 37. C_ElementsTrade(const ulong ticket, const EnumEvents ev, color _color, EnumPriority ePrio, string szDescr = "\n") 38. :C_Terminal() 39. { 40. string szObj; 41. 42. ZeroMemory(m_Info); 43. m_Info.szPrefixName = StringFormat("%I64u@%d", m_Info.ticket = ticket, (int)(m_Info.ev = ev)); 44. CreateObjectGraphics(szObj = def_NameHLine, OBJ_HLINE, _color, ePrio); 45. ObjectSetString(0, szObj, OBJPROP_TEXT, szDescr); 46. ObjectSetString(0, szObj, OBJPROP_TOOLTIP, szDescr); 47. ObjectSetInteger(0, szObj, OBJPROP_SELECTABLE, ePrio != ePriorityNull); 48. CreateObjectGraphics(szObj = def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityOrders + 1)); 49. ObjectSetString(0, szObj, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close); 50. ObjectSetInteger(0, szObj, OBJPROP_ANCHOR, ANCHOR_CENTER); 51. } 52. //+------------------------------------------------------------------+ 53. ~C_ElementsTrade() 54. { 55. if (m_Info.szPrefixName != "") 56. ObjectsDeleteAll(0, m_Info.szPrefixName); 57. } 58. //+------------------------------------------------------------------+ 59. inline void UpdatePrice(const double price) 60. { 61. m_Info.price = price; 62. UpdateViewPort(); 63. } 64. //+------------------------------------------------------------------+ 65. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 66. { 67. switch (id) 68. { 69. case CHARTEVENT_OBJECT_CLICK: 70. if (sparam == def_NameBtnClose) 71. { 72. if (PositionSelectByTicket(m_Info.ticket)) switch (m_Info.ev) 73. { 74. case evMsgClosePositionEA: 75. EventChartCustom(0, evMsgClosePositionEA, m_Info.ticket, 0, ""); 76. break; 77. case evMsgCloseTakeProfit: 78. EventChartCustom(0, evMsgCloseTakeProfit, m_Info.ticket, PositionGetDouble(POSITION_SL), PositionGetString(POSITION_SYMBOL)); 79. break; 80. case evMsgCloseStopLoss: 81. EventChartCustom(0, evMsgCloseStopLoss, m_Info.ticket, PositionGetDouble(POSITION_TP), PositionGetString(POSITION_SYMBOL)); 82. break; 83. }; 84. } 85. break; 86. case CHARTEVENT_CHART_CHANGE: 87. UpdateViewPort(); 88. break; 89. } 90. } 91. //+------------------------------------------------------------------+ 92. }; 93. //+------------------------------------------------------------------+ 94. #undef def_Btn_Close 95. #undef def_PathBtns 96. //+------------------------------------------------------------------+ 97. #undef def_NameBtnClose 98. #undef def_NameHLine 99. //+------------------------------------------------------------------+
C_ElementsTrade.mqh
Now the topic has really gotten more complicated, since many people might have expected much more extensive code, with much more information or a larger number of working elements. Looking at the code above, it becomes clear that the process works quite differently than many people expected. Let's see what is going on here. And first and foremost: let's take a look at why this code is able to do what it promises—namely, to allow a trader to use a graphical element, in the form of a button on the price line, to close a position or remove the corresponding level. In this case, we are talking about stop-loss and take-profit levels. At the same time, this allows us to close an open position. All of this is presented in a fairly simple and elegant way.
Well, if you have read this far, it means you are really curious to understand how this mechanism works. So, let's start with this: forget all that confusion about endlessly assigning names to graphical objects. This just makes our work unnecessarily complicated. Try to always use a simple and effective naming convention. Second: do not try to control or manipulate what is on the chart. Let MetaTrader 5 take care of that for you. In this implementation, we will not use lists of objects or structures of this type. We will simply create the objects properly and let MetaTrader 5 manage them as best as possible, keeping the list of objects on the chart under the control of the MetaTrader 5 terminal itself.
Just a quick note regarding what I just said: MetaTrader 5 is not a magic tool. If used incorrectly, it will not function properly. You need to understand MetaTrader 5 to truly take advantage of all its features. So, the first thing we find in the preceding code is, in fact, several macro definitions. Please note that lines 04 and 05 contain two definitions that will be used only in this header file. I say "only" because in lines 97 and 98, we removed those same definitions. If you try to use them outside of this file, the compiler will generate errors, and the code will not compile. All right, let's take a look at what we see in these two definitions. We have a variable, which we will discuss later, and a string. That's all—nothing more. This is more than enough for MetaTrader 5 to process this data for us, storing the objects we created in this code in an internal list. We do not need a separate list just for this; let MetaTrader 5 handle the list of objects on its own.
Lines 7 through 9 have already been discussed and do not warrant special mention. Next, we can look at the class code and notice that there are few procedures and functions here. We are just getting started on writing this code. Let's not rush things. The first thing to do when reviewing a class is to examine the procedures for creating and destroying it. These are the constructor and the destructor. So, on line 37, we find the declaration of the class constructor. Now I want you to go back to the previous topic and see what each of these parameters means in relation to what was explained earlier. Note that the code for this constructor is quite simple. All we are doing is creating objects that will be displayed on the chart. Essentially, I am taking the code that previously existed in the C_IndicatorPosition class and duplicating it here. It is simple. Unlike the previous example, where only one horizontal line was created, in this case we have created two objects. One of them is the line created in line 44 of the code. The second one is the interaction button. It is created on line 48 of the code. Now we have reached the point where the definitions in lines 04 and 05 will start to make sense. Please note that in line 43, I am assigning a value to the same variable that was used in the definitions. In other words, we use these definitions to tell MetaTrader 5: I want to change something in a specific object, or I want you to let me know if that object has received an event. MetaTrader 5 will understand our request and respond appropriately by performing exactly the action that was required. Therefore, we do not need an additional list of objects. MetaTrader 5 will take over management of this structure. And that is great, because it saves us from having to do a significant amount of the work by hand. But before we move on to the destructor, I want to draw your attention to one detail. Remember, back when we were trying to position the interaction button, we had to adjust its position by subtracting a certain value? Well, as I said back then, we have a better way. The correct way is to use line 50. But there is a catch: using this line of code, the button will be positioned at the point we specify. We will discuss this issue in more detail in the future. For now, please note that we no longer need to make that adjustment.
All right. Just as the constructor reflects what already existed, the same applies to the destructor in line 53. I am not going to highlight this; I am just mentioning it.
Note that, in addition to the ones we have already discussed, we have three more procedures. Although there could only be two of them. For practical reasons, I decided to divide the implementation into three procedures. The procedure on line 59 is a basic one; it simply delegates execution to the procedure on line 25. This procedure is also quite simple. Its sole purpose is to display objects on the chart in their correct locations. This does not warrant a separate mention, since we have been talking about these objects for quite some time now. However, the procedure on line 65 really does deserve a special mention, since without it, none of what you see in the chart would actually happen. Let's take a moment to calmly go over this procedure.
In the `DispatchMessage` method—which is the core of this class—we handle two events right at the beginning. One of them is a click on objects, and the other is a chart update event. Well, the update event that appears on line 86 is simple and straightforward. It simply positions elements or objects on the chart—something we have already done before. However, handling a click event is a bit more complicated than in the previous version. Do not worry—it is just a little more complicated, but that is nothing out of the ordinary.
Note that when executing line 69, the first thing we do is check whether the object passed by MetaTrader 5 matches the one we expect to process. In other words, an object of CLOSE type. Once again: do not think in rigid patterns. Each instance of the C_ElementsTrade class will correspond to one and only one of the data elements that we need to display. In other words, we do not have to know whether the CLOSE object corresponds to the take-profit level, the stop-loss level, or the opening price. It does not really matter. The main thing is that the object be of type CLOSE. That's it; we will figure out exactly what this object refers to later.
Therefore, if the check on the object is true, we will perform a new check on line 72. In this check, we verify whether the ticket stored in the class corresponds to an existing position. This check is important so that we can distinguish between orders and positions. Although we have not discussed orders yet, we are already laying the groundwork so that we do not have to make major changes to the code when implementing them. If we pass this new check as well, we will enter a small mechanism. It will do what many would expect to see at the beginning of the code.
Here, we will check which specific element received the click event. How is that possible? Will not MetaTrader 5 inform us about this? To some extent, MetaTrader 5 indicates this correctly. Since I want to explain how this works in a straightforward way, we are taking a different approach. That way, you will be able to figure out later how to work in a much more thoughtful way. In any case, the idea is the same. The current class contains a value that is defined when the object is created. In other words, go back to the constructor's code and note that on line 43, we are saving the same value that will be checked here, between lines 73 and 83.
Here is where the interesting part happens. When you specify in the C_IndicatorPosition class that the C_ElementsTrade class should create elements or graphical objects to display take-profit levels, you are actually establishing a distinction between the sets being created. This division is set on line 43, but it is used only between lines 73 and 83.
Please note the following: If the set, which was recreated in the C_IndicatorPosition class, refers to a stop-loss, then when the stop-loss interaction button is clicked, the code in line 81 will actually be executed, sending a message to the expert advisor to tell it: “Listen, expert advisor, I want the stop-loss value to be removed.” The expert advisor will send a request to the server to perform this task. When the server responds, indicating whether the request was accepted or not, the expert advisor will send a message to the chart, informing everyone that an event has occurred. The position indicator is waiting for this event. And in the C_IndicatorPosition class, it will free the C_ElementsTrade instance associated with the stop-loss. Thus, all objects associated with this instance will be deleted. The class destructor instructs MetaTrader 5 to remove from the chart the objects associated with that name prefix.
That is why I said at the beginning of this thread that there's no need to worry about certain implementation details. If you follow the correct steps, MetaTrader 5 will help you complete the necessary tasks.
Concluding Thoughts
In this article, we began to examine some of the details in greater depth. Since I want to explain everything and make the material as clear as possible, it may seem to many that we are moving slowly. But if I really did set the pace that I think is appropriate, in the end, very few people would be able to understand what is actually going on. Among those few, even fewer would be able to modify the code to adapt it to their specific goals. So, if you already have solid programming knowledge, please be patient with those who are just starting out.
For those who want to test the system, I have included the necessary files as compiled binaries in the attachment. To ensure that the entire process works correctly, do not change the directory structure provided in the application. Just extract the files and test the system. To do this, use the Mouse Study indicator, the Chart Trade indicator, and the Expert Advisor. You need to place these three applications on the chart. The position indicator will load automatically as needed.
And one last thing. Even if you choose to use only the Expert Advisor and MetaTrader 5's one-click trading system, you’ll get the same result as if all three components were loaded on the chart. Nevertheless, I recommend that you start getting used to using the set of all three applications. We will continue to improve the system. Some things will not work if you use the system differently.
| File | Description |
|---|---|
| Experts\Expert Advisor.mq5 | Demonstrates the interaction between Chart Trade and the Expert Advisor (Mouse Study is required for this 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 market replay/simulation service. (Mouse Study is required for interaction) |
| Indicators\Mouse Study.mq5 | Facilitates interaction between graphical controls and the user (which is necessary both for the market replay/simulation system and in the real market). |
| Indicators\Order Indicator.mq5 | Responsible for displaying market orders, allowing users to interact with and manage them. |
| Indicators\Position View.mq5 | Responsible for displaying market positions, enabling interaction with them, and managing them. |
| Services\Market Replay.mq5 | Creates and maintains the market replay/simulation service (the main file of the entire system). |
Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/13257
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.
Features of Custom Indicators Creation
Neural Networks in Trading: Unraveling Structural Components (SCNN)
Features of Experts Advisors
From Basic to Intermediate: Like Bubbles
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use