MQL5 Wizard Techniques you should know (Part 93): Using Suffix Automation and an Auto Encoder in a Custom Money Management Class
Introduction
The problem with traditional money management is not just that it counts lots, but that it reacts too late—it relies on losses that have already occurred. For geometric or pattern traders, this lagging approach is especially dangerous. In this article, we build `CMoneySuffixAE`, a custom risk model that differs fundamentally from standard built-in classes. Instead of looking at the history of closed losses, it proactively sizes positions based on current market structure and real-time geometric integrity.
We resume this series on the MQL5 Wizard where we last considered an implementation of a custom entry signal based on the Fenwick Tree and a CNN. In principle, the MQL5 Wizard supports three customization modes: Trailing Stop, Entry Signal, and Money Management. This series shows how to customize all three modes for different trading styles.
Our Last Outing: volume-based money managementIn this article, we return to a custom Money Management implementation. The last time we implemented such a custom class, we used the Fenwick Tree (Binary Indexed Tree) to monitor Volume as measured by the On-Balance-Volume indicator and paired this algorithm with a 1D Convolutional Neural network (CNN) for feature smoothing. Position-sizing strategies are not one-size-fits-all; they vary by trading philosophy and market regime. In the last article on custom money management, we had an implementation that was tailor-made for momentum traders who like exploiting trend-heavy situations. This was because we were proactively sizing trades based on continuous accumulation volume. However, what if some traders' strategies/outlook do not pay attention to volume? For many forex traders this is actually not an anomaly.
Case for Geometric Risk ModelThere are traders whose focus is on algorithmic patterns or navigating highly structured geometric markets. And what are geometric markets? Well, these could be understood as markets at fractal points or highly recognizable, and historically repeating patterns. Examples of these often include: harmonic patterns (think Gartley, Bat, Butterfly); Elliot Wave Sequences; Typical Chart-Patterns (e.g. Head & Shoulders, Triangles, Wedges etc.). These are readable sequential markets that could be range-bound or trending.
For a geometric trader, therefore, trade volume is secondary versus historical price action. Arguably, these traders tend to proactively scale down position sizes based on repeated recent price patterns. This would be done while concurrently shielding capital from sudden unprecedented structural anomalies. Purely reactive, performance based risk models often are poorly equipped to handle these fractal-type of situations.
Our Problem: Structural BreaksTo construct a robust Expert Advisor, one needs to curate the math tools of his respective market environment. A momentum trader depends on volume accumulation as we saw in the last implementation of a custom money management class with the Fenwick Tree. However, for a geometric trader - who hunts for precise price action patterns or fractals - volume would be on the back-burner. Their strategy's lifeblood is historical repetition.
Nonetheless, geometric trading does carry a special fatal vulnerability: The 'structural break'. This often happens when there is a macroeconomic shock that seriously severs the market's expected geometric rhythm. As a result, previously reliable patterns can fail spectacularly. If these traders were using built-in classes such as the 'CMoneySizeOptimized' they would be unprepared to handle these anomalies.
If we examine the code of this built-in class we can see it depends on a lagging loss-counter. Its policy is to reduce position size after a string of trades are closed as losses. If a trader relies on geometric patterns, then such a whipsaw environment where major news is breaking would leave him with a strategy that is reactive to loss-counters. If the market breaks structure, waiting to absorb three losses before reducing risk can cripple a portfolio.
Model idea: two engines instead of one loss counterWe therefore set out to build a specialized, proactive engine. This necessitates two components: a sequencing algorithm that is able to map accurate price patterns in real-time so as to confidently scale up on volume once setups are validated; and secondly an anomaly detector that aggressively slashes risk as soon as the market's geometry becomes unreadable - thus preserving capital way before the first loss is realized.
Here's a short summary: the Suffix Automaton looks for pattern familiarity, while the Autoencoder checks the integrity of the structure.
Model Description
To clarify the potential of this geometry-based model, we step away from MQL5 syntax and focus on the underlying algorithmic and network concepts. This comparison highlights how the two pairs fit different trading environments. Replacing the Fenwick Tree with a Suffix Automaton is like swapping an accountant for a geneticist. Replacing the 1D CNN with an autoencoder is like replacing a camera filter with a compression artist.
Fenwick Tree vs Suffix Automaton
In the last custom money management article, the Fenwick Tree served as a meticulous accountant. An accountant keeps tabs on cumulative totals - that in our case were the continuous running sum of bullish or bearish volume getting into the market. It was able to excel at addressing the question, 'How much total pressure is driving this move?' Because of this, it was the suitable tool for momentum traders riding heaving trends, but an Accountant on the other hand would be blind to the particular shape of the market.
The geometric trader though, unlike momentum followers, does not dwell on total volume but cares about precise historical repetition. Therefore, if we replace the Accountant with a Geneticist, he would ignore the volume and instead translate erratic chart raw prices into a clean discrete 'Price DNA' alphabet. To illustrate simply, a higher close is marked 'Up' (U); while a lower close would be 'Down' (D) and no change could be 'Flat' (F).
When our Expert Advisor proposes a trade, it hands the Geneticist the recent, say, 16-bar DNA sequence. Given that the Suffix Automaton has already mapped the whole market history into a highly tuned index, the Geneticist would not need to go through the history book page by page. The read off is immediate in the form: 'This precise geometric pattern has a 90% historical match rate.' This approach allows the pattern trader to scale up or down their lot size based on exact sequence familiarity as opposed to cumulative momentum.
1D CNN vs Autoencoder
Also, in the last article with the custom money management class our neural network behaved like a specialized Camera Filter. This was the 1D CNN. We were sliding a math window across continuous data, to smooth out erratic noise in order to highlight the actual directional trajectory of a trend. It brought clarification as a supervised tool. That worked then; however, for traders hunting geometric patterns, confirming a trend is not very helpful if the market's fundamental structure is actively collapsing. This in geometric markets stems from macroeconomic shocks as mentioned above, and therefore a structural shield would be better suited here, so we replace the camera filter with a compression artist.
To see how this works, imagine an artist who is meant to look at the recent geometric chart, compress it into a tiny restricted memory format, and then try to redraw it. If the current market's fractal points are following a preset rhythm, this artist would be able to easily capture this essence based on the past repetition principle. However, in situations where this rhythm breaks and becomes chaotic or unreadable, the artist's attempt to compress and redraw this resulting chaos is more likely to result in an unrecognizable mess. In this article, we want to use this 'messiness' (the math reconstruction error) as the final gatekeeper. It will be an anomaly detector, proactively reducing our risk as timely as possible, whenever geometric markets break structure. The goal here is to do this before there is a need to even use a stop-loss or alternative preset exit mechanisms.
Math Definition
Now we formalize the two checks to translate our above ideas into a workable MQL5 custom class. For a trader that hunts geometric patterns, our math objective can actually be two-fold. First we need to map exact discrete sequences in real-time, and secondly we need to measure the continuous geometric integrity of the market. This approach then is clearly different from say the reactive loss-counting approach with the built-in class.
The Suffix Automaton
In computational graph theory, a Suffix Automaton (SA) is formally defined as the least deterministic automaton that is finite while still recognizing all the possible suffixes of a given string. If we let S represent our historical 'Price DNA', a string of length n constructed solely from the categorical alphabet sigma = {U,D,F} or (Up, Down, Flat), its automaton is a Directed Acyclic Graph (DAG) where every node stands for a state, and every directed edge represents a deterministic transition triggered by a character from this preset alphabet.
A state in the SA will correspond to an equivalent class of substrings that share matching ending positions in the historical dataset (aka the 'endpos' equivalence class). In addition, every state has a 'suffix-link' - a structural pointer to another state that represents the longest suffix of the substrings that should be in different 'endpos' class. Why would this complicated discrete mathematics be important to a retail geometric trader? Because searching very deep historical arrays for an exact sequence using regular nested for loops can take up O(n^2) time complexity. In the MQL5 Expert Advisor environment that processes data multiple times within a given time interval O(n^2) scanning can easily freeze an execution thread or throttle quota VPS resources.
However, if we use Blumer's algorithm, the Suffix Automaton constructs its whole historical index online in a strictly linear time O(n) that has its limits defined by a maximum of 2n - 1 states and 3n - 4 transitions. If we then query the SA with a recent price sequence T, traversing the DAG assures us of an instant query time of O(|T|). We would be isolating the longest matching sequence from our price history immediately, and thus attaining a near-perfect categorical pattern match without exerting pressure on server resources.
The Autoencoder
While the Automaton manages discrete sequence pairing, the Autoencoder is charged with continuous anomaly detection in geometric patterns. Essentially an Autoencoder is an unsupervised feedforward neural network that is designed to learn an identity function - it tries to output the exact same data it receives, however it is forced to do so via a bottleneck. Supposing x is an input vector, that is normalized as a sequential array of recent raw prices. The neural network would be divided into two symmetrical parts. An encoder pass and a decoder pass. The encoder would compress raw market patterns into a hidden bottleneck layer, h, where its size is less than that of the input layer. This mandatory dimension reduction prevents the network from merely memorizing the input. It makes the network learn the principal geometric structures of the market data. The formal definition of an encoder is as follows:

Where:
- W(e) is the encoder (k x m) matrix
- b(e) is the bias vector
- sigma(z) is the non linear sigmoid activation function.
Following this, the decoder pass would try to rebuild the original market patterns from the highly compressed h layer. It's defined as follows:

Where:
- x is the reconstructed output vector
- W(d) is the decoder (m × k) matrix
- b(d) is the decoder bias
In order to proactively spot structural breaks in the market environment, we work out the 'reconstruction-loss' by using the Mean Squared Error between the real market input and the network's redraw by using the following formula:


MQL5 Implementantion
With our theory and math-engines laid out above, we can progress to the MQL5 coding. In order to have a seamless integration with MQL5 Wizard framework, as always, our custom class inherits from an appropriate standard library base class. In this case since we are building a money management class 'CMoneySuffixAE' and therefore we will inherit from 'CExpertMoney' base class. With that said, this is where most similarity our class will have to other built-in money management classes ends. Looking at the 'Optimize' function for instance we can spot how our new class evolves from the 'reactive-rearview' approach in favor of a strictly 'proactive-geometric-posture'.
Mini-map: What Optimize() does step by step
Before diving into the code, here is a quick overview of what the `Optimize()` function executes:
- Look Outward: Abandons historical loss tracking and instead pulls the latest raw price data.
- Build the Genetic Index: Dynamically constructs the Suffix Automaton from historical data.
- Sequence the DNA: Extracts the recent discrete patterns (for the Automaton) and continuous raw prices (for the Autoencoder).
- Query the Automaton: Evaluates the familiarity of the recent sequence and calculates a lot-size scaling factor based on the selected algorithm mode.
- Pass the Gatekeeper: Calculates the reconstruction error using the Autoencoder to definitively slash risk if the market structure is unreadable.
Evolving from the Rearview Mirror
Let us briefly look at the core logic behind the built-in 'CMoneySizeOptimized' class. Within its 'Optimize()' we run the following routine:
if(m_decrease_factor > 0) { //--- select history for access HistorySelect(0, TimeCurrent()); int orders = HistoryDealsTotal(); int losses = 0; CDealInfo deal; for(int i = orders - 1; i >= 0; i--) { deal.Ticket(HistoryDealGetTicket(i)); if(deal.Ticket() == 0) { Print("CMoneySizeOptimized::Optimize: HistoryDealGetTicket failed, no trade history"); break; } //--- check symbol if(deal.Symbol() != m_symbol.Name()) continue; //--- check profit double profit = deal.Profit(); if(profit > 0.0) break; if(profit < 0.0) losses++; } if(losses > 1) lot = NormalizeDouble(lot - lot * losses / m_decrease_factor, 2); }
What we have here is the anatomy of a lagging indicator. We are explicitly requesting the account's historical ledger ('HistorySelect()'), iterating across closed order tickets, counting consecutive negative yields, in order to decide whether to reduce the input trade volume. This can demand that the geometric trader absorbs losses prior to this function being useful. Now, if we contrast this with the function we have below for our new class labelled 'CMoneySuffixAE::Optimize()' we can notice some changes:
class CMoneySuffixAE : public CExpertMoney { protected: CSuffixAutomaton m_sa; CAutoEncoder m_ae; int m_history_length; int m_dna_window; int m_algo_mode; bool m_use_ae; public: CMoneySuffixAE(void); ~CMoneySuffixAE(void); virtual double CheckOpenLong(double price,double sl); virtual double CheckOpenShort(double price,double sl); protected: double Optimize(double lots); };
The class uses two engines. The first engine is the Suffix Automaton. It works on a discrete representation of price movement. A higher close becomes U , a lower close becomes D , and an unchanged close becomes F . In the implementation these are encoded as integers.
int char_code = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); // 0=U, 1=D, 2=F
The second engine is the Autoencoder. It works on normalized raw prices and acts as a structural filter. If the recent price shape is still readable, the Autoencoder should reconstruct it with low error. If the market becomes chaotic, the reconstruction error rises and the lot size is reduced.
The rest of the article follows the implementation of Optimize() step by step. The explanatory text is therefore attached to the code path itself rather than presented as a large theoretical introduction.
Step 1: Pull Market Structure Instead of Trade History
The built-in class begins with HistorySelect() . Our class begins with CopyClose() .
double CMoneySuffixAE::Optimize(double lots) { double lot = lots; if(m_history_length > 0) { double closes[]; ArraySetAsSeries(closes, true); if(CopyClose(m_symbol.Name(), PERIOD_CURRENT, 0, m_history_length + m_dna_window, closes) == m_history_length + m_dna_window) { // further processing continues here } } return lot; }
This change is the main philosophical pivot of the class. The lot size is no longer adjusted according to the account’s last few closed deals. Instead, the class reads the current symbol’s price structure directly. This makes the money-management model outward-looking rather than inward-looking.
For momentum systems, a previous implementation used a Fenwick Tree with On-Balance Volume and a 1D CNN. That made sense because the central question was about cumulative volume pressure. Here the environment is different. A geometric trader does not primarily ask how much volume is pushing the market. The trader asks whether the current sequence of price action is still structurally familiar.
Step 2: Build the Historical Price DNA
Once the closing prices are available, the class resets the automaton and begins converting historical price movement into a discrete alphabet.
m_sa.Reset(); for(int i = m_dna_window; i < m_history_length + m_dna_window - 1; i++) { double diff = closes[i] - closes[i + 1]; int char_code = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); // 0=U, 1=D, 2=F m_sa.Extend(char_code); }
This is where the Suffix Automaton replaces the Fenwick Tree. The Fenwick Tree was useful when the task was to keep efficient cumulative totals. It behaved like an accountant: it could quickly tell us how much volume pressure had accumulated.
The Suffix Automaton is different. It behaves more like a genetic index. It does not care about totals. It cares about sequences. A simplified Price DNA string might look like this:
U D U U F D D U F U
For a geometric trader, this is closer to the actual trading question. The system wants to know whether the current sequence of price movement has appeared before, and how deeply it matches the historical structure. A naive historical search could become expensive if implemented through nested loops over large arrays. The Suffix Automaton avoids this by building a compact structure that can be queried efficiently.
Step 3: Extract the Current Setup
The automaton is built from historical data. Now the class needs the current pattern immediately preceding the trade signal. That is done by filling two arrays at the same time.
int recent_dna[]; double recent_raw[]; ArrayResize(recent_dna, m_dna_window); ArrayResize(recent_raw, m_dna_window); double max_price = closes[0]; double min_price = closes[0]; for(int i = 0; i < m_dna_window; i++) { double diff = closes[m_dna_window - 1 - i] - closes[m_dna_window - i]; recent_dna[i] = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); recent_raw[i] = closes[m_dna_window - 1 - i]; if(recent_raw[i] > max_price) max_price = recent_raw[i]; if(recent_raw[i] < min_price) min_price = recent_raw[i]; }
recent_dna is the discrete version of the setup. It is passed to the Suffix Automaton and answers the question: Does the current directional sequence have a historical twin?
recent_raw preserves the actual price shape. It is later passed to the Autoencoder and answers a different question: Even if the sequence looks familiar, is the current market structure still clean enough to trust?
This separation is important. A discrete sequence can look familiar while the underlying price behavior is already unstable. For example, a pattern may still produce the same U-D-U-D signature during a macroeconomic shock, but the shape, range, and volatility of that movement may no longer be normal. The Suffix Automaton sees sequence familiarity. The Autoencoder sees structural integrity.
Step 4: Query the Suffix Automaton
After preparing the recent DNA sequence, the class asks the automaton for the longest historical match.
int longest_match = m_sa.GetLongestMatch(recent_dna); double dna_score = (double)longest_match / (double)m_dna_window; double scale_factor = 1.0; switch(m_algo_mode) { case 1: scale_factor = AlgorithmMode1_Linear(dna_score); break; case 2: scale_factor = AlgorithmMode2_Conservative(dna_score); break; case 3: scale_factor = AlgorithmMode3_Aggressive(dna_score); break; case 4: scale_factor = AlgorithmMode4_MeanReversion(dna_score); break; } lot = NormalizeDouble(lot * scale_factor, 2);
The result is normalized into dna_score , a value between 0.0 and 1.0 . A value near zero means the current sequence is mostly unfamiliar. A value near one means the recent setup has a strong historical match. This score becomes the first position-sizing signal.
Different geometric traders may interpret pattern familiarity differently. A breakout trader may want to increase size when a known pattern is strongly confirmed. A contrarian trader may prefer to do the opposite when the pattern looks too obvious or exhausted. For that reason, the class exposes several algorithm modes.
Step 5: Translate Pattern Familiarity into Risk
The algorithm modes are only money-management curves. They do not generate entries by themselves. The entry signal still comes from the Expert Advisor assembled by the MQL5 Wizard. The modes simply convert dna_score into a multiplier.
double CMoneySuffixAE::AlgorithmMode1_Linear(double score) { return 0.8 + 1.2 * score; } double CMoneySuffixAE::AlgorithmMode2_Conservative(double score) { return 0.5 + MathSqrt(score) * 0.75; } double CMoneySuffixAE::AlgorithmMode3_Aggressive(double score) { return 0.5 + 2.5 * score * score; } double CMoneySuffixAE::AlgorithmMode4_MeanReversion(double score) { if(score <= 0.0) return 1.0; return MathMin(3.0, 0.5 / score); }
The first mode is linear. It is the most direct interpretation of the DNA score: unfamiliar structures reduce position size, while familiar structures increase it.
The second mode is conservative. It is more suitable for traders who want pattern confirmation but do not want aggressive lot expansion. The square-root curve responds early to moderate confidence while keeping total scaling restrained.
The third mode is aggressive. It heavily rewards high-confidence matches and is designed for situations where the trader only wants to scale meaningfully when the pattern match is strong.
The fourth mode uses inverse logic. It is intended for mean-reversion or contrarian interpretations. Instead of rewarding perfect familiarity, it assumes that extremely obvious structure may be exhausted.
Step 6: Add the Autoencoder Gatekeeper
The Suffix Automaton gives us a pattern-familiarity score, but it has one limitation: it sees discrete direction, not continuous distortion. That is where the Autoencoder is added.
if(m_use_ae) { double norm_range = (max_price - min_price == 0) ? 1.0 : (max_price - min_price); for(int i = 0; i < m_dna_window; i++) recent_raw[i] = (recent_raw[i] - min_price) / norm_range; double ae_coefficient = m_ae.Calculate(recent_raw); lot = NormalizeDouble(lot * ae_coefficient, 2); }
The class first normalizes the recent raw price window into the [0, 1] range. This gives the neural network a stable input scale. Then the normalized price structure is passed into the Autoencoder.
The Autoencoder compresses the input into a smaller hidden representation and tries to reconstruct the original input from that compressed form. If the recent market structure resembles the type of structure the network can represent, reconstruction error should be low. If the market has become chaotic, the reconstruction error should rise.
h = σ(W e x + b e )
x′ = σ(W d h + b d )
MSE = (1 / n) × Σ(x i − x′ i )2
C = 1 / (1 + MSE)
When reconstruction error is small, C remains close to 1.0 , so the Suffix Automaton’s lot decision is mostly preserved. When reconstruction error rises, C moves toward zero, and the position size is reduced.
This gives the class a second layer of defense. The automaton asks whether the sequence is familiar. The Autoencoder asks whether the current structure is still clean enough to trade with normal risk.
Step 7: Validate Broker Limits
After both engines have adjusted the lot size, the class still needs to respect the broker’s volume constraints.
double stepvol = m_symbol.LotsStep(); lot = stepvol * NormalizeDouble(lot / stepvol, 0); double minvol = m_symbol.LotsMin(); if(lot < minvol) lot = minvol; double maxvol = m_symbol.LotsMax(); if(lot > maxvol) lot = maxvol; return lot;
This final block is not part of the model’s intelligence, but it is essential for execution. No matter how the Suffix Automaton or Autoencoder scales the position, the resulting lot size must be aligned with the symbol’s volume step and remain within the broker’s minimum and maximum limits.
At this point the full Optimize() workflow can be summarized as follows:
1. Read recent and historical Close prices. 2. Convert historical movement into Price DNA. 3. Build the Suffix Automaton. 4. Convert the current setup into recent DNA. 5. Query the automaton for the longest historical match. 6. Convert the match score into a lot multiplier. 7. Optionally pass the raw price structure through the Autoencoder. 8. Reduce lot size if reconstruction error is high. 9. Normalize the result to broker constraints.
The Full Optimize Logic
The core routine can therefore be read as a single pipeline. The order matters: first the Suffix Automaton proposes a multiplier based on historical sequence familiarity, and only then the Autoencoder is allowed to suppress that multiplier if the current structure is distorted.
double CMoneySuffixAE::Optimize(double lots) { double lot = lots; if(m_history_length > 0) { double closes[]; ArraySetAsSeries(closes, true); //--- Pull enough bars for both the historical index and the current DNA window if(CopyClose(m_symbol.Name(), PERIOD_CURRENT, 0, m_history_length + m_dna_window, closes) == m_history_length + m_dna_window) { m_sa.Reset(); //--- 1. Build the Suffix Automaton from historical Price DNA for(int i = m_dna_window; i < m_history_length + m_dna_window - 1; i++) { double diff = closes[i] - closes[i + 1]; int char_code = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); // 0=U, 1=D, 2=F m_sa.Extend(char_code); } //--- 2. Extract the current setup in both discrete and raw form int recent_dna[]; double recent_raw[]; ArrayResize(recent_dna, m_dna_window); ArrayResize(recent_raw, m_dna_window); double max_price = closes[0]; double min_price = closes[0]; for(int i = 0; i < m_dna_window; i++) { double diff = closes[m_dna_window - 1 - i] - closes[m_dna_window - i]; recent_dna[i] = (diff > 0) ? 0 : ((diff < 0) ? 1 : 2); recent_raw[i] = closes[m_dna_window - 1 - i]; if(recent_raw[i] > max_price) max_price = recent_raw[i]; if(recent_raw[i] < min_price) min_price = recent_raw[i]; } //--- 3. Query the automaton and convert the match into a score int longest_match = m_sa.GetLongestMatch(recent_dna); double dna_score = (double)longest_match / (double)m_dna_window; //--- 4. Convert the DNA score into a lot multiplier double scale_factor = 1.0; switch(m_algo_mode) { case 1: scale_factor = AlgorithmMode1_Linear(dna_score); break; case 2: scale_factor = AlgorithmMode2_Conservative(dna_score); break; case 3: scale_factor = AlgorithmMode3_Aggressive(dna_score); break; case 4: scale_factor = AlgorithmMode4_MeanReversion(dna_score); break; } lot = NormalizeDouble(lot * scale_factor, 2); //--- 5. Use the Autoencoder as a structural gatekeeper if(m_use_ae) { double norm_range = (max_price - min_price == 0) ? 1.0 : (max_price - min_price); for(int i = 0; i < m_dna_window; i++) recent_raw[i] = (recent_raw[i] - min_price) / norm_range; double ae_coefficient = m_ae.Calculate(recent_raw); lot = NormalizeDouble(lot * ae_coefficient, 2); } } } //--- 6. Validate broker limits double stepvol = m_symbol.LotsStep(); lot = stepvol * NormalizeDouble(lot / stepvol, 0); double minvol = m_symbol.LotsMin(); if(lot < minvol) lot = minvol; double maxvol = m_symbol.LotsMax(); if(lot > maxvol) lot = maxvol; return lot; }
This order is important because a familiar sequence is not always a safe sequence. A pattern can still look familiar in discrete form while the continuous market structure is already breaking.
Post-Optimization Analysis
To see whether each part of the model contributes something useful, the class was tested in two configurations on GBPUSD using the H4 timeframe. The optimization period was 2025, and the forward walk covered 2026.01.01 to 2026.05.01.
The first run used only the Suffix Automaton. The automaton was kept in Mode 1, while the Autoencoder filter was disabled.
m_algo_mode = 1 m_use_ae = false
This test checks whether sequence familiarity alone can provide a usable money-management signal. The forward walk produced the following report.

The result shows that the automaton can produce a profitable baseline. That matters because the lot adjustment was not based on recent account losses. It was based on whether the current Price DNA had historical similarity. However, the equity curve remained volatile, with drawdown approaching 11%.
That weakness is consistent with the limitation discussed earlier. A Suffix Automaton can recognize sequence familiarity, but it does not necessarily recognize structural chaos. It can tell us that a recent sequence resembles history, but it cannot fully judge whether the current market environment has been distorted by a sudden shock.
The second run added the Autoencoder.
m_algo_mode = 1 m_use_ae = true
Now the position size was first adjusted by the Suffix Automaton and then filtered by the Autoencoder’s reconstruction coefficient.

This forward result was more stable, with the equity drawdown almost halved compared with the automaton-only run. This does not prove that the model is universally robust. The test was performed on one symbol and one limited time window. Additional tests across symbols, timeframes, spreads, and market regimes are required before any adoption should be considered.
Still, the comparison is useful. It suggests that the two engines are not redundant. The Suffix Automaton contributes pattern familiarity. The Autoencoder contributes structural defense.
Practical Takeaways
The main lesson from this implementation is that money management does not have to be based only on the account’s trade history. The built-in CMoneySizeOptimized class reduces volume after a sequence of losses. That approach is simple and practical, but it is reactive. The market must first inflict damage before the class responds.
The CMoneySuffixAE class tries to respond earlier. It uses current market structure rather than closed trade history. For momentum-oriented systems, volume-based methods such as a Fenwick Tree applied to OBV can make sense. For geometric or pattern-based systems, sequence familiarity may be more relevant. That is where the Suffix Automaton becomes useful.
However, sequence familiarity alone is not enough. A market can still produce recognizable directional fragments while its broader structure is breaking. The Autoencoder is added to catch that kind of distortion by measuring reconstruction error.
The resulting model is therefore conditional. If the current sequence is familiar and the structure is clean, the class can preserve or increase exposure. If the sequence is unfamiliar or the structure becomes unreadable, the class can reduce exposure before the loss counter would normally react.
Conclusion
This article built a custom MQL5 Wizard money-management class named CMoneySuffixAE . Instead of reducing position size after a series of losing trades, the class reads the current market structure directly. It converts historical price action into discrete Price DNA, uses a Suffix Automaton to measure how familiar the current setup is, and then applies an Autoencoder as a structural gatekeeper.
The result is a proactive money-management model designed for geometric traders — traders whose systems depend on recognizable price structures rather than cumulative volume pressure. The tests suggest that the Suffix Automaton can provide a useful sequence-based sizing signal, while the Autoencoder can help reduce drawdown by suppressing exposure during structurally unstable periods.
The broader idea is simple: for pattern-based systems, risk should not wait for the account history to confirm that something has gone wrong. Sometimes the market structure gives the warning first.
| name | description |
|---|---|
| wz_93.mqh | Wizard Assembled Expert Advisor (links shared for new readers) |
| MoneySizeOptimized.mqh | Built-In sampled Custom Money Managment Class |
| MoneySuffixAE.mqh | Article Coded Custom Money Management class |
| r1.set | Input settings for Expert Advisor in run 1 |
| r2.set | Input settings for Expert Advisor in run 2 |
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.
Developing a Multi-Currency Expert Advisor (Part 28): Adding a Position Closing Manager
Price Action Analysis Toolkit Development (Part 71): Weekend Gap Structure Mapping in MQL5
Features of Experts Advisors
Building an Object-Oriented Z-Score Statistical Arbitrage Engine in MQL5
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use