From "Best Pass" to Robust Solutions: Exploring the Optimization Surface in MetaTrader 5
Introduction
Optimization of trading strategies has long been a standard part of developing algorithmic trading systems. MetaTrader 5 provides powerful tools for this: a multi-threaded tester, distributed computing, and user-defined criteria. Optimization results can be visualized as one-, two-, and three-dimensional dependencies of performance metrics on the EA parameters. At first glance, this is more than enough: optimization is launched, the results are sorted by Profit Factor or another summary criterion, after which the best pass is selected. However, this is where one of the most common illusions of algorithmic trading begins.
The problem is that optimization almost always focuses on finding the maximum result from an individual pass. The tester shows the peak of the parameter surface - the highest Profit Factor, the best Sharpe Ratio or maximum profit. But the market rarely rewards systems built on single extremes. On the contrary, such peaks often turn out to be the result of a random coincidence of parameters with the local structure of historical data. It only takes a slight change in the indicator period, stop loss value, or market mode, and an impressive result begins to rapidly crumble.
That is why high Profit Factor itself does not guarantee anything. Two strategies may demonstrate the same returns, but differ fundamentally in terms of robustness. The first one maintains acceptable results across the entire range of adjacent parameters. The second exists only in the form of a narrow needle on the optimization surface, surrounded by chaotic and unprofitable configurations. Formally, both systems are profitable. In practice, only one of them has a chance of surviving in the real market.
MetaTrader 5 built-in visualization tools clearly show the dependence of the final metrics on individual optimization parameters. The tester shows the metric values and the distribution of results. However, it offers little help in assessing adjacent configurations: degradation rate, the presence of stable plateaus, sensitivity to parameters, and overoptimization limits. In other words, the function's maximum is no longer as important as the geometry of the entire optimization surface.
This is where optimization frames acquire special value. This mechanism allows storing and analyzing arbitrary user data after each optimization pass. Instead of working with several built-in indicators, the developer has the ability to create their own research telemetry: transmit additional metrics, evaluate the stability of adjacent configurations, analyze local parameter areas, and build criteria focused not on extreme profitability, but on the stability of the system behavior. At this point, optimization ceases to be a search for a pretty number and turns into a full-fledged research.
In this article, we will look at the practical application of optimization frames in MetaTrader 5, learn how to save our own metrics and analyze the results, as well as build custom stability criteria based on the entire optimization surface. The main focus will be on identifying stable areas of parameters capable of maintaining predictable behavior even after changing market conditions.

Optimization frames as a research telemetry system
Despite all the capabilities of the built-in tester, standard optimization in MetaTrader 5 by its nature remains a dry procedure. The agent performs calculations. The tester records the result. In the end, the user sees a ready-made set of figures: Profit Factor, Sharpe Ratio, drawdown, profit and other summary metrics. This is sufficient for the initial selection. But for a serious analysis, this is insufficient. The top is visible, but the terrain itself remains largely invisible.
This is where Optimization Frames come into play. This is not just another way to save the result of a pass. This is a full-fledged channel for transmitting research data between test agents and the analyzing code. Through frames, we can convey everything that helps us understand the strategy behavior more deeply: additional statistics, intermediate parameters, model state, equity curve features, stability assessment and any other custom metrics.
The operation of this mechanism appears quite natural. Upon completion of each pass, the expert transmits the data via FrameAdd() from OnTester(). This triggers the TesterPass event in the terminal, which in turn calls OnTesterPass(). Inside this handler, you can immediately parse the incoming frame: sequentially read the data via FrameFirst() and FrameNext(), and if necessary, filter the required entries. In practice, this gives the feeling of a live stream of results: the pass is completed, the frame has arrived, the data can now be analyzed.
But this flow has an important feature. Frames may arrive not one at a time, but in batches, and sometimes with a noticeable delay. This is especially noticeable in distributed optimization, when some of the calculations are performed by remote agents. That is why not every TesterPass manages to be processed exactly at the moment of the next pass completion. It is for this reason that OnTesterDeinit() remains the last reliable stage of working with the results. If some frames arrived later, they can be read after the optimization is complete, going through the entire data array again from the beginning via FrameFirst() and, if necessary, sorted or filtered by FrameFilter().
As a result, optimization is no longer just a series of isolated passes. It is transformed into a system for accumulating research telemetry across the entire surface of parameters. This fundamentally changes the very logic of work. Now the developer sees not only the final value of the criterion, but also the context it was obtained in. Together with Profit Factor, we can store additional metrics: profit concentration, transaction uniformity, stability across time segments and equity characteristics. Separately, it is possible to save the degradation indicators on adjacent parameters and sensitivity to the market regime.
Thus, each pass begins to look like a full-fledged diagnostic batch. And the longer or more complex the optimization, the higher the value of this approach. In fact, the real value of optimization frames is in post-analysis of the entire optimization surface. Instead of analyzing individual points, it becomes possible to study the parameter space as a whole: to search for stable areas, measure the rate of degradation of results, evaluate the sensitivity of a strategy, and construct one’s own stability criteria based on neighboring configurations.
This is why optimization in this approach becomes a study of a multidimensional surface, where not only the absolute values of the metrics are important, but also the shape of their distribution, the density of stable zones, and the behavior of neighboring parameters. Here the winner is no longer the one with the highest isolated peak, but the one with a more robust and reliable surface.
Simple strategy as an object of research
After getting acquainted with optimization frames, a completely natural question arises: What exactly should we use as the subject of this analysis? Intuitively, we want to take a complex trading system — a multi-layer model, a set of filters, a cascade of indicators, adaptive position management. But the complex strategy is starting to steal the show. Instead of exploring optimization, the article gradually turns into a discussion of the trading system itself. And this is a completely different story .
In our work, we need the opposite approach. The strategy should be as simple as possible, almost trivial. It should be so transparent that the reader can easily understand the source of each signal and each change in results. The purity of the research experiment is more important here than the magic of profitability.
Therefore, as an object of research we use an extremely simple model built around two well-known ideas: the intersection of moving averages and the RSI mode. The first part of the system is responsible for the moment of entry. The second is for filtering the market state. In this case, the signals themselves are generated not by direct polling of indicators from the EA, but via an event model. Indicators generate signals independently and transmit them via user events.
This approach solves several problems at once. The strategy remains extremely compact and readable. The signal logic turns out to be well decomposed: the indicators are only responsible for generating events, and the EA is only responsible for processing states and managing positions. And it is the event-driven architecture that fits particularly well with the research nature of the article, as it allows for the easy accumulation of diagnostic telemetry about the system behavior.
At the heart of logic lies a simple idea. EMA intersection is used as a potential entry trigger.
//--- chek signal if(fast_buff[1] > slow_buff[1] && fast_buff[0] <= slow_buff[0]) SendSignalEvent(EVENT_EMA_CROSS, DIR_BUY, 0.0, "EMA CROSS"); if(fast_buff[1] < slow_buff[1] && fast_buff[0] >= slow_buff[0]) SendSignalEvent(EVENT_EMA_CROSS, DIR_SELL, 0.0, "EMA CROSS");
However, the crossover signal itself does not open a position. First, the system checks the current RSI mode.
At the same time, RSI generates an event only at the moment of mode change - when the specified levels are crossed.
//--- chek signal ENUM_SIGNAL_DIRECTION new_regime = current_regime; if(rsi_buff[0] <= UpperLevel && rsi_buff[1] > UpperLevel) new_regime = DIR_BUY; if(rsi_buff[0] >= LowerLevel && rsi_buff[1] < LowerLevel) new_regime = DIR_SELL; if(rsi_buff[1] >= LowerLevel && rsi_buff[1] <= UpperLevel) new_regime = DIR_NONE; //--- send signal if(new_regime != current_regime) { current_regime = new_regime; SendSignalEvent(EVENT_RSI_REGIME, current_regime, rsi_buff[1], "RSI REGIME"); }
Thanks to this, it is possible to significantly reduce noise. The EA stores the current RSI state in the form of an internal flag and uses it as a market context. If the signals match, the event is considered synchronized, and the EA opens a trade.
void OnChartEvent(const int32_t id, const long &lparam, const double &dparam, const string &sparam) { //--- if(id-CHARTEVENT_CUSTOM == EVENT_RSI_REGIME) { rsi_regime = (ENUM_SIGNAL_DIRECTION)lparam; rsi_regime_changes++; CloseConflictingPosition(); } if(id-CHARTEVENT_CUSTOM == EVENT_EMA_CROSS) { ema_events++; ProcessEntry((int)lparam); } }
This is an important point for all further optimization. We deliberately build a system around the consistency of independent sources of information.
Risk management is also deliberately kept simple. Stop Loss and Take Profit are calculated through ATR multipliers when opening a position. ATR value is used only as an estimate of current volatility and is not involved in the event model. This allows us to keep the architecture as clean as possible: events are responsible for direction, while ATR defines the risk scale.
void ProcessEntry(const int signal) { if(PositionSelect(_Symbol)) return; if(signal == DIR_BUY && rsi_regime == DIR_BUY) { if(!atr_buff.CopyIndicatorBuffer(atr_handle, 0, 1, 2)) return; double atr = atr_buff[0]; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); synchronized_entries++; double sl = NormalizeDouble(bid - atr * iSLmult, _Digits); double tp = NormalizeDouble(bid + atr * iTPmult, _Digits); trade.Buy(iLots, _Symbol, 0, sl, tp); } if(signal == DIR_SELL && rsi_regime == DIR_SELL) { if(!atr_buff.CopyIndicatorBuffer(atr_handle, 0, 1, 2)) return; double atr = atr_buff[0]; double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); synchronized_entries++; double sl = NormalizeDouble(ask + atr * iSLmult, _Digits); double tp = NormalizeDouble(ask - atr * iTPmult, _Digits); trade.Sell(iLots, _Symbol, 0, sl, tp); } }
From a practical point of view, such a strategy is interesting precisely because of its predictability. There is no complex logic, hidden dependencies or heavy filtering. Any change in optimization results is almost directly related to a change in the model parameters. This means that the optimization surface becomes much more readable and suitable for exploratory analysis.
Custom metrics and higher-level analysis
The MetaTrader 5 tester can calculate many built-in indicators: profit, Profit Factor, Recovery Factor, Sharpe Ratio, drawdown, expected payoff and other pass characteristics. This is quite sufficient for evaluating a single configuration. However, such indicators describe only one point in the parameter space. They answer the question of how successful a particular pass was. But almost nothing is said about how the strategy behaves near this point.
This is where the fun begins. MetaTrader 5 offers several optimization options and sorts the results by the best value of the selected criterion. The results table also displays other performance indicators, so the picture appears quite complete. However, visualization is built only for the selected criterion and only as its dependence on the optimized parameters. That is, the developer sees the surface of one metric, but does not see how stable it is in the vicinity of the found area. But this is a fundamentally different question.

Two configurations may look very similar in the final report, but behave completely differently. One gives high Profit Factor, however, it only holds on to a narrow peak, which crumbles at the slightest shift in parameters. The other one shows a more modest result, but maintains similar values over a wider range. A standard tester will almost always select the first configuration as the best. But from a research point of view, the second one often turns out to be more valuable because its behavior appears stable.
Therefore, we begin to collect not only built-in statistics, but also our own diagnostic metrics already at the level of a single pass. FrameAdd allows us to transmit any additional telemetry that helps us understand the internal behavior. In our case, the number of EMA crossing events, the number of RSI mode changes and synchronized entries, as well as signal conversion rate, profit per signal and user-defined stability criterion are saved together with the basic data. This is an attempt to see how exactly the system works internally.
double OnTester() { //--- double profit_per_signal = 0.0; double trigger_conversion_ratio = 0.0; //--- double profit = TesterStatistics(STAT_PROFIT); double pf = TesterStatistics(STAT_PROFIT_FACTOR); if(ema_events > 0) { trigger_conversion_ratio = (double)synchronized_entries / (double)ema_events; } if(synchronized_entries > 0) { profit_per_signal = profit / synchronized_entries; } double ret = CalculateCustomCriterion(pf, trigger_conversion_ratio, profit_per_signal ); //--- double telemetry[12]; telemetry[0] = profit; telemetry[1] = pf; telemetry[2] = ema_events; telemetry[3] = rsi_regime_changes; telemetry[4] = synchronized_entries; telemetry[5] = trigger_conversion_ratio; telemetry[6] = profit_per_signal; telemetry[7] = ret; telemetry[8] = iFastEMA; telemetry[9] = iSlowEMA; telemetry[10] = iSLmult; telemetry[11] = iTPmult; //--- FrameAdd("Telemetry", 0, ret, telemetry); //--- return(ret); }
The trigger_conversion_ratio conversion rate of signals is especially useful. It shows what proportion of EMA triggers actually leads to a trade. If the value is high, then the base signal and the mode filter behave in a coordinated manner. If it is low, the system is either too selective, or too noisy, or the parameter logic itself is not working properly. In any case, this metric does not speak about profit per se, but about the quality of interaction between the model components.
Another metric is profit per signal (profit_per_signal). It is similar to average profit per trade, but they are different values. In our model, a trade is opened only after the EMA and RSI signals are agreed upon, but the open position is not closed automatically upon reverse crossing EMA. Moreover, while the position remains active, subsequent signals can simply be ignored. Therefore, the number of synchronized signals and the number of actually executed trading cycles do not always coincide exactly. In this sense, profit per signal reflects how effectively the system monetizes available entry points, taking into account missed signals.
Here the transition to the next level appears. When optimization frames begin to accumulate telemetry across the entire surface of parameters, it becomes possible to analyze not just individual passes, but their mutual behavior. And this is much more important than just a list of the best results. We are interested not only in where the maximum is, but also in how neighboring configurations behave, how quickly the result degrades with a small change in parameters, and whether there is a stable plateau around the point found.
That is why the data exported through frames is saved to a CSV file.
void OnTesterDeinit() { ArrayResize(frames, 0, 100); FrameFirst(); int pos = 0; ulong pass = 0; string name = ""; long id = 0; double value = 0; double data[]; SFrameData item; //--- while(FrameNext(pass, name, id, value, data)) { if(ArraySize(data) < 12) continue; item.pass = id; item.profit = data[0]; item.pf = data[1]; item.ema_events = data[2]; item.regime_changes = data[3]; item.synchronized_entries = data[4]; item.conversion_ratio = data[5]; item.profit_per_signal = data[6]; item.custom_criterion = data[7]; item.fast_ema = data[8]; item.slow_ema = data[9]; item.sl_mult = data[10]; item.tp_mult = data[11]; pos = ArraySize(frames); if(ArrayResize(frames, pos + 1, (pos + 49) / 50) < pos + 1) return; frames[pos] = item; } ExportCSV(); //--- }
This format is convenient because it can be further analyzed using a wide range of tools available to the reader: from spreadsheets to specialized analytical packages. In our work, we used Python for this purpose. MetaTrader 5 provides a necessary integration with it. This allows us to construct a parameter surface study: smooth values, evaluate local stability, look at the distribution of results, and compare adjacent areas.
For testing and subsequent analysis, we took historical data on EURUSD on timeframe H1 for the period from 1.01.2020 to 1.01.2026. This range is long enough to allow for a variety of market patterns to emerge: calm periods, impulse movements, protracted trends, and periods of increased noise. This is important not only to obtain a plausible result, but also to ensure that the optimization surface does not appear artificially smooth.

At the first stage, the EA is optimized according to four parameters: fast EMA, slow EMA, stop loss multiplier and take profit multiplier. These four parameters define the basic shape of the strategy and determine its reaction to the market. To expand the active range of event generation and prevent overly narrow filtering, RSI boundaries were shifted to 40 and 60. In this configuration, the mode filter becomes more sensitive and the event model becomes less constrained. This allows the EA to enter the market states being explored more frequently and, therefore, to form a more expressive optimization surface.
Once the optimization is complete, the results are saved to a CSV file and go to Python processing. Here, for each pair of parameters, a two-dimensional surface is constructed through pivot_table, after which Gaussian smoothing is applied to it. This results in a calmer, smoother criteria surface — the research version — where random peaks become noticeably less pronounced.

In the same function, local stability is calculated as the ratio of the mean value in the neighborhood to its standard deviation.

A gradient map is then constructed to show where the surface changes abruptly and where it remains relatively flat.

Such graphs allow us to see whether there is a broad plateau in the parameter space or just a narrow, fragile peak.
A separate analysis layer provides a graph of coordinates for all parameters at once. Before its construction, the parameters are normalized and only the best passes are highlighted by the selected criterion on Parallel Coordinates. This is convenient when you need to see not only a single successful point, but also its position among other configurations. The script prints 5% of the best results according to the criterion, so that the visual picture is immediately accompanied by numerical support.

As a result, Python works as the next level of analysis. MetaTrader 5 gives the optimization surface, while Python removes noise from it, shows local stability, compares neighboring areas and helps to distinguish a truly stable section of parameters from a random beautiful peak. Optimization ceases to be a matter of sorting individual passes by a single number. It turns into a study of a multidimensional surface, where not only the value of the criterion is important, but also how the entire surrounding area behaves. This is what gives us the opportunity to look at strategy more deeply as a system whose stability is determined by the shape of the optimization surface.
From searching for a maximum to searching for a stable plateau
The main goal of exploratory optimization is not to maximize the result, but to find such ranges of parameters, in which the strategy remains stable even with small changes in the configuration.
This difference is fundamental. A single high peak on the optimization surface almost always looks tempting. But in practice, such extremes often turn out to be the result of market noise, the characteristics of a particular historical period, or simply the model overfitting. They look convincing in the tester, however on live market this may not be the case.
That is why after uploading the results to Python, we look not at individual passes, but at the entire surface of parameters as a whole.
It is first useful to compare the raw and smoothed surfaces of the optimization criterion. The initial surface looks motley: there are many local bursts, sharp maxima and single peaks. This is a typical picture of noise optimization, when random coincidences are easily disguised as successful solutions. After smoothing, most of these outliers disappear, and more stable structures, rather than random ones, come to the fore.
This is where it becomes clear that not every beautiful peak is trustworthy. If after smoothing the surface disintegrates and loses shape, then the extremes found were more of an accident than a pattern. In our case the picture is different: two distinct ridges remain around SlowEMA in the ranges of 120–150 and 220–240. This is already a serious sign that we are not dealing with a one-time surge, but with a statistically stable pattern. Moreover, the optimum here does not look like a sharp needle, but like a wide comb. And this is actually a good sign. Robust strategies are rarely born in one place. More often than not, they remain on a plateau, where adjacent values give similar results.
After this, it makes sense to look at the gradient heatmap. This graph shows how quickly the objective function changes when the EMA parameters are shifted. Here areas of increased sensitivity become noticeable, primarily with SlowEMA in the ranges of 70–90 and 200–220. In these ranges, even a small change in settings can dramatically change the result. And this is already a direct hint at an increased risk of over-optimization.
The gradient itself has a pronounced vertical character. This means that SlowEMA affects the result significantly more than FastEMA. The logic here is quite natural: the slow average sets the market regime, while the fast one is more responsible for the entry point and local adjustment of the system behavior. On the contrary, the areas around SlowEMA in the ranges of 160–190 and 30–50 show a low gradient. But it is important not to overestimate this fact: Raw Surface and Smoothed Surface clearly show that these are rather areas of low or near-zero profitability, rather than truly productive areas. The smoothness of the surface does not make the strategy valuable.
The Parallel Coordinates graph completes the picture built for the best 5% configurations according to user criteria. And here the main thing is clear: sustainable solutions do not converge on one magical point. They are distributed among several working combinations. This is a very important signal. A narrow single maximum often looks impressive, but is usually associated with over-optimization. On the contrary, distributed clusters indicate that the system can operate in several modes.
It is especially noticeable that the best configurations tend to have higher FastEMA values. This makes sense: smoother values reduce noise and make signals cleaner. At the same time, SlowEMA forms several clusters, which may indicate different market modes of strategy operation. SLMult and TPMult parameters are distributed more evenly. This suggests that position management has a weaker impact on the final result than the structure of EMA signals itself. In other words, the core of the system is in the entry logic, and trade management plays a secondary role.
The next step is logically taken not across the entire initial grid of parameters, but across the area that has shown signs of stable operation. After the first stage we narrow the FastEMA, SlowEMA, SLMult and TPMult to those areas where the surface looks like a wide plateau. Then two more parameters are added to the model - the lower and upper RSI boundaries, which at this stage we optimize as part of the overall entry structure. This allows us to avoid focusing on the entire parametric array at once, but to move from the found stable configuration to its finer tuning.
This is where forward test comes in handy. In MetaTrader 5, during forward optimization, the period specified for the study is automatically divided into two parts: the first one is used for optimization, and the second one for forward testing. Moreover, not all runs are launched in the forward period, but only the best ones: with a full enumeration of parameters, 10% of the best are selected, and with a genetic algorithm - 25%. The optimization and forward results can then be compared on separate tester tabs. This is convenient for checking the fit. This can also be used to check whether the selected cluster of parameters remains stable outside the training history section.
The first stage has already shown that strategy does not have to live on one sharp peak. The task now is to check whether the detected parameter zone can withstand the transition to a more stringent test mode. A forward test helps to separate a stable region of parameters from one that looks good only in the past data period.

At the second stage, the task changes. Now we are interested in the ability of the found parameter region to remain stable after transferring to new data. This is why forward testing becomes a logical continuation of the analysis of a stable plateau. We are talking about checking the viability of the found configuration on an independent section of data. This is especially important for our EMA model with the RSI mode filter. Such systems often demonstrate very attractive results within the optimization period, but noticeably lose quality after going beyond the training set. It is the forward test that allows us to separate truly stable decisions from parameters that coincide by chance with the characteristics of a specific market regime.
The forward optimization table immediately shows that the best values of the user criterion in the forward phase do not have to coincide with the best values in the optimization phase.

In our case, at the top of the table we see passes with a very high optimization criterion, but a closer look reveals that some of these decisions are based on an extremely small number of trades.
The imbalance of RSI levels at the top of the table is particularly indicative. The lower limit is fixed at around 40-45, while the upper limit goes into the range of 75-90. This means that the model actually begins to take into account the global predominantly ascending market regime and noticeably shifts towards trend one-sidedness. This design can show very strong results in a section where the market really supports the directional movement, but when the phase changes, it easily loses a significant part of its effectiveness. In other words, the risk of overfitting is already visible here: the strategy is too well adapted to the specific character of the historical period.
Against this background, 244 pass line looks much more interesting. It does not give an extreme value of the criterion, but it appears noticeably more balanced and therefore probably more robust. The following parameters were used for this configuration:
- iFastEMA = 25,
- iSlowEMA = 135,
- iUpperLevel = 60,
- iLowerLevel = 40,
- iTPmult = 5,
- iSLmult = 4.
This combination no longer seeks to squeeze the maximum out of one favorable market segment, but rather forms a more even and restrained structure of entries. As a result, the strategy shows the profit of USD 134.16, Profit Factor of 4.08, Drawdown — 7.05% and 5 trades. For this period, this looks like a completely workable and at the same time cautious profile.
The balance graph and equity for this set of parameters confirm the same picture.

Single pass graph for two periods - optimization and forward testing.
During the optimization phase, which spanned 72 months, the strategy completed 259 trades and showed a profit of 141.16% at the maximum balance drawdown of 63.57% and Profit Factor 1.25.
During the 3-month forward test, 5 trades were made: the profit comprised 13.42%, drawdown — 3.91%, while Profit Factor — 4.08.
Starting with the deposit of USD 1000, the strategy initially shows growth to a level of about USD 1700, after which it goes through a significant correction, falling to a local minimum of approximately USD 620 in September 2022. This is followed by a long recovery and a sustained upward section ending at a level of about USD 2400 by the end of the optimization period. In the forward segment, a tendency towards an increase in balance is also noticeable, which further supports the conclusion about the practical viability of the chosen configuration.
Thus, forward optimization allowed us to filter out overly aggressive and statistically fragile solutions, including configurations with PF above 8, which, upon closer inspection, turn out to be tailored to the testing period. Against this background, the 244 pass looks like the most balanced option: it combines acceptable profitability, moderate drawdown, and sufficient stability on an independent data section. It is precisely these solutions that are of greatest interest in exploratory optimization — not because they produce the most striking peak, but because they better withstand the test of time and changing market conditions.
Conclusion
In this article, we have deliberately shifted the focus from the search for maximum profit to the study of the sustainability of a trading strategy. This approach changes the very logic of optimization. The focus is not on a single successful run, but on the entire surface of parameters: where the strategy holds firm, where it begins to lose shape, and where it turns into a thin and fragile peak. This is why optimization frames are especially useful. They allow us to look at the structure of the results, their behavior in neighboring configurations, and how the quality of the model changes with a small shift in parameters. In this sense, frames become a tool for engineering diagnostics.
Hence the main conclusion of the work:
Strong optimization is not about maximizing profit, but about maximizing understanding of what exactly was optimized.
And it is for this reason that for this article, not the most profitable strategy was chosen, but an extremely simple and transparent one. Here this means a methodological advantage. We needed a clean testbed, which can demonstrate the process itself: from basic optimization and selection of a stable plateau to forward testing and analysis of results outside the training area. This choice allows us to demonstrate the main thing without unnecessary hassle — MetaTrader 5 optimization features are revealed especially clearly when the developer begins to look for a stable area that can survive a change in the market regime.
Programs used in the article
| # | Name | Type | Description |
|---|---|---|---|
| 1 | Expert.mq5 | Expert Advisor | The EA for the strategy optimized in the article |
| 2 | CrossEMA.mq5 | Indicator | Indicator generating events when crossing EMA |
| 3 | RSI.mq5 | Indicator | RSI with event generation when crossing levels |
| 4 | postanalysis.py | Script | Post-analysis script in Python |
| 5 | defines.mqh | Code Base | Library of macro substitutions and common methods |
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/22578
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
Features of Custom Indicators Creation
MetaTrader 5: Build a Market to Suit Your Strategy — Renko/Range/Volume, Synthetics, and Stress Tests on Custom Symbols
Features of Experts Advisors
MQL5 Trading Tools (Part 32): Crosshair, Magnifier, and Measure Mode
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use