MQL5 Wizard Techniques you should know (Part 90): Fenwick Tree Money Management with 1D CNN in MQL5
Introduction
In the last article of the MQL5 Wizard Series, we explored market entry signals by merging a bitwise vectorization algorithm with a perceptron classifier. We got some hints that the pairing has potential for further development into a usable trade system based on the test reports from some forward walks we made. Nonetheless, entry signals, though fundamental, are only half the battle. We look to a custom money management class usable within the MQL5 wizard to put together an Expert Advisor. Our approach when exploring unique money management systems is to reduce position sizing when it is pertinent. Even with precise entry signals whether navigating the turbulent swings of NVDA or very liquid forex pairs using a rigid lot size can leave some alpha on the table. Worse, the drawdown risks tend to follow suit.
A default way of adjusting position size can be to proportion it inversely to the prevalent tick volume. These indicative volume values are usually surmised from trailing averages. These averages can be over large periods, which can be taxing to compute resources and lead to some latency. Thus as a theme, we delve into another approach at building a system that fluidly responds to real-time volume pressure without suffocating execution speed while relying on the Fenwick Tree.
Problem
When an Expert Advisor uncovers a high-probability entry signal, such as with bitwise combinations in the last article, the question of how much capital to employ can be answered in a variety of ways. For most traders, the safest approach is to scale the position using cumulative volume flow (e.g., On-Balance Volume) over a dynamic window. This process, though, runs the risk of looking at just one bar's volume spike or even attempting to smooth it out with a basic moving average. This completely fails to capture the true distribution of that volume over a given lookback window. This presents some challenges in expressing the shape or topography of the momentum to the Expert Advisor. There can be a lack of structural context.
In this article, we introduce a custom money management class with four Fenwick Tree modes. These are Linear, Conservative, Aggressive, and Mean-Reversion. Despite this assortment, eventual testing in a forward walk showed that relying only on the algorithm ratios without any spatial filter was unfruitful. A large volume spike can trigger aggressive volume scaling back which might be unwarranted in cases where these happen for instance at price exhaustion, where the RSI pivots. Safely navigating real volume spikes may require a paradigm shift: treating volume not as a flat metric but as a topographic structure. We need to structurally map cumulative volume in order to better apply the aforementioned four distinct lenses of our algorithm. This should happen while we use a spatial pattern spotter to better read the terrain in a non-linear manner to better gate our capital exposure.
Algorithm: Fenwick Tree (BIT) as a Data Topographer
When reading market volume in a structural as opposed to a linear fashion, standard tools in coding can fall short. Take the basic array, it is essentially a flat, one-dimensional list. They record volumes bar by bar but this can miss a cohesive story. In order to map the actual topology of the actual momentum a Binary Indexed Tree could serve us better. This is also referred to as the Fenwick Tree. Traders developing Expert Advisors can also rely on the Fenwick Tree because of its compute speed. It has a readable hierarchical structure. It not only holds data but it natively organizes it by cumulative frequencies. By being able to isolate particular bits within an array's index, the tree is able to create unique topological containers. Once we use it, we are no longer looking at a flat list of 32 volume bars. Instead, we get a structured map for querying and comparing different time and momentum scales. The helper class for this algo is introduced as follows in MQL5:
//+------------------------------------------------------------------+ //| Fenwick Tree (Binary Indexed Tree) Helper Class | //| Efficiently calculates prefix sums for OBV/Volume in O(log n) | //+------------------------------------------------------------------+ class CFenwickTree { private: double m_tree[]; int m_size; public: CFenwickTree(int size) : m_size(size) { //--- 1-based indexing is standard for Binary Indexed Trees ArrayResize(m_tree, m_size + 1); ArrayInitialize(m_tree, 0.0); } void Reset() { ArrayInitialize(m_tree, 0.0); } void Update(int i, double delta) { for(; i <= m_size; i += (i & -i)) m_tree[i] += delta; } double Query(int i) { double sum = 0.0; for(; i > 0; i -= (i & -i)) sum += m_tree[i]; return sum; } double QueryRange(int left, int right) { if(left > right || left < 1 || right > m_size) return 0.0; return Query(right) - Query(left - 1); } };
One interesting quirk of the Fenwick Tree is that it demands 1-based indexing. The zero index needs to always be empty since performing bitwise operations on zero would make the logic recursive non-stop. The elegance of this map though, is how it is constructed. When we receive new volume data, we do not just append it to a queue rather we update the tree by leveraging the two's complement. This allows the algorithm to navigate upward via its own hierarchy while also updating parent nodes. This can be akin to pouring water onto a topographic model. The volume spreads out exactly where it should go in order to maintain the structural integrity of the cumulative map. Once a map is drawn, we explore it with the range query tool as follows:
double QueryRange(int left, int right) { if(left > right || left < 1 || right > m_size) return 0.0; return Query(right) - Query(left - 1); }
With this, we are no longer forced to write clunky array loops to accrue random chunks of volume, and we are able to ask the tree specific spatial questions. We can query the exact volume weight of the prior sixteen periods, and compare it to the prior periods of equal number. This tree returns the data as is and immediately while preserving structural context of the sequence.
Using the existing RSI and Envelopes custom signal classes, we can assemble an Expert Advisor to test this approach. We try out the Fenwick Tree in four modes. The first baseline iteration is the linear scaling mode. In this mode, the Expert Advisor pulls data from the raw On-Balance-Volume (OBV) buffer. Since the Fenwick Tree operates more effectively on positive frequencies, this algorithm does require normalization of the raw OBV array. We scan for the minimum value in our lookback window and subtract it from all elements thus converting the raw messy OBV into a refined usable format of positive volume deltas. Once the tree is populated the Expert Advisor splits the window in half to get the historical sum and recent sum.
//+------------------------------------------------------------------+ //| Iteration 1: Linear Scaling (Standard Trend Following) | //+------------------------------------------------------------------+ double CMoneyFenwickCNN::AlgorithmMode1_Linear(double current_ratio) { if(current_ratio <= m_base_volume_ratio) return 1.0; return MathMin(current_ratio / m_base_volume_ratio, 2.0); // Capped at 2.0x }
Our starting logic here is straightforward if the recent volume aggressively outweighs the historical baseline, the trade setup likely gets the institutional backing to deserve a larger lot size. We use a multiplier that scales up to a hard cap of 2.0x in order to guard against anomalies. This is our logical first step. The Fenwick Tree builds the structure of the market's volume and in our first mode we attempt to read this narrative. Algorithm's linear logic though functional in principle, can be inflexible in some instances and this may explain the discrepancy we got from our two forward walk test reports below, as we'll cover later. From the optimization run, 'AlgorithmModel Linear' was the preferred algorithm mode, however there are three other options also worth covering briefly.
Whereas mode 1, which we covered above, can be deemed 'too trusting' an alternative lens that is more skeptical of market anomalies could be better suited for certain securities in special market situations. Enter mode-2 where we use conservative scaling:
//+------------------------------------------------------------------+ //| Iteration 2: Conservative Scaling (Square Root Dampening) | //+------------------------------------------------------------------+ double CMoneyFenwickCNN::AlgorithmMode2_Conservative(double current_ratio) { if(current_ratio <= m_base_volume_ratio) return 1.0; //--- Dampens aggressive spikes to protect capital return MathMin(MathSqrt(current_ratio / m_base_volume_ratio), 1.25); // Capped tightly at 1.25x }
This maths lens assumes sudden topographical spikes are inherently traps. When we thus apply a square root damping, we deliberately round off the jagged peaks of volume data which hugely reduces our exposure, enforcing a strict 1.25x ceiling in order to aggressively protect capital. Conversely, when our Envelopes entry signals align with a well-structured breakout how do we press our advantage. For this we could look at another mode, mode-3 as implemented below:
//+------------------------------------------------------------------+ //| Iteration 3: Aggressive Scaling (Exponential Momentum) | //+------------------------------------------------------------------+ double CMoneyFenwickCNN::AlgorithmMode3_Aggressive(double current_ratio) { if(current_ratio <= m_base_volume_ratio) return 0.5; //--- Punish low volume //--- Exponential reward for structural volume breakouts return MathMin(MathPow(current_ratio / m_base_volume_ratio, 2.0), 3.0); // Capped at 3.0x }
This is the unforgiving-lens where if volume topology lacks conviction the setup is actively pushed by halving the lot size. However when the tree maps a genuine structural surge, this mode mirrors it, exponentially scaling exposure up to an aggressive 3.0x limit. So the Fenwick here is used to maximize alpha. Finally, our last mode is probably the most contrarian of them which could be summed up as void. Supposing the Fenwick Tree reveals a recent dry-up in volume versus past accumulation? What we would simply be seeking would be mean reversion. We code this as follows:
//+------------------------------------------------------------------+ //| Iteration 4: Contrarian/Mean Reversion (Inverse Scaling) | //+------------------------------------------------------------------+ double CMoneyFenwickCNN::AlgorithmMode4_MeanReversion(double current_ratio) { if(current_ratio == 0) return 1.0; //--- Scales up when recent volume is heavily exhausted compared to historical return MathMin(m_base_volume_ratio / current_ratio, 2.0); }
The math lens we use here completely inverts our traditional logic. We are actively looking for a topographical cliff or a sudden profound exhaustion of momentum. If the recent volume ratio falls while the Envelopes and RSI entry signals point to a reversal, this mode would spot a vacuum/void. This is meant to spot when retail crowd has exhausted its capital. Instead of backing down when facing low volume, this mode scales up. By inverting the ratio, we heavily fund an impending mean-reversion snapback. We safely cap exposure at a 2.0x multiplier. This is meant to be counter-intuitive where capital is deployed mostly when the market has run out of breath
The Deep Learning Layer: 1D CNN
Our Fenwick algorithm above structures the cumulative volume however as already mentioned above and even noted in prior articles, algorithms on their own can be a bit rigid. In the case of the Fenwick, it may even be argued that its edge is not its ability to give a sensible volume scale ratio per se but how it helps normalize input feed to our second filter gate, a One-Dimensional Convolutional Neural Network.
Volume accumulation on its own can be jagged sometimes deceptive and fraught with institutional traps. If we were to simply apply a linear multiplier to a ratio we would be treating all volume identically. A sustained rolling accumulation over say fifteen periods does paint a very different microstructure picture as opposed to a massive one-period volume spike even though their sums could match exactly. With this neural-gate we get into trying to understand the underlying shape of the data.
To interpret the spatial hierarchy, we use a 1-Dimensional CNN as a deep learning gatekeeper. While recurrent neural networks are usually better suited for time series data, they can also be computationally heavy. A 1-D CNN on the other hand can actually be lightweight and excel in local feature extraction. By sliding a CNN kernel across the OBV array input data, we effectively have an advanced filter that can spot specific shapes of institutional volume footprints while potentially ignoring retail noise. Thus, rather than depending on heavy external libraries, or bespoke forward-pass ONNX python exports, we implement inference engine directly within MQL5. The network class we use is designed to take in the normalized sequence produced by the Fenwick Tree. Below is our CNN class implementation:
//+------------------------------------------------------------------+ //| 1D Convolutional Neural Network (CNN) Helper Class | //+------------------------------------------------------------------+ class CCNN1DNetwork { private: int m_input_size; int m_filters; int m_kernel_size; double m_conv_weights[]; double m_dense_weights[]; public: CCNN1DNetwork(int input_size, int filters, int kernel_size) : m_input_size(input_size), m_filters(filters), m_kernel_size(kernel_size) { ArrayResize(m_conv_weights, m_filters * m_kernel_size); ArrayResize(m_dense_weights, m_filters); } void InitBaseline() { for(int i = 0; i < ArraySize(m_conv_weights); i++) m_conv_weights[i] = 0.1; for(int i = 0; i < ArraySize(m_dense_weights); i++) m_dense_weights[i] = 1.0 / m_filters; } double Calculate(const double &sequence[]) { int output_length = m_input_size - m_kernel_size + 1; if(output_length <= 0) return 1.0; double dense_input[]; ArrayResize(dense_input, m_filters); ArrayInitialize(dense_input, 0.0); //--- 1D Convolution & ReLU for(int f = 0; f < m_filters; f++) { double filter_sum = 0.0; for(int i = 0; i < output_length; i++) { double conv_val = 0.0; for(int k = 0; k < m_kernel_size; k++) conv_val += sequence[i + k] * m_conv_weights[f * m_kernel_size + k]; if(conv_val < 0.0) conv_val = 0.0; filter_sum += conv_val; } dense_input[f] = filter_sum / output_length; // Global Average Pooling } //--- Dense Layer double logit = 0.0; for(int f = 0; f < m_filters; f++) logit += dense_input[f] * m_dense_weights[f]; return 1.0 / (1.0 + MathExp(-logit)); // Sigmoid [0,1] } };
From our code above, first we define our network to have four discrete filters and kernel size of three. As the kernel slides across the input data sequence it is meant to create a feature map that highlights particular micro-patterns. Immediately after this convolution, we apply a Rectified Linear Unit activation function. This process introduces some non-linearity in the processing which zeroes out market noise for volume data points that are misaligned with target volume patterns.
Rather than flatten the output, we use Global Average Pooling. With this, the network calculates the spatial mean of every feature map. The network is then translationally invariant, meaning it 'cares' that an institutional-volume-pattern (or significant volume pattern) is in the lookback window, but it's not rigidly fixated on which price bar index it occurred on. Finally the pooled extracted features are passed through a fully connected dense layer, then through a Sigmoid activation function where the end output is bound between zero and one.
We merge the Fenwick Algorithm and the CNN Network in the Optimize function of the custom money management class as follows:
//+------------------------------------------------------------------+ //| Optimizing lot size via Fenwick Tree and optionally the 1D CNN | //+------------------------------------------------------------------+ double CMoneyFenwickCNN::Optimize(double lots) { double lot = lots; if(m_window_size > 0 && m_handle_obv != INVALID_HANDLE) { double obv_buffer[]; ArraySetAsSeries(obv_buffer, true); if(CopyBuffer(m_handle_obv, 0, 0, m_window_size, obv_buffer) == m_window_size) { m_bit.Reset(); double min_obv = obv_buffer[0]; for(int i = 0; i < m_window_size; i++) if(obv_buffer[i] < min_obv) min_obv = obv_buffer[i]; for(int i = 0; i < m_window_size; i++) { double normalized_vol = MathAbs(obv_buffer[m_window_size - 1 - i] - min_obv); m_bit.Update(i + 1, normalized_vol); } int mid_point = m_window_size / 2; double recent_vol_sum = m_bit.QueryRange(mid_point + 1, m_window_size); double historical_vol_sum = m_bit.QueryRange(1, mid_point); if(historical_vol_sum > 0) { double current_ratio = recent_vol_sum / historical_vol_sum; double scale_factor = 1.0; //--- Select the designated Algorithm Iteration switch(m_algo_mode) { case 1: scale_factor = AlgorithmMode1_Linear(current_ratio); break; case 2: scale_factor = AlgorithmMode2_Conservative(current_ratio); break; case 3: scale_factor = AlgorithmMode3_Aggressive(current_ratio); break; case 4: scale_factor = AlgorithmMode4_MeanReversion(current_ratio); break; } lot = NormalizeDouble(lot * scale_factor, 2); } //--- Gatekeeper: The Neural Network Filter if(m_use_cnn) { double seq[]; ArrayResize(seq, m_window_size); for(int i = 0; i < m_window_size; i++) { seq[i] = m_bit.QueryRange(i + 1, i + 1); } double cnn_coefficient = m_cnn.Calculate(seq); lot = NormalizeDouble(lot * cnn_coefficient, 2); } } } 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); }
From our listing above, the key point where we use the CNN is under the if clause that checks for whether we are using a CNN. If we are, the declared sequence array gets filled with data and then fed through an object instance of the CNN class using the 'Calculate' function. The network evaluates the spatial footprint of the volume and returns a probability coefficient. This value is not a static ratio but a dynamic conviction score from the network as a confidence barometer on the signals generated by the Envelopes and RSI signal classes.
For safety, if the algorithm is more capital heavy but the CNN makes an assessment of the volume sequence and identified a chaotic signature such as from an exhaustion climax, then it would output a coefficient close to zero. This would instantly throttle the position size aggressively and mitigate any potential risk. In the end therefore capital would no longer be used linearly but would be better guided by the shape of the momentum.
Tester Reports & Performance Analysis
We test in order to establish the efficacy of the Algorithm, the Neural Network as well as to establish if their combination gives us any synergies. We did perform 2 independent tests though - one with just the Fenwick algorithm and the other with both the Fenwick and the CNN. Our measuring of the synergy between the Algorithm and the Network helps set the merits of shifting from a rigid, linear approach to a non-linear machine-learning-gated architecture. We test EURUSD from 2025.01.01 to 2026.05.01. The optimization window is 2025.01.01– 2025.30.01, and the forward walk is 2026.01.01–2026.05.01. We use the same entry signals from the custom classes of Envelopes and RSI, focusing solely on the money-management element. The report for the forward walk following an optimization with only the Algorithm enabled was as follows:
For this first run, highlighted above we test the baseline where we go with just the algo. To enable this, the muse_cnn parameter was set to false. With this, the Expert Advisor used the Fenwick Tree to map the volume and it applied the linear scale based on the volume ratio. This test was meant to vindicate the Fenwick however the result was not as promising. As always though, our test symbol and window were overly restricted so this should be taken only as a demonstration on how this algorithm could be used in making trade decisions.
The forward walk test-run was disappointing with a net loss of -$773.82 and an abysmal profit factor of 0.39. If we look into the trade breakdown, the structural interpretive flaws are obvious. Out of a total of six trades, the Expert Advisor had five of them in the red implying a loss rate of 83.33%. For this postmortem, what could be the reasons why this happened? The binary indexed tree flawlessly mapped the volume ratio, and gave us accurate data. But the linear logic was not on point.
The scaling up of lots was too aggressive, even in instances where recent volume outweighed historical volume meaning it was blinding itself to context. The algorithm was excitedly interpreting every sudden spike in OBV as genuine trend initiation. However as the charts indicate, these spikes were frequently RSI exhaustion climaxes which were simply the final volatility spikes of dying trends. By blindly scaling into these deceptive traps, the Expert Advisor suffered a lot of drawdown as well.
With these results, it is worth noting that the entry signals may have been missed, making the forward-walk report loss-making regardless. To test whether the neural network can tame this behavior, we re-optimized the signal and money-management thresholds for 2025 and ran a forward walk over the first four months of 2026. Thus, the trading environment, timeframe (4- hours), and baseline used indicators remained the same. The opening and closing thresholds though were adjusted to 'work better' with the algorithm and neural network combination. Every 32-period sequence of spatially formatted volume data queried from the Fenwick Tree was now passed directly through the forward inference engine of the CNN.
The transformation in the Expert Advisor's performance was drastic. We end up with a net profit of about $115.96 with perhaps the most striking change being the placing of just one trade down from six. This can be taken as our 1D CNN acting as a ruthless institutional gatekeeper. When presented with the exact same six signals from the indicators of the Envelopes and RSI, the CNN looked at the spatial sequence structured by the Fenwick. From its analysis, it localized the footprint of the volume accompanying five of these entry setups and correctly (arguably) identified them as deceptive exhaustion signatures as opposed to valid structural breakouts. With this verdict, it outputs probability coefficients that are near zero, effectively throttling the lot size to be below the minimum required to place a trade, thus sidestepping the five losses that were logged in the first test run.
The lone trade our CNN sanctioned through Gate 2 gave us a 100% win rate. Despite this we also get a 15.65% equity drawdown which means our entry was still fraught with risk considering that this $1,500 drawdown is significantly more than the $100 amount we net in the end. However, given the hand we were dealt where five out of six trades were losers, it can be argued that the CNN was resourceful in recognizing that the underlying volume structure was valid and this allowed us to survive the drawdown onslaught.
If we juxtapose the two forward walks we can draw interesting conclusions on risk-management. Alpha can be preserved from omission, and not just by execution. The Fenwick Tree can build the market narrative and help turn chaotic volume buffers into proper feature maps. However it is the CNN that seems to be essential in safely interpreting the narrative so as to protect our capital.
Takeaways
Possessing a high-probability signal generator like with the bitwise perceptron that we had in the last article can be insufficient on its own. Alpha generation seems to require a more holistic approach of pairing of an algorithm with a neural network. These though could be the main points worth summarizing from this article:
- Topological Data Structuring: The Fenwick Tree is much more than a speed optimization tool. It is a resourceful tool in organizing cumulative data, hierarchically. It allowed us to map the topography of volume without using complicated spatial queries or messy array manipulations.
- Feature Engineering for ML: A neural network is only as good as its inputs. We can make the argument that our 1D CNN would be heavily handicapped if it were fed raw unformatted buffers. Our Fenwick Tree natively normalizes and prepares the sequential data thus converting 'chaotic' volume series into actionable feature maps. This introduces a synergy between the Algorithm and the Network.
- Risk Mitigation from Non-Linearity: Our test reports have shown that non-linear position sizing can be a fundamentally protective strategy. By using a CNN to read the volume shape, the Expert Advisor was not only in a position to ramp up position size, more importantly it eliminated capital deployment in cases of exhaustion traps that the linear multiplier was falling blind into. We preserved margin from sitting out noise.
Conclusion
This article has been a take on how else we can confront the dangerous limitations of static position sizing as well as the deceptive nature of one-dimensional volume tracking. Presented were methods on interpreting genuine volume momentum where we used the Fenwick Tree not just for speed but also topographical data structuring as well as feature preparation. More importantly if we pass this data sequence through a 1D CNN, we transform it into a usable probability coefficient for accurately adjusting our position size. This union allows us to safely deploy capital and give this custom money management class potential use beyond this tested symbol and time window.
| name | description |
|---|---|
| wz_90_1.mq5 | wizard assembled Expert Advisor |
| MoneyFenwick1.mqh | Custom Money Management Class used in MQL5 Wizard |
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.
MetaTrader 5 Machine Learning Blueprint (Part 16): Nested CV for Unbiased Evaluation
Eagle Strategy (ES)
Features of Experts Advisors
The Power of MetaTrader 5: From Step-by-Step Debugging to EX5 Protection in a Unified Environment
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use

