
In our previous discussion on feedback controllers, we learned that these systems can stabilize the performance of trading strategies by first observing their behavior in action. We have provided a quick link to the previous discussion, here. This application design, allowed us to capture the dominant correlational structures that persisted across both winning and losing trades. In essence, feedback controllers helped our trading application learn how to behave optimally under current market conditions — much like human traders, who focus less on predicting the future and more on responding intelligently to the present.
The reader should note that, up to this point, our focus has been on feedback controllers that correct simple, rule-based strategies. This simple approach allowed the reader to immediately observe the impact the feedback controller made, even if it may have been the reader’s first encouter with the subject matter. In Figure 1 below, we have built a schematic diagram of the application configuration to help the reader visualize the changes we are making today.
Figure 1: Visualizing the design pattern we initially selected for our feedback controller
In this discussion, we push beyond that boundary and ask a deeper question: Can we learn to optimally control a trading strategy that is itself defined by a statistical model of the market? This represents a shift in how we apply machine learning in algorithmic trading. Instead of using models merely for prediction, we explore how statistical models can supervise or correct one another — a potentialy new class of tasks for machine learning systems.
Figure 2: We will substitute the fixed trading strategy with a statistical model estimated from the market data
Our objective is to determine whether starting with a more sophisticated, data-driven trading strategy provides richer structure for the feedback controller to learn from, and ultimately, better results. To investigate this, we revisited our earlier work on feedback control and linear system identification, where we built a simple moving-average strategy and fitted a feedback controller to establish a baseline. We then replaced the moving-average component with a supervised statistical model of the EUR/USD market and evaluated performance under identical testing conditions. The findings were:
Together, these results demonstrate that coupling a feedback controller with a well suited statistical model can lead to material improvements in both efficiency and stability. The synergy between closed-loop control and supervised learning enables a form of intelligent adaptation. Such a system may bring to mind reinforcement learning algorithms, but taken from a supervised perspective.
When all is done, this article, will outline the design choices that shaped this improved system and provide readers with a structured approach for enhancing the performance of their own MetaTrader 5 applications using feedback control principles to support their statistical models.
The first step in our Python-based analysis of MetaTrader 5 market data is to import the necessary libraries.
Once our dependencies are loaded, we proceed to initialize the MetaTrader 5 terminal.
At this stage, we select the market symbol we’re interested in analyzing.
If you’ve followed along this far, you’re now ready to fetch historical market data directly from your MetaTrader 5 terminal. Be sure to convert timestamps from seconds to a human-readable format, as MetaTrader 5 returns time data in seconds by default
Figure 3: The market data we fetched from the MetaTrader 5 terminal
The terminal provides a detailed dataset containing multiple market attributes. However, for this discussion, we will focus only on the four key price levels — Open, High, Low, and Close. Therefore, we drop all other columns from the dataset.
Figure 4: We will focus on the four fundamanetal price levels for this exercise
Next, we remove any observations that overlap with the backtest period we plan to use. In our previous discussion on linear system identification, we ran a backtest from January 1, 2023 up to October 2025 (the current period at the time of writing). To maintain consistency, we’ll preserve the same backtest window here. It’s good practice to eliminate any data that might leak information from the test period into the training set
Figure 5: It is good practice to drop off all observations that overlap with the back test we intend to perform
With our dataset cleaned, we now define the forecast horizon — how far into the future our model will attempt to predict — and label the dataset accordingly with the target values.
Finally, we drop all missing rows to ensure data integrity. Once the dataset is complete and properly formatted, we’re ready to load the machine learning libraries and begin model training.
Then we drop any rows that have missing data.
Let us now get ready to start fitting our machine learning models. Since we are not aware which model will work best, we import a variety of models to get started.
Creates new instances of each model.
Partition or data into equal halves. One for training and the latter testing.
Now that our dataset is ready, we can define the inputs and targets for our machine learning model.
We begin by creating a dedicated function that returns a fresh model instance each time it’s called.
As we learned in our earlier discussion, not all historical data is always useful for present forecasting. Readers who have not read our prior discussion on market memory, can find a helpful link provided, here. To determine how much history we actually need, we once again perform cross-validation, this time testing how effectively the first half of our training data can predict the second half. Our findings reveal that only about 60% of the first half is required to accurately predict the remaining half. This means we can safely reduce our training set to focus only on the most coherent partition — the portion of data that appears internally consistent.
Figure 6: As we learned in our previous discussion on effective memory cross validation, not all the historical data on hand is helpful
Identify the coherent index.
Reshape the training data and remove older, less relevant observations.
Figure 7: We have trimmed our data set down, to only keep observations we believe are best aligned with the present
Earlier in our design process, we defined a list of candidate model types. We will now iterate through each one and assess their performance on the test set. Note that while we evaluate the models on the test data, we never fit them on this test set, since it is reserved for our final backtest.
Next, we visualize each model’s performance using a bar plot. As shown, the Ridge Regression model performs best, though the Deep Neural Network (DNN) closely follows. This suggests the DNN may benefit from parameter tuning.
Figure 8: We have identified a good benchmark model to outperform
To find optimal parameters for the DNN, we employ time-series cross-validation using scikit-learn.
We define the number of splits and the temporal gap between each fold, then specify a parameter grid covering all values to explore. We then define a base neural network configuration with fixed parameters to ensure reproducibility. For example, we disable shuffle=True (since time-series data must preserve order) and fix the random state to 0 so that weight initialization remains consistent across runs. We also disable early stopping and set the maximum iterations to 1000.
After running the grid search, the model returns the best-performing parameter combination, which we compare against earlier results. Interestingly, our optimized DNN — visible on the far right of the performance chart — still does not outperform the Ridge Regression benchmark.
Figure 9: We have successfully outperformed the control level we identified earlier
With the optimization complete, we now export the final model to the Open Neural Network Exchange (ONNX) format. ONNX provides a framework-independent interface that allows trained models to be shared and deployed across multiple programming environments without carrying over their original training dependencies.
To begin the export, we define the model, import the necessary ONNX libraries.
Specify the input shape (1×4, corresponding to the four major price levels) and output shape (1×1, representing the predicted value).
We then generate the ONNX prototype, an intermediary representation of the model.
Finally save it to disk as an ONNX buffer file which we will subsequently import into our MetaTrader 5 application.
Once these constants are defined, we load the ONNX model created earlier.
Our application also imports several supporting libraries to simplify common trading operations, such as opening, closing, and modifying positions.
Next, we define global variables to maintain shared state across functions — ensuring the same key values are accessible wherever needed.
During initialization, we instantiate the ONNX model from the exported buffer and perform an integrity check to confirm that it hasn’t been corrupted. If successful, we define the model’s input and output shapes, which must match those defined in Python. We then load our technical indicators and initialize the global variables with default values.
When the program ends, all allocated resources are freed to ensure efficient memory usage.
Whenever new price data arrives, the system checks for the formation of a new candle. If a new candle has formed, it updates both the candle count and the total number of “scenes” (episodes) observed by the feedback controller. Once the controller has gathered the required number of observations, it is activated — from that point, its predictions are consulted before any new trades are placed.
If no open positions exist, the application updates its indicators and requests a forecast — either from the feedback controller (if active) or from the ONNX model. The model receives the four major price levels as input and outputs a forecast value. The system then takes a snapshot of key variables such as the price levels, account balance, equity, and indicator readings.
Trading signals are generated only when no positions are open. If the ONNX model expects prices to rise, a buy signal is registered — but only if the price is already above its moving average. Conversely, a sell signal is registered only if the closing price is below the moving average and the model expects depreciation.
The feedback controller’s prediction method begins by copying all previously recorded observations and appending the current one. It then constructs two shifted partitions: one representing the present inputs and another representing the next-step targets (future observations). The target variable in this setup is the future account balance.
Using Singular Value Decomposition (SVD), the controller factorizes the observation matrix into three unitary matrices. Since two of these are orthogonal, their inverses can be obtained simply by taking their transposes — leaving only the diagonal S matrix to invert. This approach greatly reduces computational load.
Once the optimal coefficients are derived, the controller multiplies them by the current input vector to obtain a predicted future balance. If the predicted balance exceeds the current one, trading permission is granted; otherwise, it is withheld. In rare cases where coefficient estimation fails — typically due to a singular diagonal matrix (S containing zeros) — the controller aborts the prediction process.
The system continuously records snapshots of its state throughout the process of its trading sessions. This method ofrecordings are how we build applications that can learn from their experience of the market.
When shutting down, it clears all previously defined constants and variables.
Figure 10: Selecting our Expert Advisor for its 2 year back test
Ensuring that the evaluation period poses realistic trading challenges is key to emulating real market. This includes enabling random delays in MetaTrader 5’s Strategy Tester to simulate live trading uncertainty.
Figure 11: Select back test conditions that emulate real market conditions
The performance metrics of our enhanced application speak for themselves. The total net profit increased by more than twofold, and trading accuracy rose to 72%, approaching the 80% range. Key performance ratios — including expected payoff, Sharpe ratio, and recovery factor — all improved over the baseline model.
Figure 12: Detailed statistics on the performance of our trading application over the 2 year test period
The equity curve of this revised system exhibits a smoother, more consistent upward trend across the same backtest period. Because this backtest window was not part of the training data, we can be confident that the improvements are genuine and not the result of information leakage.
Figure 13: The equity curve produced by our trading application has a strong upward which is what we desire to observe
Finally, one particularly impressive outcome was the feedback controller’s predictive precision. At the end of the backtest, it forecasted a final balance of $270.28, while the actual result was within 10 cents of that estimate. As we’ve discussed in previous articles, this minor discrepancy is likely due to the inherent difference between the mathematical manifold of model predictions and that of real-world outcomes — meaning perfect alignment is theoretically impossible. Nevertheless, the proximity of this result confirms that our feedback control framework delivers meaningful forecasts.
Figure 14: The feedback controller also appears to have reasonable expectations of how the strategy affects the balance of the account
After reading this article, the reader learns a new framework for building self adapting trading applications, that control their behaviour depending on the outcomes of their actions. Linear feedback control algorithms allow us to effeciently identify unwanted behaviour even in a complex non-linear system. Their utility for algorithmic trading cannot be exhausted. These algorithms appear well suited to enhance our classical models of the market. Additionally, this article has taught the reader how to build an ensemble of intelligent systems that coorperate to build trading applications that seek to learn good behaviour in the market. It appears that time series forecasting on its own, is only a component of a bigger solution.

