Русский Español Português
preview
Market Simulation (Part 05): Creating the C_Orders Class (II)

Market Simulation (Part 05): Creating the C_Orders Class (II)

MetaTrader 5Examples |
747 1
Daniel Jose
Daniel Jose

Introduction

In the previous article, Market Simulation (Part 04): Starting the C_Orders Class (I), I was primarily focused on explaining how the code for sending market trade orders would look. The entire explanation aimed to demonstrate how you could structure the class code in order to decode the information received from the Chart Trade indicator.

However, even without having seeing the source code of the Expert Advisor, I believe that those with some experience could already implement it in practice. And even though it might seem that the code shown in the previous article would not be capable of executing operations on a live trading server, that is not entirely true. It is true, though, that the code would not be sufficient for use with HEDGING accounts.

For NETTING accounts, that code would already be able to open and close positions. But for HEDGING-type accounts, things work a little differently.

If you attempt to close a buy position using the sell button, for example, you would actually be opening a sell position. This applies to HEDGING accounts. In a NETTING account, performing the same action would close the buy position, or at the very least, would produce some kind of effect, such as:

  • A position reversal (flip). This would occur if you sold a larger volume than your existing buy position. In this case, you would move from being long to being short. The size of the new short position would be the difference between the volume of the previous buy position and the volume sold in the reversal.
  • Partial closing. This would occur if the volume sold was smaller than the existing buy position. In that case, part of the buy volume would be closed, leaving the remaining portion still open in the market.

All of this is quite interesting and has already been explained in detail in another series of articles I published some time ago. That series aimed to thoroughly explain how to create an Expert Advisor that could operate automatically. It consisted of 15 articles, each covering one of the aspects and precautions necessary to develop such an Expert Advisor. From a manually operated model all the way to a fully automated system. Those interested in learning more should refer to that series. The first article can be found here: Learning to Build an Automatically Operating EA (Part 01): Concepts and Structures.

Much of the code that will be shown here originates from that same series. The emphasis here, however, will be slightly different. This is because our primary goal is different. In other words, we are not aiming to develop a system for sending orders or communicating with a live trading server. What we actually want — and will do — is develop a simulated server. However, before we can do that, we need to implement a few things. Since the ideal scenario is to have shared components between our replay/simulation system and the real trading server (whether in a demo or live account), we will first implement the Expert Advisor so that it can communicate with the real server. Once that implementation is complete, we will then proceed to develop the simulated trading server.

The reason for doing it this way is to define and restrict the types of calls we actually need to implement. At least initially, I do not intend to simulate every single call that a real server can respond to, since in most cases we simply don't need all of them. Over time, I may change my mind, but for now, this approach will suffice. So, we can now begin the first topic of this article.


Handling the Message to Close Positions

Alright. If you haven't yet understood the content of the previous article, I suggest you go back and review it first, as we'll simply be continuing from there. However, since a small improvement was made to the code structure, you will be able to see the complete code again in full below. That said, the parts that were already explained in the previous article will not be revisited here, as the structural changes made do not invalidate any of the explanations given earlier. So, let's move on to the code itself.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Defines.mqh"
005. //+------------------------------------------------------------------+
006. class C_Orders
007. {
008.    protected:
009. //+------------------------------------------------------------------+
010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; }
011. //+------------------------------------------------------------------+
012.       bool ClosePosition(const ulong ticket)
013.          {
014.             bool   IsBuy;
015.             string szContract;
016.             
017.             if (!PositionSelectByTicket(ticket)) return false;
018.             IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
019.             szContract = PositionGetString(POSITION_SYMBOL);
020.             ZeroMemory(m_Base.TradeRequest);
021.             m_Base.TradeRequest.action    = TRADE_ACTION_DEAL;
022.             m_Base.TradeRequest.type      = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
023.             m_Base.TradeRequest.price     = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS));
024.             m_Base.TradeRequest.position  = ticket;
025.             m_Base.TradeRequest.symbol    = szContract;
026.             m_Base.TradeRequest.volume    = PositionGetDouble(POSITION_VOLUME);
027.             m_Base.TradeRequest.deviation = 1000;
028.             
029.             return SendToPhysicalServer() != 0;
030.          };
031. //+------------------------------------------------------------------+   
032.    private   :
033. //+------------------------------------------------------------------+
034.       struct stBase
035.       {
036.          MqlTradeRequest TradeRequest;
037.          ulong           MagicNumber;
038.          bool            bTrash;
039.       }m_Base;
040. //+------------------------------------------------------------------+
041.       struct stChartTrade
042.       {
043.          struct stEvent
044.          {
045.             EnumEvents  ev;
046.             string      szSymbol,
047.                         szContract;
048.             bool        IsDayTrade;
049.             ushort      Leverange;
050.             double      PointsTake,
051.                         PointsStop;
052.          }Data;
053. //---
054.          bool Decode(const EnumEvents ev, const string sparam)
055.             {
056.                string Res[];
057.       
058.                if (StringSplit(sparam, '?', Res) != 7) return false;
059.                stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])};
060.                if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc;
061.                else return false;
062.                
063.                return true;
064.             }
065. //---
066.       }m_ChartTrade;
067. //+------------------------------------------------------------------+
068.       ulong SendToPhysicalServer(void)
069.          {
070.             MqlTradeCheckResult  TradeCheck;
071.             MqlTradeResult       TradeResult;
072.             
073.             ZeroMemory(TradeCheck);
074.             ZeroMemory(TradeResult);
075.             if (!OrderCheck(m_Base.TradeRequest, TradeCheck))
076.             {
077.                PrintFormat("Order System - Check Error: %d", GetLastError());
078.                return 0;
079.             }
080.             m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult);
081.             if (TradeResult.retcode != TRADE_RETCODE_DONE)
082.             {
083.                PrintFormat("Order System - Send Error: %d", TradeResult.retcode);
084.                return 0;
085.             };
086.             
087.             return TradeResult.order;
088.          }
089. //+------------------------------------------------------------------+   
090.       ulong ToMarket(const ENUM_ORDER_TYPE type)
091.          {
092.             double price  = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID));
093.             double vol    = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP);
094.             uchar  nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS);
095.             
096.             ZeroMemory(m_Base.TradeRequest);
097.             m_Base.TradeRequest.magic        = m_Base.MagicNumber;
098.             m_Base.TradeRequest.symbol       = m_ChartTrade.Data.szContract;
099.             m_Base.TradeRequest.price        = NormalizeDouble(price, nDigit);
100.             m_Base.TradeRequest.action       = TRADE_ACTION_DEAL;
101.             m_Base.TradeRequest.sl           = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit);
102.             m_Base.TradeRequest.tp           = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit);
103.             m_Base.TradeRequest.volume       = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit);
104.             m_Base.TradeRequest.type         = type;
105.             m_Base.TradeRequest.type_time    = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
106.             m_Base.TradeRequest.stoplimit    = 0;
107.             m_Base.TradeRequest.expiration   = 0;
108.             m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN;
109.             m_Base.TradeRequest.deviation    = 1000;
110.             m_Base.TradeRequest.comment      = "Order Generated by Experts Advisor.";
111. 
112.             MqlTradeRequest TradeRequest[1];
113. 
114.             TradeRequest[0] = m_Base.TradeRequest;
115.             ArrayPrint(TradeRequest);
116. 
117.             return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0);
118.          };
119. //+------------------------------------------------------------------+
120.       void CloseAllsPosition(void)
121.          {
122.             for (int count = PositionsTotal() - 1; count >= 0; count--)
123.             {
124.                if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue;
125.                if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue;
126.                ClosePosition(PositionGetInteger(POSITION_TICKET));
127.             }
128.          };
129. //+------------------------------------------------------------------+   
130.    public   :
131. //+------------------------------------------------------------------+
132.       C_Orders(const ulong magic)
133.          {
134.             m_Base.MagicNumber = magic;
135.          }
136. //+------------------------------------------------------------------+   
137.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
138.          {
139.             switch (id)
140.             {
141.                case CHARTEVENT_CUSTOM + evChartTradeBuy     :
142.                case CHARTEVENT_CUSTOM + evChartTradeSell    :
143.                case CHARTEVENT_CUSTOM + evChartTradeCloseAll:
144.                   if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev)
145.                   {
146.                      case evChartTradeBuy:
147.                         ToMarket(ORDER_TYPE_BUY);
148.                         break;
149.                      case evChartTradeSell:
150.                         ToMarket(ORDER_TYPE_SELL);
151.                         break;
152.                      case evChartTradeCloseAll:
153.                         CloseAllsPosition();
154.                         break;
155.                   }
156.                   break;
157.             }
158.          }
159. //+------------------------------------------------------------------+   
160. };
161. //+------------------------------------------------------------------+

Code of the C_Orders.mqh header file

And here it is. The code shown above is capable of properly responding to all messages that the Chart Trade indicator sends to the Expert Advisor in order to communicate with the trading server. From this point on, all buttons and controls in the Chart Trade indicator become functional. You can use this code in any Expert Advisor — as long as you make a few adjustments that will be discussed later in this article. The idea here is precisely to make this possible. You will be able to use the modules that make up the Chart Trade and Mouse Indicator. By using the code shown above, you can make your Expert Advisor compatible with the already compiled modules of the Chart Trade and the Mouse Indicator. In other words: program it once, test to confirm it works, and don't touch it again. Then move on to the next stage. That's the idea.

For those who are just starting out and cannot yet fully understand the code, let's go over what's new here. Remember that the structural change I mentioned occurred on line 34, where I grouped the private variables of the class into a single structure. However, as previously stated, this does not in any way invalidate what was explained in the previous article.

The first thing you might be wondering is: How does this class know which position should be closed when we press the button to close positions on the Chart Trade indicator? The answer is: the class does not know which position should be closed. You might think that's absurd, because when you look at the buttons, the one responsible for closing positions is labeled "CLOSE ALL POSITIONS". So, logically, you'd expect all positions to be closed. Right? Wrong. That's not actually what this class is doing. At least, not without a few modifications to the code shown above.

At this point, you may be feeling confused. If pressing "CLOSE ALL POSITIONS" doesn't close all positions, why does the button say that? To understand this, we need to take a brief step back in time, before diving into how the class code actually works.

In the articles about the cross-order system:

I explained how and why, at times, we need to use a special type of asset - the so-called historical asset. There, I explained that we must ensure the Chart Trade indicator can tell the Expert Advisor which actual asset should be traded. That's because a "historical asset" is not a tradable asset; therefore, trade requests cannot be sent using it. Many of these historical assets exist due to contract structures. I also explained them in those two articles. Here, however, the problem becomes more complex. You might be using a chart of a historical asset and ask the Expert Advisor to switch from a full contract to a mini contract at any time. This will cause both the Expert Advisor and, more importantly, the Chart Trade indicator to change the asset being traded.

Now comes the real issue. Suppose you've been using a historical chart, for example, of the DOLLAR, and you've told the Expert Advisor to operate on the full dollar contract. Later, you change your mind and ask the Expert Advisor to switch to the mini contract. The Expert Advisor will send a message to the Chart Trade indicator informing it of the contract change. So far so good. But the problem begins if you had an open position on the full contract. If you then tell the Expert Advisor to switch to the mini contract, even with an open full-contract position, the Expert Advisor will simply comply. And the Chart Trade indicator will also update accordingly. Now we have a problem. And this is the issue you must understand to adapt the code to your needs.

If, at this point, the Chart Trade indicator shows that the tradable asset is the mini contract, what happens when you request to close all open positions? Remember that the open position is in the full contract. The Chart Trade indicator will indeed send a message to the Expert Advisor. This message, as explained in previous articles, contains all the necessary data for the Expert Advisor to know what to do. However, the Expert Advisor doesn't actually know what to do. This happens because it's operating on a chart that cannot be traded (a historical chart of the contract). To handle this, the message is intercepted at line 153 of the code. By that point, the message will have already been translated, successfully. Then, the code execution continues to line 120. Here the real work begins.

In line 122, we have a loop. It reads all open positions. Absolutely all of them, without exception. But why read all positions, even those unrelated to our DOLLAR example? Because we are not storing the ticket number the server provided when each position was opened. Therefore, we must search for those tickets again.

The next question is: why don't we store these tickets? It would certainly make things simpler. Indeed, it would. But storing these tickets cannot be done within the Expert Advisor itself. Any change to the chart involving the Expert Advisor would result in losing that ticket information. Thus, we are forced to retrieve them again whenever necessary. This will happen every time you change the chart timeframe or the contract type.

So, for now, we'll proceed this way. But doing it this way means we can't simply call the procedure to close positions — since we're looping through all positions, including those unrelated to the Chart Trade message.

Therefore, we need to apply filters. The first filter is at line 124, where we check the symbol name of the position and compare it with the name received from the Chart Trade indicator. Now pay attention: in our example, we have an open position in the full contract. But the message from Chart Trade indicates the mini contract. Therefore, this test will fail, because the asset names don't match. The same thing would happen in the reverse situation - if we had an open mini-contract position and tried to use Chart Trade to close full-contract positions. This test prevents such confusion.

Here's an important note. Although this test may seem to complicate things, it's absolutely necessary. Even if you're using a historical contract to trade both mini and full contracts through the cross-order system, you must not remove this test at line 124. Even if you want to close any DOLLAR-related position, this test must remain. Because without it, positions from entirely different assets could be closed when using Chart Trade, as we are scanning all open positions.

If the test at line 124 succeeds, we get a new ticket. We move to another test designed to isolate and filter something else. In the previous article, I mentioned that this class represents a unique entity, identified by a magic number. This allows a single Expert Advisor to contain multiple independent trading strategies, each separated by its magic number.

For NETTING accounts, however, this doesn't make sense, because in that type of account, the trading server averages the position price. This means there can only be one open position per asset.

In HEDGING accounts, though, things are entirely different. You can have multiple positions open at once - both buy and sell - on the same asset. In such cases, it’s essential for this class to distinguish itself from others within the same Expert Advisor. To avoid complicating things, I won't go into the details of how this is done. You just need to know that it is indeed possible and supported by this class.

Now, besides the Expert Advisor opening positions (which will be marked with a magic number indicating which class created them), there's another possibility: the trader may have opened a position manually. This is where things can get tricky, because the trader might not want their manual position to be closed when using Chart Trade. In that case, the Expert Advisor will compare the position's magic number with the class's magic number - and if they don't match, that position will be ignored and remain open. So, if the trader opened a manual trade, the Expert Advisor will leave it untouched, and it must be closed manually.

You can see how these behaviors unfold. Although they can be modified, the entire system is designed primarily for HEDGING accounts, ensuring it doesn't interfere with anything outside its scope. So, if both tests pass, line 126 executes — capturing the position's ticket and calling the procedure that closes the indicated position. This procedure is located at line 12. Note that it's inside a protected clause, so it can be accessed through inheritance but not directly from outside the inheritance system. In short: it cannot be called directly from the Expert Advisor code or any other external code.

Now, there are a few peculiar aspects of this code that I might eventually remove. Here's the situation: when the code at line 12 is called, it already knows some things, such as the asset name and the ticket number. However, in line 17, it again checks whether the ticket belongs to an open position. Why repeat this check? Because, at this point, I'm not yet certain how another tool (still to be developed) will communicate with this procedure. Still, we must use the library function PositionSelectByTicket to refresh certain data we'll need from the server. However, this update already occurred earlier when PositionGetSymbol was called at line 124. In other words, there's some duplication here. But for now, that's not a problem.

Either way, we do need to refresh the information. Once the data is updated and the ticket confirmed as a valid position, line 18 checks whether the position is a buy or sell. This is important. Then, line 19 captures the position's asset name. This, too, might change in the future once the new tool is implemented, since looking only at this class, these steps are redundant — the procedure at line 120 could have passed this information directly. Again, I don't yet know how ClosePosition will work in the future. So , we proceed to the step of filling out the structure that will be sent to the server.

Unlike the ToMarket procedure discussed in the previous article, where I said the explanation of each field would come later, here I can explain the main fields. Understanding them will be important later on. In line 21, we tell the server what kind of action to perform. Now, some might ask: why not use TRADE_ACTION_CLOSE_BY, since we're trying to close a position? That's a fair question — and the main reason lies in NETTING accounts. To understand, look at line 26, where we specify the trade volume. If we used TRADE_ACTION_CLOSE_BY, the server would simply close the position outright.

That would force us to create additional logic and code for another action that NETTING accounts already allow: partial closes. In partial closes, we only close a portion of our position. Here, I haven't yet implemented logic for partial closes or reversals. That's a design choice — depending on what should or shouldn't be implemented. Since this system can be used in either account type, we must prioritize some features over others. NETTING and HEDGING accounts each have their own capabilities — some mutually exclusive. I'm trying to accommodate both. This is why this explanation matters.

All the other fields specify details for the server: whether we're buying or selling (line 22); the price at which the trade should occur (line 23); the ticket number of the position (line 24); the symbol name (line 25) — note that it must match the name on the ticket, or an error will occur; the volume, which determines what will be done (line 26); and finally, the deviation, meaning the allowable price slippage (line 27). All this information instructs the server to execute the market request — performing the trade at the best available price, modifying volume or direction, or closing the position as required.

Notice that, unlike TRADE_ACTION_CLOSE_BY, which only closes positions, this approach allows for far greater flexibility. That's why I'm using TRADE_ACTION_DEAL. However, it only applies when a position is already open. But cannot modify stop loss or take profit levels. Those are handled through another action that will be discussed later. For now, we will continue using the system provided by MetaTrader 5. That means that, even though it's technically possible to trade using a historical contract, you won't see price lines on that chart. They'll appear on the actual traded contract. This, too, will be changed in the future.

So, the last thing left to consider is the Expert Advisor code. Here it is:

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. //+------------------------------------------------------------------+

Expert Advisor source code

This code has already been almost entirely presented in the article Market Simulation (Part 02): Cross Order (II), having undergone only a few small changes to make use of the C_Orders class. These changes are quite simple. However, if you don't have much experience coding in C, some things might seem a bit odd. But there's nothing mysterious here, just slightly different. The first change appears on line 9, where we include the header file introduced in this article. Another change is on line 15, where we declare the class using a pointer.

On line 21, we initialize the class using the NEW operator. You might find something unusual there. the class constructor is declared to expect a 64-bit long integer, yet the value being passed looks strange: 0xC0DEDAFE78514269 containing both letters and numbers. What kind of odd thing is that? Actually, it's not odd at all. It's a hexadecimal value. Notice the prefix 0x, which tells the compiler that the value that follows is hexadecimal. This is quite useful in many situations. However, when you later look at this value as the magic number of the position, you’ll see something completely different — the decimal representation of this hexadecimal value. I'll leave it to you to discover what that decimal value is, I won't reveal it here.

Finally, the last change made is on line 30. This line passes the necessary data so that the class can perform its work. It enables the Expert Advisor to handle the messages sent by the Chart Trade indicator. Notice how, with very little code, we already have something remarkable and quite interesting, since the entire system is divided into modules. This modular design means that improvements in one module do not directly affect another, allowing the code to grow safely and much more efficiently. This is in stark contrast to a monolithic system, where everything would be contained in a single, massive file, making development and maintenance much more exhausting and error-prone.


Final Thoughts

In these last two articles, I presented the Expert Advisor together with the class code responsible for sending market orders. This system can operate perfectly well on any account type — whether NETTING or HEDGING — allowing you to use the same Expert Advisor across Forex, stock, or OTC markets. This makes the whole process far more flexible and accessible.

However, this Expert Advisor is not yet complete. There is still a great deal to be added before it can fulfill its main purpose. This is to simulate the trading server for use in replay/simulation.

Before we can do that, though, we'll need to build a few other components. So, don't miss the next article — where I’ll begin explaining something that can already be done, even though this Expert Advisor isn't finished yet. I want to start discussing it now to make things simpler. Because if we leave it for later, the explanation could become much more complex. And, dear reader, that might make it difficult for you to grasp all the details involved.

FileDescription
Experts\Expert Advisor.mq5
Demonstrates the interaction between Chart Trade and the Expert Advisor (Mouse Study is required for interaction)
Indicators\Chart Trade.mq5Creates the window for configuring the order to be sent (Mouse Study is required for interaction)
Indicators\Market Replay.mq5Creates controls for interacting with the replay/simulator service (Mouse Study is required for interaction)
Indicators\Mouse Study.mq5Enables interaction between graphical controls and the user (Required for operating both the replay simulator and live market trading)
Servicios\Market Replay.mq5Creates and maintains the market replay and simulation service (Main file of the entire system)

Translated from Portuguese by MetaQuotes Ltd.
Original article: https://www.mql5.com/pt/articles/12598

Attached files |
Anexo.zip (490.53 KB)
Last comments | Go to discussion (1)
Arad alipor
Arad alipor | 4 Nov 2025 at 09:37
You explained it completely and I will use it.
Reimagining Classic Strategies (Part 17): Modelling Technical Indicators Reimagining Classic Strategies (Part 17): Modelling Technical Indicators
In this discussion, we focus on how we can break the glass ceiling imposed by classical machine learning techniques in finance. It appears that the greatest limitation to the value we can extract from statistical models does not lie in the models themselves — neither in the data nor in the complexity of the algorithms — but rather in the methodology we use to apply them. In other words, the true bottleneck may be how we employ the model, not the model’s intrinsic capability.
The MQL5 Standard Library Explorer (Part 3): Expert Standard Deviation Channel The MQL5 Standard Library Explorer (Part 3): Expert Standard Deviation Channel
In this discussion, we will develop an Expert Advisor using the CTrade and CStdDevChannel classes, while applying several filters to enhance profitability. This stage puts our previous discussion into practical application. Additionally, I’ll introduce another simple approach to help you better understand the MQL5 Standard Library and its underlying codebase. Join the discussion to explore these concepts in action.
MetaTrader 5 Machine Learning Blueprint (Part 5): Sequential Bootstrapping—Debiasing Labels, Improving Returns MetaTrader 5 Machine Learning Blueprint (Part 5): Sequential Bootstrapping—Debiasing Labels, Improving Returns
Sequential bootstrapping reshapes bootstrap sampling for financial machine learning by actively avoiding temporally overlapping labels, producing more independent training samples, sharper uncertainty estimates, and more robust trading models. This practical guide explains the intuition, shows the algorithm step‑by‑step, provides optimized code patterns for large datasets, and demonstrates measurable performance gains through simulations and real backtests.
Price Action Analysis Toolkit Development (Part 48): Multi-Timeframe Harmony Index with Weighted Bias Dashboard Price Action Analysis Toolkit Development (Part 48): Multi-Timeframe Harmony Index with Weighted Bias Dashboard
This article introduces the “Multi-Timeframe Harmony Index”—an advanced Expert Advisor for MetaTrader 5 that calculates a weighted bias from multiple timeframes, smooths the readings using EMA, and displays the results in a clean chart panel dashboard. It includes customizable alerts and automatic buy/sell signal plotting when strong bias thresholds are crossed. Suitable for traders who use multi-timeframe analysis to align entries with overall market structure.