Advertisement IC Markets

Your Plan Should Include an "If This, Then That" Contingency Section

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: Your Plan Should Include an "If This, Then That" Contingency Section

Post by PTScalper »

2. The MT5 Implementation (MQL5)

In MT5, the structure is more modern. We use HistorySelect() to fetch the account's historical "Deals" rather than looping through a generic order pool, and we utilize the built-in CTrade library to easily kill open positions.

Code: Select all

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

input double DailyLossLimit  = 500.0;
input double BaseLotSize     = 0.1;
input int    NewsHourStart   = 13;
input int    NewsMinuteStart = 45;
input int    NewsHourEnd     = 14;
input int    NewsMinuteEnd   = 15;

datetime timeoutEndTime = 0;
CTrade trade;

void OnTick()
{
    // SCENARIO 1: Daily Loss Limit
    if(IsDailyLossReached())
    {
        CloseAllPositions();
        return; // Halt trading for the day
    }

    // SCENARIO 3: News Blackout
    if(IsNewsBlackout())
    {
        CloseAllPositions();
        return; // Halt trading during news
    }

    // SCENARIO 2: Penalty Box (Three Consecutive Losses)
    if(TimeCurrent() < timeoutEndTime) return; // In 60-min timeout
    
    CheckConsecutiveLosses(); 

    // Cut lot size by 50% if currently on a losing streak of 3 or more
    double currentLotSize = BaseLotSize;
    if(GetConsecutiveLosses() >= 3) currentLotSize = BaseLotSize / 2.0;
    
    // --- YOUR STRATEGY LOGIC GOES HERE ---
    // Example: if(BuySignal) trade.Buy(currentLotSize);
}

// =====================================================================
// HELPER FUNCTIONS
// =====================================================================

bool IsDailyLossReached()
{
    double todayRealized = 0.0;
    
    // Get midnight of the current day safely
    MqlDateTime dt; TimeToStruct(TimeCurrent(), dt);
    dt.hour = 0; dt.min = 0; dt.sec = 0;
    datetime startOfDay = StructToTime(dt);
    
    // Pull history for today
    HistorySelect(startOfDay, TimeCurrent());
    for(int i = 0; i < HistoryDealsTotal(); i++)
    {
        ulong ticket = HistoryDealGetTicket(i);
        long entryType = HistoryDealGetInteger(ticket, DEAL_ENTRY);
        
        // Only count realized profits (trades exiting the market)
        if(entryType == DEAL_ENTRY_OUT || entryType == DEAL_ENTRY_INOUT)
        {
            todayRealized += HistoryDealGetDouble(ticket, DEAL_PROFIT) +
                             HistoryDealGetDouble(ticket, DEAL_SWAP) +
                             HistoryDealGetDouble(ticket, DEAL_COMMISSION);
        }
    }
    
    double floatingProfit = AccountInfoDouble(ACCOUNT_EQUITY) - AccountInfoDouble(ACCOUNT_BALANCE);
    return ((todayRealized + floatingProfit) <= -DailyLossLimit);
}

bool IsNewsBlackout()
{
    MqlDateTime dt; TimeToStruct(TimeCurrent(), dt);
    int currentMinutes = dt.hour * 60 + dt.min;
    int startMinutes   = NewsHourStart * 60 + NewsMinuteStart;
    int endMinutes     = NewsHourEnd * 60 + NewsMinuteEnd;
    
    return (currentMinutes >= startMinutes && currentMinutes <= endMinutes);
}

int GetConsecutiveLosses()
{
    int lossCount = 0;
    HistorySelect(0, TimeCurrent()); // Load full history
    
    // Loop backwards through closed deals
    for(int i = HistoryDealsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = HistoryDealGetTicket(i);
        long entryType = HistoryDealGetInteger(ticket, DEAL_ENTRY);
        
        if(entryType == DEAL_ENTRY_OUT || entryType == DEAL_ENTRY_INOUT)
        {
            double pnl = HistoryDealGetDouble(ticket, DEAL_PROFIT) +
                         HistoryDealGetDouble(ticket, DEAL_SWAP) +
                         HistoryDealGetDouble(ticket, DEAL_COMMISSION);
                         
            if(pnl < 0) lossCount++;
            else if(pnl > 0) break; // Streak broken
        }
    }
    return lossCount;
}

void CheckConsecutiveLosses()
{
    static int lastHistoryCount = 0;
    HistorySelect(0, TimeCurrent());
    int currentHistoryCount = HistoryDealsTotal();
    
    if(currentHistoryCount > lastHistoryCount)
    {
        if(GetConsecutiveLosses() >= 3) timeoutEndTime = TimeCurrent() + 3600; 
        lastHistoryCount = currentHistoryCount;
    }
}

void CloseAllPositions()
{
    for(int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if(ticket > 0) 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: Your Plan Should Include an "If This, Then That" Contingency Section

Post by PTScalper »

Placing these gates at the absolute top of the OnTick() event handler means that regardless of what crazy signals your indicators are throwing, the EA physically cannot fire a trade if you are violating your pre-written, calm-minded risk rules.
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: Your Plan Should Include an "If This, Then That" Contingency Section

Post by PTScalper »

For cTrader, you must write your logic using cTrader Automate (formerly cAlgo) in C#.

Unlike MT4/MT5, cTrader is built on modern C# (.NET), which makes working with historical trades and account states much cleaner. We can use standard LINQ queries to quickly calculate daily profit or filter out recent consecutive losses.

Here is the complete cBot skeleton that hard-codes your three risk rules directly into the OnTick() loop.

Code: Select all

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class CompleteRiskManagementBot : Robot
    {
        // =========================================================================
        // PARAMETERS (Adjustable from the cTrader UI)
        // =========================================================================
        [Parameter("Daily Loss Limit ($)", DefaultValue = 500.0, Group = "Risk Rules")]
        public double DailyLossLimit { get; set; }

        [Parameter("Base Volume (e.g., 100k = 1 lot)", DefaultValue = 100000, Group = "Risk Rules")]
        public double BaseVolume { get; set; }

        [Parameter("News Blackout Start (Hour)", DefaultValue = 13, Group = "News Blackout (Server Time)")]
        public int NewsStartHour { get; set; }

        [Parameter("News Blackout Start (Minute)", DefaultValue = 45, Group = "News Blackout (Server Time)")]
        public int NewsStartMinute { get; set; }

        [Parameter("News Blackout End (Hour)", DefaultValue = 14, Group = "News Blackout (Server Time)")]
        public int NewsEndHour { get; set; }

        [Parameter("News Blackout End (Minute)", DefaultValue = 15, Group = "News Blackout (Server Time)")]
        public int NewsEndMinute { get; set; }

        // State variables
        private DateTime _timeoutEndTime;
        private int _lastHistoryCount;
        private bool _tradingHaltedForDay;
        private DateTime _currentTradingDay;

        protected override void OnStart()
        {
            _timeoutEndTime = Server.Time;
            _lastHistoryCount = History.Count;
            _tradingHaltedForDay = false;
            _currentTradingDay = Server.Time.Date;
        }

        protected override void OnTick()
        {
            // Reset daily halt flag when a new day begins
            if (_tradingHaltedForDay && Server.Time.Date > _currentTradingDay)
            {
                _tradingHaltedForDay = false;
                _currentTradingDay = Server.Time.Date;
                Print("New trading day. Daily loss limit reset.");
            }

            // =========================================================================
            // SCENARIO 1: The Daily Loss Limit
            // =========================================================================
            if (IsDailyLossReached())
            {
                if (!_tradingHaltedForDay)
                {
                    Print("Daily Loss Limit Hit! Flattening all positions and halting for the day.");
                    FlattenAccount();
                    _tradingHaltedForDay = true;
                }
                return; // Robot refuses to execute further logic today
            }

            // =========================================================================
            // SCENARIO 3: Scheduled News Events
            // =========================================================================
            if (IsNewsBlackout())
            {
                if (Positions.Count > 0 || PendingOrders.Count > 0)
                {
                    Print("Entering News Blackout Window! Flattening account.");
                    FlattenAccount();
                }
                return; // Robot refuses to execute further logic until news window ends
            }

            // =========================================================================
            // SCENARIO 2: Three Consecutive Losses (Timeout & Size Reduction)
            // =========================================================================
            CheckConsecutiveLosses();
            
            // Check if we are currently inside the 60-minute penalty box
            if (Server.Time < _timeoutEndTime) return; 
                
            // Adjust volume: cut size by 50% if currently on a losing streak of 3 or more
            double currentVolume = BaseVolume;
            if (GetConsecutiveLosses() >= 3)
            {
                currentVolume = BaseVolume / 2.0;
                // Ensure the halved volume aligns with the broker's minimum volume steps
                currentVolume = Symbol.NormalizeVolumeInUnits(currentVolume, RoundingMode.Down);
            }
            
            // =========================================================================
            // ENTRY EXECUTION LOGIC GOES HERE
            // =========================================================================
            // Example:
            // if (buySignalCondition) 
            // {
            //      ExecuteMarketOrder(TradeType.Buy, SymbolName, currentVolume, "BotEntry", 20, 20);
            // }
        }

        // =========================================================================
        // HELPER FUNCTIONS
        // =========================================================================
        
        private bool IsDailyLossReached()
        {
            // 1. Get realized net profit from trades closed today (using LINQ)
            var todaysHistory = History.Where(t => t.ClosingTime.Date == Server.Time.Date);
            double todayRealized = todaysHistory.Sum(t => t.NetProfit);
            
            // 2. Add current floating profit/loss
            double floatingProfit = Account.Equity - Account.Balance;
            
            return (todayRealized + floatingProfit) <= -DailyLossLimit;
        }
        
        private bool IsNewsBlackout()
        {
            var now = Server.Time;
            var startTime = now.Date.AddHours(NewsStartHour).AddMinutes(NewsStartMinute);
            var endTime = now.Date.AddHours(NewsEndHour).AddMinutes(NewsEndMinute);
            
            return now >= startTime && now <= endTime;
        }
        
        private int GetConsecutiveLosses()
        {
            int lossCount = 0;
            // Iterate history backwards (most recent trades first)
            var recentTrades = History.OrderByDescending(t => t.ClosingTime);
            
            foreach (var trade in recentTrades)
            {
                if (trade.NetProfit < 0) lossCount++;
                else if (trade.NetProfit > 0) break; // Streak broken by a winner
            }
            
            return lossCount;
        }
        
        private void CheckConsecutiveLosses()
        {
            // Only trigger a check at the exact moment a new trade is added to history
            if (History.Count > _lastHistoryCount)
            {
                if (GetConsecutiveLosses() >= 3)
                {
                    Print("3 Consecutive Losses Hit. Initiating 60-minute timeout.");
                    _timeoutEndTime = Server.Time.AddMinutes(60); 
                }
                _lastHistoryCount = History.Count;
            }
        }
        
        private void FlattenAccount()
        {
            // We use .ToArray() to avoid collection modification errors during closing
            foreach (var position in Positions.ToArray())
            {
                ClosePositionAsync(position);
            }
            
            foreach (var order in PendingOrders.ToArray())
            {
                CancelPendingOrderAsync(order);
            }
        }
    }
}
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: Your Plan Should Include an "If This, Then That" Contingency Section

Post by PTScalper »

Key Differences in the cTrader Execution:

1.) Clean History Tracking (IsDailyLossReached): Instead of parsing arrays by ID loops, cTrader allows us to use LINQ .Where(t => t.ClosingTime.Date == Server.Time.Date) to instantly pull today's closed trades and .Sum() their NetProfit. We add this to Account.Equity - Account.Balance to see exactly where we stand intraday.

2.) Safe Volume Halving (NormalizeVolumeInUnits): cTrader is extremely strict about order sizes matching the broker's minimum step volume. If your Base Volume is 150,000 units, dividing it by 2 is 75,000. If the broker only allows increments of 10,000, the order will bounce. Symbol.NormalizeVolumeInUnits(currentVolume, RoundingMode.Down) protects your script from crashing when halving your size.

3.) Asynchronous Flattening (FlattenAccount): When dealing with extreme market events (like the News Blackout window), you don't want the bot waiting for one position to close before sending the command for the next. Using ClosePositionAsync() rapidly fires the close commands for all open trades and pending limit/stop orders simultaneously.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply