Position Management: Scaling Into Winners With A Falling-Risk Pyramid
Table of Contents
- Introduction
- Architecture Overview
- The Five Integration Points
- Engine Additions
- The Bridge Class
- Wiring a Complete Expert Advisor
- Conclusion
- Attached Files
Introduction
Part 13 of this series translated the four AFML bet-sizing methods into MQL5. BetSizeProbability produces a concurrency-corrected, discretized position signal in [−1, 1]. BetSizeDynamic maps forecast-price divergence to a bet size through a calibrated sigmoid or power curve. BetSizeBudget normalizes the long-short imbalance of active directional signals into a capacity fraction. BetSizeReserve reads the empirical distribution of concurrent imbalance via the EF3M mixture CDF. Each method returns a BetSizeResult struct, and the EA in Part 13 maps the signed bet_size field to a single position whose lot size is InpMaxLots × |bet_size|.
That mapping has one structural gap: it connects bet magnitude to a single-layer position. A bet_size of 0.90 and a bet_size of 0.55 open trades of different sizes, but both are executed as a single market order at a fixed stop. The sizing information shapes how much capital is committed; it does not shape how the trade is built, how risk evolves as the position moves in favor, or when to add to a winning trade. Those questions belong to position management, not to sizing.
An article by Tola Moses Hector, Position Management: Safe Pyramiding with a Unified Stop in MQL5, presents CPyramidEngine. It is a self-contained MQL5 class that layers a trade through strictly decreasing lot sizes and uses a single unified stop that advances after each addition. The engine is mathematically constructed so that total account risk decreases with every add-on. It is designed to plug into any Expert Advisor with six changes to existing code.
The two systems are complementary. The bet-sizing module answers how much to risk. The pyramid engine answers how to structure that risk across layers. This article builds the adapter layer that connects them. The result is CPyramidBridge, a wrapper class that sits between the bet-sizing stack and the pyramid engine and wires them through five integration points. Each point replaces a hardcoded parameter in CPyramidEngine with a live output from the sizing module.
Before we begin, note the prerequisites. The code in this article depends on all five files from Part 13 (BetSizingUtils.mqh, EF3M.mqh, Ch10Snippets.mqh, BetSizing.mqh, BetSizingEA.mq5) and on the three files from the pyramiding article (PyramidUtils.mqh, PyramidEngine.mqh, PyramidEA.mq5). This article adds two new files: PyramidEngine_additions.mqh, which extends the engine with seven new public methods, and BetSizingPyramidBridge.mqh, which implements CPyramidBridge. A wired demonstration EA, BetSizingPyramidEA.mq5, shows all five integration points active at once. All three new files are in the attached archive.
Figure 1 illustrates pyramiding. Decreasing lot sizes entered at higher price levels form a triangular outline. Because the unified stop advances after each add-on, total dollar risk falls at each stage even as the total position size increases.

Figure 1. 2-panel illustration of the pyramiding concept
- Panel (a): Three horizontal bars drawn at their entry price levels — Initial (1.00 lot at E1), Add-on 1 (0.60 lot at E2, +60 pips), Add-on 2 (0.30 lot at E3, +120 pips). The dashed line connecting the bar tips traces the pyramid outline: a wide base at the lowest price, narrowing to an apex at the highest. The unified stop level for each stage is shown as a dotted horizontal line; the red-shaded zone illustrates the Stage 1 risk band.
- Panel (b): Total risk in pip-lots at each stage (blue/green/orange bars, left axis) alongside total open lots (grey dashed line, right axis). Risk falls from 50 → 40 → 15 pip-lots while the position grows from 1.00 → 1.60 → 1.90 lots. The reduction occurs because the advancing unified stop moves previously entered positions to break-even or into locked profit, so only the newest, smallest add-on carries open risk.
Architecture Overview
The five-file bet-sizing stack from Part 13 forms a clean dependency hierarchy. BetSizingUtils.mqh is the foundation, providing the normal CDF, its inverse, the sweep-line concurrency counter, and the BetSizeResult struct. EF3M.mqh and Ch10Snippets.mqh each depend only on that layer. BetSizing.mqh depends on both, and the EA sits at the top. CPyramidEngine is an independent two-file stack: PyramidUtils.mqh provides instrument-agnostic pip-value computation and broker-level stop validation; PyramidEngine.mqh implements the pyramid management class.
BetSizingPyramidBridge.mqh does not modify either existing stack. It includes both stacks, owns a CPyramidEngine instance as a private member, and exposes a public interface that replaces direct engine calls in the EA. The EA talks exclusively to the bridge; the bridge talks to both the sizing functions and the engine. The integration points are wired inside the bridge so that no sizing logic appears in the EA and no engine knowledge is required from the sizing functions.
Figure 2 shows the full dependency graph.

Figure 2. 3-layer dependency graph showing the BetSizing stack, CPyramidEngine, and the CPyramidBridge adapter
- Left column: the Part 13 BetSizing stack from BetSizingUtils.mqh up through BetSizing.mqh.
- Right column: PyramidUtils.mqh, the original PyramidEngine.mqh, and the seven new public methods added by PyramidEngine_additions.mqh.
- Center: BetSizingPyramidBridge.mqh, which connects the two stacks. The EA calls only the bridge. The five numbered labels in the callout box correspond to the five integration points developed in the following section.
| File | Place in | Role in this article |
|---|---|---|
| BetSizingUtils.mqh | MQL5\Include\BetSizing\ | Unchanged from Part 13 |
| EF3M.mqh | MQL5\Include\BetSizing\ | Unchanged from Part 13 |
| Ch10Snippets.mqh | MQL5\Include\BetSizing\ | Unchanged from Part 13 |
| BetSizing.mqh | MQL5\Include\BetSizing\ | Unchanged from Part 13 |
| PyramidUtils.mqh | MQL5\Include\Pyramid\ | Unchanged from pyramiding article |
| PyramidEngine.mqh | MQL5\Include\Pyramid\ | Unchanged; receives seven new public methods |
| PyramidEngine_additions.mqh | MQL5\Include\Pyramid\ | New. Paste the contents into CPyramidEngine's public section |
| BetSizingPyramidBridge.mqh | MQL5\Include\Pyramid\ | New. The bridge adapter class |
| BetSizingPyramidEA.mq5 | MQL5\Experts\ | New. Demonstration EA wiring all five points |
The Five Integration Points
The five integration points address five separate design choices in the original CPyramidEngine that were hardcoded at initialization and had no mechanism for the sizing module to influence. Each point maps a specific output of the sizing stack to a specific parameter or decision in the engine.
Point 1 — Probability-Calibrated Lot Sizing
In the Part 13 EA, lot_initial is a fixed input. A 0.55-confidence signal and a 0.85-confidence signal open the same-sized initial position. The fix is to compute lot_initial at entry time from the probability method's output:
//--- Integration Point 1: base lot from BetSizeProbability double base_lot = m_cfg.max_lots * MathAbs(prob_bet_size);
Add-on lots must maintain the strictly decreasing constraint that CPyramidEngine::Init() validates. Expressing them as fixed ratios of base_lot preserves the constraint across the full range of prob_bet_size outputs:
//--- Compute proportional add-on lots, floor to broker step double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); out_initial = NormalizeDouble(MathFloor(base_lot / step) * step, 2); out_addon1 = NormalizeDouble(MathFloor(base_lot * m_cfg.addon1_ratio / step) * step, 2); out_addon2 = NormalizeDouble(MathFloor(base_lot * m_cfg.addon2_ratio / step) * step, 2);
Flooring rather than rounding is intentional. Rounding can advance a lot to the next broker step, which can cause the constraint to fail after clipping to SYMBOL_VOLUME_MIN. Flooring guarantees the resulting lots are always smaller than the unrounded value; the constraint survives clipping. When the computed lots collapse to the same minimum after clipping — which occurs when prob_bet_size is small and the instrument has a coarse lot step — ComputeProportionalLots() returns false and the entry is skipped. This is correct behavior: a low-confidence signal that cannot be expressed as a valid pyramid structure should not open a trade at all.
The engine's lot fields are not set at Init() time for each trade; they are updated immediately before each OpenInitial() call via the new UpdateLots() method. This is safe because UpdateLots() refuses to write when a pyramid is already active.
Point 2 — Budget Gate on Entry
The only entry gate in the original CPyramidEngine is IsActive(): if no pyramid is running, any signal that passes the entry logic can open one. This ignores the concurrent occupancy of the signal book. BetSizeBudget computes the normalized long-short imbalance fraction. Its complement — 1 − |c_t| — is the available headroom. The bridge checks this before calling TryOpenInitial(). SeedBudgetMaxima() is required. Without it, the running maxima start at 1, so the earliest bars produce oversized budget fractions:
//--- Integration Point 2: budget gate BetSizeResult r = BetSizeBudget(open_t, close_t, sides, now); double headroom = 1.0 - MathAbs(r.c_t); if(headroom < m_cfg.budget_min_bet) { PrintFormat("Budget gate blocked | c_t=%.3f headroom=%.3f", r.c_t, headroom); return(false); }
The threshold budget_min_bet is a configuration parameter in SBridgeConfig. A value of 0.10 blocks entry when the book is 90% occupied. A value of 0.00 disables the gate entirely. The correct value depends on the average holding period and the expected maximum concurrent signal count for the strategy.
Point 3 — Dynamic Add-On Trigger
The original engine fires add-ons when the position has moved a fixed number of pips in favor. Fixed pip triggers have no awareness of the model's forecast. BetSizeDynamic maps the divergence between the current price and the model's forecast price through a calibrated sigmoid or power curve, returning a value in [−1, 1]. As the trade moves in favor and the price approaches the forecast, the dynamic bet size rises. The bridge tracks the dynamic bet size at the entry bar and compares it against two thresholds on every tick. When the first threshold is crossed, it advances the engine's internal add-on 1 pip trigger to 0.1 pip — a value so small that the position's existing P&L will always satisfy it — and the engine fires the add-on on the next Manage() call:
//--- Integration Point 3: dynamic add-on trigger bool a1_crossed = (!m_engine.IsAddon1Open()) && (current_dyn_bet >= m_cfg.dynamic_level_1); bool a2_crossed = (m_engine.IsAddon1Open()) && (!m_engine.IsAddon2Open()) && (current_dyn_bet >= m_cfg.dynamic_level_2); if(a1_crossed) m_engine.SetAddonTriggerPips(0.1, m_engine.GetAddon2TriggerPips()); if(a2_crossed) m_engine.SetAddonTriggerPips(m_engine.GetAddon1TriggerPips(), 0.1);
SetAddonTriggerPips() can only reduce triggers. This prevents a crossed threshold from being undone if the dynamic bet size briefly dips afterward. The original pip-based triggers remain as fallbacks: if use_dynamic_trigger is false in SBridgeConfig, the bridge does nothing and the engine's original pip logic operates unchanged.
Point 4 — Reserve Sizing as Adaptive Trail Multiplier
BetSizeReserve maps the raw concurrent imbalance through the CDF of the EF3M mixture fitted to historical imbalance data. When the reserve bet size contracts, the imbalance is moving back toward the center of the historical distribution; the empirical support for holding is weaker. The bridge computes the pyramid's current allocation ratio as total open lots divided by max_lots and compares it against the reserve bet size on each new bar. When the reserve drops below the allocation, the trail is tightened by reserve_tighten_mul; when the reserve recovers, the base trail is restored:
//--- Integration Point 4: reserve-adaptive trail double total_lots = m_live_lot_initial + m_live_lot_addon1 + m_live_lot_addon2; double alloc_ratio = (m_cfg.max_lots > 0.0) ? total_lots / m_cfg.max_lots : 0.0; if(reserve_bet < alloc_ratio) { double tight_pips = m_base_trail_pips * m_cfg.reserve_tighten_mul; double tight_step = m_base_trail_step * m_cfg.reserve_tighten_mul; m_engine.SetTrailParams(tight_pips, tight_step); } else m_engine.SetTrailParams(m_base_trail_pips, m_base_trail_step);
This point requires the EF3M warm-up described in Part 13: FitM2N() must be called in OnInit() from the historical c_t series before the reserve method can be used here. The minimum recommended history is 500 bets; with fewer observations the mixture fit is unstable and the adaptive trail will oscillate.
Point 5 — Signal Array Synchronization on Pyramid Close
When CPyramidEngine detects that the initial position has been closed, it calls ResetState() and goes inactive. The sizing module's signal arrays in the EA do not know this has happened. The most recent open signal's close_t entry still points to the original triple-barrier t1 timestamp, so the signal remains registered as active in the concurrency counter. Every subsequent call to BetSizeProbability or BetSizeBudget overcounts active signals by one. The bridge exposes a single boolean check, WasJustClosed(), that the EA calls after every HandleTransaction() pass:
//--- Integration Point 5: detect pyramid close and sync arrays bool was_active = g_bridge.IsActive(); g_bridge.HandleTransaction(trans); if(g_bridge.WasJustClosed(was_active)) SyncSignalArraysOnClose();
SyncSignalArraysOnClose() walks backward through g_close_t[] and sets the timestamp of the most recent still-open signal to TimeCurrent(). For the concurrency counter, which uses event times at bar granularity, the approximation is exact.
Engine Additions
The five integration points require seven new public methods on CPyramidEngine. None of these modify existing logic; they add public read and write access to member variables that were already present but not exposed. Paste the contents of PyramidEngine_additions.mqh into the public section of CPyramidEngine in PyramidEngine.mqh, immediately after the existing GetUnifiedStop() declaration.
//+------------------------------------------------------------------+ //| UpdateLots: update lot sizes before the next TryOpenInitial call | //| Returns false when the pyramid is active; no writes occur | //+------------------------------------------------------------------+ bool UpdateLots(double lot_initial, double lot_addon1, double lot_addon2) { if(m_state.active) { Print("UpdateLots called while pyramid active — ignored."); return(false); } if(lot_addon1 >= lot_initial || lot_addon2 >= lot_addon1) { Print("UpdateLots: decreasing constraint violated."); return(false); } m_lot_initial = lot_initial; m_lot_addon1 = lot_addon1; m_lot_addon2 = lot_addon2; return(true); } //+------------------------------------------------------------------+ //| SetAddonTriggerPips: advance add-on pip triggers; never retracts | //| A trigger can only decrease; passing a larger value is ignored | //+------------------------------------------------------------------+ void SetAddonTriggerPips(double trig1, double trig2) { if(trig1 < m_addon1_trigger_pips) m_addon1_trigger_pips = trig1; if(trig2 < m_addon2_trigger_pips) m_addon2_trigger_pips = trig2; } //+------------------------------------------------------------------+ //| Read-only accessors used by CPyramidBridge for dynamic triggers | //+------------------------------------------------------------------+ double GetAddon1TriggerPips(void) { return(m_addon1_trigger_pips); } double GetAddon2TriggerPips(void) { return(m_addon2_trigger_pips); } bool IsAddon1Open(void) { return(m_state.addon1_open); } bool IsAddon2Open(void) { return(m_state.addon2_open); } //+------------------------------------------------------------------+ //| SetTrailParams: update trailing stop parameters at runtime | //| Used by the reserve-adaptive trail (Integration Point 4) | //+------------------------------------------------------------------+ void SetTrailParams(double trail_pips, double trail_step) { m_trail_pips = trail_pips; m_trail_step_pips = trail_step; }
SetAddonTriggerPips() uses a strictly non-increasing write: it only accepts a new trigger value if that value is smaller than the one currently stored. UpdateLots() checks m_state.active first and returns false without writing if a pyramid is currently open; calling it mid-trade would produce a silent state inconsistency.
The Bridge Class
The full BetSizingPyramidBridge.mqh is in the attached archive. SBridgeConfig carries all bridge configuration in one struct passed to Init(). This keeps the EA's input section clean: the bridge configuration is assembled into a struct in OnInit() and passed once, rather than spreading a dozen parameters across separate function calls.
struct SBridgeConfig { //--- Point 1 — lot sizing double max_lots; // Maximum lots at full confidence double addon1_ratio; // lot_addon1 = lot_initial * ratio double addon2_ratio; // lot_addon2 = lot_initial * ratio //--- Point 2 — budget gate bool use_budget_gate; double budget_min_bet; // Skip entry when headroom < this value //--- Point 3 — dynamic add-on trigger bool use_dynamic_trigger; double dynamic_level_1; // |BetSizeDynamic| threshold for add-on 1 double dynamic_level_2; // |BetSizeDynamic| threshold for add-on 2 //--- Point 4 — reserve adaptive trail bool use_reserve_trail; double reserve_tighten_mul; // Trail multiplier when reserve < alloc_ratio double min_lot; // Hard floor for lot_initial };
CPyramidBridge owns one private CPyramidEngine instance. It also stores the base trail parameters separately so the reserve trail logic can always restore them after tightening. The live lot values are stored after each TryOpenInitial() call to allow the allocation ratio calculation in Point 4 to run without querying broker positions. The public interface exposes five integration methods plus pass-throughs to the engine for operations the EA must call directly:
bool Init(const SBridgeConfig &cfg, double trail_pips, double trail_step); bool InitEngine(int magic, int slip, double lot_i, double lot_a1, double lot_a2, double trig1, double trig2, double stop1, double stop2, bool trail, double trail_pips, double trail_step); //--- Points 1 and 3: open initial position with calibrated lots bool TryOpenInitial(long direction, double price, double sl, double prob_bet_size, double dynamic_bet_size, string comment = "Pyramid Entry"); //--- Point 2: budget gate check before entry bool IsBudgetClearForEntry(const datetime &open_t[], const datetime &close_t[], const int &sides[], datetime now); //--- Point 3: update dynamic trigger — call every tick when active void UpdateDynamicTrigger(double current_dyn_bet); //--- Point 4: reserve-adaptive trail — call once per bar when active void AdaptTrailToReserve(double reserve_bet); //--- Point 5: did the pyramid just close? bool WasJustClosed(bool was_active_before); //--- Engine pass-throughs void Manage(void); void HandleTransaction(const MqlTradeTransaction &trans); void RecoverState(void); bool IsActive(void); double GetUnifiedStop(void);
The separation between Init() and InitEngine() is deliberate. The bridge configuration is independent of the engine configuration: the two can be changed on separate development cycles without touching the other. The lot values passed to InitEngine() serve as placeholders only; they are overwritten by UpdateLots() at each TryOpenInitial() call.
Wiring a Complete Expert Advisor
Figure 3 shows what the five integration points produce on synthetic data over 30 bars: panel (a) plots the BetSizeProbability output, with threshold lines marking when add-on 1 (|bet_size| > 0.40) and add-on 2 (|bet_size| > 0.65) would be warranted; panel (b) shows the corresponding pyramid lot layers — the same structure shown abstractly in Figure 1 now sized dynamically by the classifier's confidence.

Figure 3. 2-panel illustration of BetSizeProbability output driving proportional pyramid lot allocation
- Panel (a): Discretized bet_size from BetSizeProbability over 30 synthetic bars. The orange dotted line marks the add-on 2 threshold (0.65); the green dotted line marks the add-on 1 threshold (0.40).
- Panel (b): Stacked pyramid lot layers derived from the signal above. Blue bars show lot_initial. Green shows lot_addon1 (0.60 × lot_initial). Orange shows lot_addon2 (0.30 × lot_initial), which only appears when |bet_size| > 0.65.
The full demonstration EA, BetSizingPyramidEA.mq5, wires all five integration points. The OnTick() sequence is:
//+------------------------------------------------------------------+ //| OnTick: entry point called on every market tick | //| Runs dynamic trigger and Manage() on every tick; reserve trail | //| and entry evaluation run on new bars only | //+------------------------------------------------------------------+ void OnTick(void) { datetime current_bar = iTime(_Symbol, PERIOD_H1, 0); bool is_new_bar = (current_bar != g_last_bar); if(is_new_bar) g_last_bar = current_bar; //--- Point 3: update dynamic trigger on every tick if(g_bridge.IsActive()) { double dyn_bet = MathAbs(BetSizeDynamic( g_current_pos, InpMaxLots * 100, SymbolInfoDouble(_Symbol, SYMBOL_BID), GetForecastPrice(), InpCalDiv * _Point, InpCalBetSize, InpDynFunc).bet_size); g_bridge.UpdateDynamicTrigger(dyn_bet); } //--- Manage pyramid on every tick bool was_active = g_bridge.IsActive(); g_bridge.Manage(); //--- Point 5: sync arrays on pyramid close if(g_bridge.WasJustClosed(was_active)) SyncSignalArraysOnClose(); if(is_new_bar) { //--- Point 4: adapt trail once per bar if(g_bridge.IsActive() && InpUseReserveTrail) { BetSizeResult rv = BetSizeReserve(g_open_t, g_close_t, g_sides, TimeCurrent(), g_reserve_params); g_bridge.AdaptTrailToReserve(MathAbs(rv.bet_size)); } if(!g_bridge.IsActive()) CheckForEntry(); } }
The call ordering reflects the different time scales of the five points. The dynamic trigger check and Manage() run on every tick because add-on triggers must respond to intrabar price movement. The reserve trail and entry signal run on new bars only: the trail adapts at bar granularity, and the entry signal is evaluated on completed bars to avoid signal noise within a bar.
CheckForEntry() calls IsBudgetClearForEntry() first and returns immediately if the gate is blocked. It then evaluates its signal, calls BetSizeProbability for the confidence-weighted bet_size, rejects entries where the probability method's direction disagrees with the signal direction, and calls TryOpenInitial() with both the probability and dynamic bet sizes:
//+------------------------------------------------------------------+ //| CheckForEntry: evaluate signal and submit via the bridge | //| Applies the budget gate (Point 2), computes the probability and | //| dynamic bet sizes, then calls TryOpenInitial | //+------------------------------------------------------------------+ void CheckForEntry(void) { datetime now = TimeCurrent(); //--- Point 2: budget gate if(!g_bridge.IsBudgetClearForEntry(g_open_t, g_close_t, g_sides, now)) return; // ... evaluate signal, determine direction, price, sl ... //--- Point 1 source: probability bet size BetSizeResult prob_r = BetSizeProbability( g_open_t, g_close_t, g_prob, g_pred, 2, InpStepSize, InpAvgActive, now); if(direction == POSITION_TYPE_BUY && prob_r.bet_size <= 0.0) return; if(direction == POSITION_TYPE_SELL && prob_r.bet_size >= 0.0) return; //--- Point 3 source: dynamic bet size at entry bar double dyn_bet = MathAbs(BetSizeDynamic( 0, InpMaxLots * 100, price, GetForecastPrice(), InpCalDiv * _Point, InpCalBetSize, InpDynFunc).bet_size); if(g_bridge.TryOpenInitial(direction, price, sl, prob_r.bet_size, dyn_bet, "Pyramid Entry")) AppendNewSignal(now, direction, MathAbs(prob_r.bet_size)); }
Two calibration decisions must be made before deployment. First, addon1_ratio and addon2_ratio must satisfy the strict constraint: addon1_ratio must be less than 1.0, and addon2_ratio must be less than addon1_ratio. For instruments with coarse lot steps, check that the computed lots survive clipping to SYMBOL_VOLUME_MIN before going live. Second, dynamic_level_1 and dynamic_level_2 must be calibrated against the strategy's historical BetSizeDynamic output. Run the bet-sizing stack over the warm-up data and inspect the distribution of |bet_size| values on bars where the position was profitable; the median and 75th percentile of that distribution are reasonable starting points for the two thresholds.
Conclusion
The Part 13 bet-sizing module and the pyramiding engine from Position Management: Safe Pyramiding with a Unified Stop in MQL5 solve different parts of the same problem. The sizing module answers how much capital a signal deserves, accounting for classifier confidence, label concurrency, and the empirical distribution of past positions. The pyramid engine answers how to structure that capital across layers with a mathematically provable risk-reduction property — the same property illustrated in Figure 1: each add-on is smaller than the previous, the unified stop advances, and total dollar risk falls at every stage. The two systems were not designed to work together; connecting them requires a thin adapter layer that translates sizing outputs into engine parameters.
CPyramidBridge implements that adapter across five integration points. The probability method drives initial lot sizing. The budget method gates entry when the signal book is at capacity. The dynamic method advances add-on triggers when the price divergence supports them. The reserve method tightens the trailing stop when the empirical distribution of imbalance contracts below the current allocation. A synchronization check keeps the signal arrays consistent when the pyramid closes. Each point is independently controllable through the SBridgeConfig struct; disabling any flag restores the original engine behavior for that aspect.
In Part 16 of the Blueprint series, the sizing layer is connected to the CPCV backtesting framework in the MetaTrader 5 Strategy Tester. The probability estimates flowing into BetSizeProbability are subject to systematic bias from class imbalance and calibration error; their characterization and correction are covered in Part 12.
Attached Files
| File | Place in | Depends On | Description | |
|---|---|---|---|---|
| 1 | BetSizingUtils.mqh | MQL5\Include\BetSizing\ | — | Unchanged from Part 13. NormCDF, NormICDF, NormPDF, RawMoments, SweepLineActiveCounts, BetSizeResult, Clamp, MathSign. |
| 2 | EF3M.mqh | MQL5\Include\BetSizing\ | BetSizingUtils.mqh | Unchanged from Part 13. M2NParams, DeriveComponentParams, FitM2N, MixtureCDF, ReserveBetSize. |
| 3 | Ch10Snippets.mqh | MQL5\Include\BetSizing\ | BetSizingUtils.mqh | Unchanged from Part 13. GetSignal, AvgActiveSignals, DiscreteSignal, SigmoidBetSize, PowerBetSize, GetW, LimitPrice. |
| 4 | BetSizing.mqh | MQL5\Include\BetSizing\ | Ch10Snippets.mqh, EF3M.mqh | Unchanged from Part 13. BetSizeProbability, BetSizeDynamic, BetSizeBudget, BetSizeReserve, SeedBudgetMaxima. |
| 5 | PyramidUtils.mqh | MQL5\Include\Pyramid\ | — | Unchanged from pyramiding article. Pip-value helpers and broker-level stop validation. |
| 6 | PyramidEngine.mqh | MQL5\Include\Pyramid\ | PyramidUtils.mqh | Original CPyramidEngine receives seven new public methods: UpdateLots, SetAddonTriggerPips, GetAddon1TriggerPips, GetAddon2TriggerPips, IsAddon1Open, IsAddon2Open, SetTrailParams. Paste into CPyramidEngine's |
| 7 | BetSizingPyramidBridge.mqh | MQL5\Include\Pyramid\ | BetSizing.mqh, PyramidEngine.mqh | SBridgeConfig struct and CPyramidBridge class implementing all five integration points. |
| 8 | BetSizingPyramidEA.mq5 | MQL5\Experts\ | BetSizingPyramidBridge.mqh | Demonstration EA wiring an EMA crossover signal to the bridge. All five integration points are active. Replace CheckForEntry() with any classifier output. |
References
- López de Prado, M. (2018). Advances in Financial Machine Learning. John Wiley & Sons. Chapter 10.
- López de Prado, M. and Foreman, M. (2014). A mixture of two Gaussians approach to mathematical portfolio oversight: The EF3M algorithm. Quantitative Finance, 14(5), 913–930.
- Hector, T. M. (2026). Position Management: Safe Pyramiding with a Unified Stop in MQL5.
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.
Custom Debugging and Profiling Tools for MQL5 Development (Part II): Profiling EAs and Testing Trading Logic
MQL5 Wizard Techniques you should know (Part 92): Using B-Tree Indexing and a Bayesian NN in a Custom Signal Class
Formulating Dynamic Multi-Pair EA (Part 9): Market Microstructure Execution Noise Filtering
MQL5 Bootstrap (I): Reusable Functions for Working with Positions and Orders
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use