Advertisement IC Markets

Exponencial money management with profit only

Master exponential money management, position sizing calculators, strict daily stop-loss limits, and overcoming FOMO on micro-timeframes.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Exponencial money management with profit only

Post by PTScalper »

MetaTrader 5 (MQL5) Implementation

Code: Select all

//+------------------------------------------------------------------+
//|                                        HouseMoney_Engine_MT5.mq5 |
//+------------------------------------------------------------------+
#property strict
#include <Trade\Trade.mqh>

// --- Risk Management Engine ---
input double InpReferenceBalance = 100000.0; // Reference Baseline (0 = use current balance)
input double InpBaseRiskPct      = 0.5;      // Base Risk (%)
input double InpScaledRiskPct    = 1.5;      // Scaled Risk (%)
input double InpProfitBuffer     = 5.0;      // Activation Threshold (%)
input double InpHardStopEq       = 10.0;     // Max Drawdown Killswitch (%)

// --- Execution Parameters ---
input int    InpSLPoints         = 150;      // Stop Loss (Points)
input int    InpTPPoints         = 350;      // Take Profit (Points)
input ulong  InpMagicNumber      = 77777;    // Magic Number

double initBalance;
datetime lastBarTime;
CTrade trade;

//+------------------------------------------------------------------+
int OnInit() {
    initBalance = (InpReferenceBalance > 0) ? InpReferenceBalance : AccountInfoDouble(ACCOUNT_BALANCE);
    trade.SetExpertMagicNumber(InpMagicNumber);
    lastBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
void OnTick() {
    datetime currentTime = iTime(_Symbol, PERIOD_CURRENT, 0);
    if(currentTime == lastBarTime) return;
    lastBarTime = currentTime;

    double currentEq = AccountInfoDouble(ACCOUNT_EQUITY);
    double killswitchEq = initBalance * (1.0 - (InpHardStopEq / 100.0));
    double targetEq = initBalance * (1.0 + (InpProfitBuffer / 100.0));

    // Hard Stop Killswitch
    if(currentEq <= killswitchEq) {
        CloseAllPositions();
        Print("HARD STOP: Drawdown limit breached. Trading halted.");
        ExpertRemove();
        return;
    }

    // Only allow one open position
    if(PositionsTotal() > 0) return;

    // Buffer arrays for price data
    double high[], low[], close[];
    ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true);
    
    if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, 8, high) <= 0) return;
    if(CopyLow(_Symbol, PERIOD_CURRENT, 0, 8, low) <= 0) return;
    if(CopyClose(_Symbol, PERIOD_CURRENT, 0, 3, close) <= 0) return;

    // Structure detection: 5-bar lookback pivot (indexes 2 through 6)
    double lastPivotHigh = 0;
    double lastPivotLow  = 999999;
    
    for(int i = 2; i <= 6; i++) {
        if(high[i] > lastPivotHigh) lastPivotHigh = high[i];
        if(low[i]  < lastPivotLow)  lastPivotLow  = low[i];
    }

    // Sweep conditions (Bar 1 swept the pivot and closed back inside)
    bool sweepShort = high[1] > lastPivotHigh && close[1] < lastPivotHigh;
    bool sweepLong  = low[1] < lastPivotLow && close[1] > lastPivotLow;

    if(!sweepShort && !sweepLong) return;

    // Dynamic Size Calculation
    double activeRisk = (currentEq >= targetEq) ? InpScaledRiskPct : InpBaseRiskPct;
    double riskCapital = currentEq * (activeRisk / 100.0);
    
    double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
    double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
    
    if(tickValue == 0 || tickSize == 0) return;

    // Normalization for points/ticks
    double pointMultiplier = _Point / tickSize;
    double adjustedSL = InpSLPoints * pointMultiplier;

    double rawLots = riskCapital / (adjustedSL * tickValue);
    double step    = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
    double minLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
    double maxLot  = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
    
    double lotSize = MathFloor(rawLots / step) * step;
    if(lotSize < minLot) lotSize = minLot;
    if(lotSize > maxLot) lotSize = maxLot;

    // Execution
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
    double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

    if(sweepLong) {
        double sl = NormalizeDouble(ask - (InpSLPoints * _Point), _Digits);
        double tp = NormalizeDouble(ask + (InpTPPoints * _Point), _Digits);
        trade.Buy(lotSize, _Symbol, ask, sl, tp, "HouseMoney L");
    }
    else if(sweepShort) {
        double sl = NormalizeDouble(bid + (InpSLPoints * _Point), _Digits);
        double tp = NormalizeDouble(bid - (InpTPPoints * _Point), _Digits);
        trade.Sell(lotSize, _Symbol, bid, sl, tp, "HouseMoney S");
    }
}

//+------------------------------------------------------------------+
void CloseAllPositions() {
    for(int i = PositionsTotal() - 1; i >= 0; i--) {
        ulong ticket = PositionGetTicket(i);
        if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == InpMagicNumber) {
            trade.PositionClose(ticket);
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Exponencial money management with profit only

Post by PTScalper »

Porting this to cTrader (cAlgo / C#) actually allows us to improve the architecture significantly.

Because C# and the cTrader API provide a strict event-driven separation between OnTick() and OnBar(), we don't have to awkwardly bundle our time-tracking logic like we did in MQL4/5.

We can split the engine logically:

The Entry Trigger (OnBar): Evaluates the liquidity sweep strictly on the close of the candle. No repainting wicks, no false signals mid-heat.

The Risk Killswitch (OnTick): Evaluates the hard drawdown limit tick-by-tick. If market volatility spikes and hits your killswitch, it halts the bot instantly rather than waiting for the candle to close.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Exponencial money management with profit only

Post by PTScalper »

Here is the production-grade cBot.

Code: Select all

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class HouseMoneyEngine : Robot
    {
        // =========================================================================
        // PARAMETERS: RISK MANAGEMENT
        // =========================================================================
        [Parameter("Reference Balance (0 = Use Current)", Group = "Risk Management", DefaultValue = 100000.0)]
        public double ReferenceBalance { get; set; }

        [Parameter("Base Risk (%)", Group = "Risk Management", DefaultValue = 0.5, MinValue = 0.1, Step = 0.1)]
        public double BaseRiskPct { get; set; }

        [Parameter("Scaled Risk (%)", Group = "Risk Management", DefaultValue = 1.5, MinValue = 0.1, Step = 0.1)]
        public double ScaledRiskPct { get; set; }

        [Parameter("Activation Threshold (%)", Group = "Risk Management", DefaultValue = 5.0, MinValue = 0.1, Step = 0.5)]
        public double ProfitBuffer { get; set; }

        [Parameter("Max Drawdown Killswitch (%)", Group = "Risk Management", DefaultValue = 10.0, MinValue = 1.0, Step = 1.0)]
        public double HardStopEq { get; set; }

        // =========================================================================
        // PARAMETERS: EXECUTION (Converted to Pips for cTrader standard)
        // =========================================================================
        [Parameter("Stop Loss (Pips)", Group = "Execution", DefaultValue = 15.0)]
        public double StopLossPips { get; set; }

        [Parameter("Take Profit (Pips)", Group = "Execution", DefaultValue = 35.0)]
        public double TakeProfitPips { get; set; }

        private double _initBalance;
        private const string TradeLabel = "HouseMoney";

        protected override void OnStart()
        {
            // Initialize baseline. Hard-coding the ReferenceBalance ensures a VPS 
            // or terminal restart doesn't reset your baseline capital metric.
            _initBalance = ReferenceBalance > 0 ? ReferenceBalance : Account.Balance;
        }

        protected override void OnTick()
        {
            // 1. Tick-by-Tick Killswitch Monitoring
            // We monitor absolute capital preservation live, not just at bar close.
            double killswitchEq = _initBalance * (1.0 - (HardStopEq / 100.0));
            if (Account.Equity <= killswitchEq)
            {
                foreach (var position in Positions.Where(p => p.Label == TradeLabel && p.SymbolName == SymbolName))
                {
                    ClosePosition(position);
                }
                
                Print("HARD STOP: Drawdown limit breached. Trading halted.");
                Stop(); // Kills the cBot instance
                return;
            }

            // 2. Real-time Telemetry HUD
            UpdateHUD();
        }

        protected override void OnBar()
        {
            // Restrict to one active position to prevent risk compounding
            if (Positions.Count(p => p.Label == TradeLabel && p.SymbolName == SymbolName) > 0) return;

            // =========================================================================
            // MICROSTRUCTURE PROXY: LIQUIDITY SWEEP
            // =========================================================================
            // We scan indices 2 to 6, because index 1 is the bar that just closed.
            double lastPivotHigh = double.MinValue;
            double lastPivotLow  = double.MaxValue;

            for (int i = 2; i <= 6; i++)
            {
                if (Bars.HighPrices.Last(i) > lastPivotHigh) lastPivotHigh = Bars.HighPrices.Last(i);
                if (Bars.LowPrices.Last(i) < lastPivotLow)   lastPivotLow  = Bars.LowPrices.Last(i);
            }

            // Sweep conditions: The trigger bar pierced the fractal wick but closed back inside.
            bool sweepShort = Bars.HighPrices.Last(1) > lastPivotHigh && Bars.ClosePrices.Last(1) < lastPivotHigh;
            bool sweepLong  = Bars.LowPrices.Last(1)  < lastPivotLow  && Bars.ClosePrices.Last(1) > lastPivotLow;

            if (!sweepShort && !sweepLong) return;

            // =========================================================================
            // DYNAMIC RISK & POSITION SIZING
            // =========================================================================
            double targetEq = _initBalance * (1.0 + (ProfitBuffer / 100.0));
            
            // Immediate Reset Logic:
            double activeRisk = (Account.Equity >= targetEq) ? ScaledRiskPct : BaseRiskPct;
            double riskCapital = Account.Equity * (activeRisk / 100.0);
            
            // Calculate fiat cost per unit for accurate sizing across forex, indices, or commodities
            double riskPerUnit = StopLossPips * Symbol.PipValue;
            if (riskPerUnit <= 0) return; 

            double rawVolume = riskCapital / riskPerUnit;
            double volume = Symbol.NormalizeVolumeInUnits(rawVolume, RoundingMode.Down);

            if (volume < Symbol.VolumeInUnitsMin) return;
            if (volume > Symbol.VolumeInUnitsMax) volume = Symbol.VolumeInUnitsMax;

            // =========================================================================
            // EXECUTION
            // =========================================================================
            if (sweepLong)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, volume, TradeLabel, StopLossPips, TakeProfitPips);
            }
            else if (sweepShort)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, volume, TradeLabel, StopLossPips, TakeProfitPips);
            }
        }

        private void UpdateHUD()
        {
            double targetEq = _initBalance * (1.0 + (ProfitBuffer / 100.0));
            bool isScaled = Account.Equity >= targetEq;
            double activeRisk = isScaled ? ScaledRiskPct : BaseRiskPct;
            
            string hudText = $"=== HOUSE MONEY ENGINE ===\n" +
                             $"Live Equity: {Account.Equity:C2}\n" +
                             $"Target Eq:   {targetEq:C2}\n" +
                             $"Status:      {(isScaled ? "SCALED RISK" : "BASE RISK")}\n" +
                             $"Active Risk: {activeRisk}%";

            Chart.DrawStaticText("RiskHUD", hudText, VerticalAlignment.Bottom, HorizontalAlignment.Right, 
                isScaled ? Color.LimeGreen : Color.DarkGray);
        }
    }
}
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
LondonScalper
Posts: 770
Joined: Sat Sep 05, 2026 7:54 am

Re: Exponencial money management with profit only

Post by LondonScalper »

PTScalper wrote:I reset size immediately to baseline the moment equity drops below the designated profit threshold. Stepping down in stages is an illusion of control. Profits are a risk expansion budget — scaled risk only unlocks at a locked equity threshold, and the privilege is revoked the moment the buffer is gone.
Immediate reset beats staged step-downs — I have watched the gentle glide just finance being out of sync with the tape. Treating locked profit as a defined expansion budget rather than casino chips is the healthy frame; hard-coding the unlock and revoke removes mid-heat temptation better than any pep talk. Base risk that never needs profit permission remains the cost of doing business; heavier size is a privilege tied to a buffer that can disappear on a normal drawdown.

Desk rule: size steps only on closed equity thresholds, never mid-trade; if equity loses the buffer, next ticket is base unit with no debate. Exponential curves on a whiteboard still need brakes on a cold month.

Where do you set the unlock threshold in practice — a fixed R multiple of starting equity, or a count of closed green days before scaled risk is allowed?
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Exponencial money management with profit only

Post by PTScalper »

LondonScalper wrote: Tue Sep 22, 2026 10:02 pm
PTScalper wrote:I reset size immediately to baseline the moment equity drops below the designated profit threshold. Stepping down in stages is an illusion of control. Profits are a risk expansion budget — scaled risk only unlocks at a locked equity threshold, and the privilege is revoked the moment the buffer is gone.
Immediate reset beats staged step-downs — I have watched the gentle glide just finance being out of sync with the tape. Treating locked profit as a defined expansion budget rather than casino chips is the healthy frame; hard-coding the unlock and revoke removes mid-heat temptation better than any pep talk. Base risk that never needs profit permission remains the cost of doing business; heavier size is a privilege tied to a buffer that can disappear on a normal drawdown.

Desk rule: size steps only on closed equity thresholds, never mid-trade; if equity loses the buffer, next ticket is base unit with no debate. Exponential curves on a whiteboard still need brakes on a cold month.

Where do you set the unlock threshold in practice — a fixed R multiple of starting equity, or a count of closed green days before scaled risk is allowed?
Hi LondonScalper,

A fixed R multiple of closed equity is the only mathematically sound threshold. Counting closed green days is a psychological metric, not a mathematical one, and relying on it for sizing decisions usually results in scaling up at exactly the wrong time.

You could easily grind out three consecutive "green days" that total +1.5R, unlock a heavier size tier on day four, and wipe out the entire week's progress with a single loss at the expanded size. Time does not pay for risk; only captured equity does.

Setting the threshold based on a fixed R multiple of closed profit ensures that the market pays for your size increase. Here is how that translates to the mechanics of the desk:

The Funding Block: Set the unlock threshold at +4R to +5R. This creates a concrete block of expansion capital. If you require a closed +4R buffer before stepping up your size (e.g., from 1% to 1.5%), your scaled tickets are entirely funded by recent performance. The starting equity is insulated.

The Revoke Floor: The step-down trigger must act before the buffer is fully depleted. If your threshold was +4R and a drawdown takes your buffer down to +2R, the scaling privilege is instantly revoked. You never wait for the buffer to hit zero to size down. The moment the cushion cannot comfortably absorb a normal two-ticket drawdown at the heavier size, the next ticket is base unit.

Immunity to Time: A fixed R threshold completely ignores the calendar. It does not matter if you captured the required buffer in a single, volatile session or ground it out over two weeks of tight price action. The math protects the baseline regardless of market speed.

Treating the R-multiple as a hard-coded toggle keeps ego and "feel" out of the sizing equation. The ledger either grants the privilege to expand, or it immediately denies it.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
LondonScalper
Posts: 770
Joined: Sat Sep 05, 2026 7:54 am

Re: Exponencial money management with profit only

Post by LondonScalper »

+4R to unlock and +2R to revoke is a sensible bracket. The part I'd test is the gap between them.

Say base risk is 1% and the step-up is 1.5%. You unlock at +4R; two losers at the heavier size cost 3R in base units, which leaves +1R — below the floor after just two tickets. Fine if intended, but it means the privilege survives two losses on an ordinary day. I set my floor as "buffer minus two heavy losers", worked out in advance, so I know exactly how many tickets the step-up can absorb before it goes.

Agreed on green-day counts. They reward frequency, not equity.

One refinement I use: the unlock only counts closed profit made since the last revoke. Otherwise you can bounce in and out of the heavier tier on the same buffer, which feels disciplined and isn't.
Post Reply