Page 2 of 2

Re: News Spread Widening Will Blow Your Stop

Posted: Sat Sep 26, 2026 6:53 pm
by PTScalper
To elevate this from a basic time filter to a professional-grade execution module for cTrader (C#), we need to address how institutional algorithms actually manage risk around macroeconomic events.

A "Pro" implementation does not just look at the clock; it tracks market microstructure, handles multiple sessions, and manages open exposure dynamically.

Re: News Spread Widening Will Blow Your Stop

Posted: Sat Sep 26, 2026 6:53 pm
by PTScalper
Here is the upgraded cTrader cBot template featuring four professional enhancements:

Real-Time Spread Tracking: The true symptom of a hollow order book is a widened spread. The bot blocks entries the millisecond the spread exceeds your defined pip threshold.

Pre-News Risk Management: Instead of a blind "close everything" logic that might realize a loss, you can instruct the bot to move all profitable trades to Break-Even right before the news hits, giving you a "free ride" through the volatility.

Multiple Time Windows: Supports parsing HH:MM strings for multiple daily events (e.g., morning European news and afternoon US news).

On-Chart HUD (Heads-Up Display): Paints a live status dashboard directly on your chart so you know exactly why the bot is currently blocking trades.

Re: News Spread Widening Will Blow Your Stop

Posted: Sat Sep 26, 2026 6:54 pm
by PTScalper
Ctrader ver2.00 Pro

Code: Select all

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ProExecutionFilter : Robot
    {
        // --- 1. News Window Parameters ---
        [Parameter("Enable Time Filters", DefaultValue = true, Group = "Time Windows (UTC)")]
        public bool EnableTimeFilters { get; set; }

        [Parameter("Event 1 Start (HH:MM)", DefaultValue = "13:25", Group = "Time Windows (UTC)")]
        public string Event1Start { get; set; }
        [Parameter("Event 1 End (HH:MM)", DefaultValue = "13:45", Group = "Time Windows (UTC)")]
        public string Event1End { get; set; }

        [Parameter("Event 2 Start (HH:MM)", DefaultValue = "18:55", Group = "Time Windows (UTC)")]
        public string Event2Start { get; set; }
        [Parameter("Event 2 End (HH:MM)", DefaultValue = "19:15", Group = "Time Windows (UTC)")]
        public string Event2End { get; set; }

        // --- 2. Microstructure / Liquidity Parameters ---
        [Parameter("Max Allowed Spread (Pips)", DefaultValue = 2.0, Group = "Liquidity Guard")]
        public double MaxSpreadPips { get; set; }

        [Parameter("Max Volatility Multiplier (ATR)", DefaultValue = 3.0, Group = "Liquidity Guard")]
        public double VolMultiplier { get; set; }

        // --- 3. Pre-News Risk Management ---
        [Parameter("Pre-News Action", DefaultValue = RiskAction.MoveToBreakEven, Group = "Risk Management")]
        public RiskAction PreNewsAction { get; set; }

        public enum RiskAction
        {
            None,
            MoveToBreakEven,
            CloseAllPositions
        }

        private AverageTrueRange _atr;
        private TimeSpan _e1Start, _e1End, _e2Start, _e2End;

        protected override void OnStart()
        {
            // Initialize ATR for price-action volatility checks
            _atr = Indicators.AverageTrueRange(14, MovingAverageType.WilderSmoothing);
            
            // Parse HH:MM time strings safely into TimeSpan objects
            TimeSpan.TryParse(Event1Start, out _e1Start);
            TimeSpan.TryParse(Event1End, out _e1End);
            TimeSpan.TryParse(Event2Start, out _e2Start);
            TimeSpan.TryParse(Event2End, out _e2End);

            // Update the on-chart HUD every second
            Timer.Start(1); 
        }

        protected override void OnTick()
        {
            // 1. Manage open positions if we enter a blackout zone
            if (IsNewsWindowActive())
            {
                ExecutePreNewsRiskManagement();
            }

            // 2. Integration Example: Your strategy entry logic goes here
            // if (IsMarketSafeToTrade() && MyMovingAverageCrossed()) 
            // { 
            //     ExecuteTrade(); 
            // }
        }

        protected override void OnTimer()
        {
            UpdateHeadsUpDisplay();
        }

        // --- Core Filter Logic ---
        public bool IsMarketSafeToTrade()
        {
            // The Holy Trinity of execution safety: Normal Spread, Normal Volatility, Clear Schedule
            bool isSpreadSafe = (Symbol.Spread / Symbol.PipSize) <= MaxSpreadPips;
            bool isVolSafe = !IsAbnormalVolatility();
            bool isTimeSafe = !IsNewsWindowActive();

            return isSpreadSafe && isVolSafe && isTimeSafe;
        }

        private bool IsNewsWindowActive()
        {
            if (!EnableTimeFilters) return false;

            TimeSpan now = Server.Time.TimeOfDay;
            bool inEvent1 = now >= _e1Start && now <= _e1End;
            bool inEvent2 = now >= _e2Start && now <= _e2End;

            return inEvent1 || inEvent2;
        }

        private bool IsAbnormalVolatility()
        {
            if (Bars.Count < 2) return false;
            
            // Measure the last closed candle to prevent mid-tick false positives
            double candleSize = Bars.HighPrices.Last(1) - Bars.LowPrices.Last(1);
            return candleSize > (_atr.Result.Last(1) * VolMultiplier);
        }

        // --- Risk Management ---
        private void ExecutePreNewsRiskManagement()
        {
            // Only manage positions opened by this specific instance of the bot
            var myPositions = Positions.FindAll(Label, SymbolName);

            foreach (var pos in myPositions)
            {
                if (PreNewsAction == RiskAction.CloseAllPositions)
                {
                    ClosePosition(pos);
                }
                else if (PreNewsAction == RiskAction.MoveToBreakEven)
                {
                    // Move stop loss to entry price ONLY if we are safely in profit
                    if (pos.GrossProfit > 0 && pos.StopLoss != pos.EntryPrice)
                    {
                        ModifyPosition(pos, pos.EntryPrice, pos.TakeProfit);
                    }
                }
            }
        }

        // --- UI / Dashboard ---
        private void UpdateHeadsUpDisplay()
        {
            double currentSpread = Math.Round(Symbol.Spread / Symbol.PipSize, 1);
            bool safe = IsMarketSafeToTrade();
            
            string statusText = safe ? "MARKET SAFE" : "TRADING BLOCKED";
            Color statusColor = safe ? Color.MediumSeaGreen : Color.Crimson;

            string hud = $"--- PRO EXECUTION FILTER ---\n" +
                         $"Status: {statusText}\n" +
                         $"Spread: {currentSpread} pips (Max: {MaxSpreadPips})\n" +
                         $"News Window Active: {IsNewsWindowActive()}\n" +
                         $"Broker Time (UTC): {Server.Time:HH:mm:ss}";

            Chart.DrawStaticText("execution_hud", hud, VerticalAlignment.Top, HorizontalAlignment.Left, statusColor);
        }
    }
}

Re: News Spread Widening Will Blow Your Stop

Posted: Sat Sep 26, 2026 6:54 pm
by PTScalper
How to use this template

Instead of writing your strategy from scratch, simply place your entry conditions inside the OnTick() method, wrapped in the if (IsMarketSafeToTrade()) condition. The HUD will handle the visual feedback, and the internal logic will automatically handle spread detection and stop-loss modifications before the news drops. Make sure your configured times in the inputs match cTrader's UTC server time.