Page 2 of 2

Re: Friday flat-by rule: my cut-off clock and why I keep it rigid

Posted: Mon Sep 14, 2026 9:05 pm
by PTScalper
Moving this protocol to cTrader is where the system truly reaches its enterprise potential. cTrader uses C# (.NET) and has a built-in UI framework that allows us to build native, WPF-style control panels directly onto the chart.

Instead of relying on clunky text labels like MT4/MT5, we can build a proper docked visual HUD that looks like a native part of the trading platform. Like the MetaTrader versions, this relies on a precise 1-second timer rather than price ticks, ensuring your protocol enforces exactly on time even if Friday volume has completely dried up.

Here is the complete C# code for the cTrader Automate environment.

cTrader: Enterprise Friday Risk Protocol

How to Install:

1.) Open cTrader and go to the Automate tab on the left menu.

2.) Click the + New button next to Indicators.

3.) Name it FridayRiskManager.

4.) Delete all the default code, paste the C# code below, and click Build (the hammer icon at the top).

Re: Friday flat-by rule: my cut-off clock and why I keep it rigid

Posted: Mon Sep 14, 2026 9:05 pm
by PTScalper
Ctrader version:

Code: Select all

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

namespace cAlgo
{
    public enum WeekStatus
    {
        Neutral,
        Green_Protect,
        Red_StopDigging
    }

    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class FridayRiskManager : Indicator
    {
        [Parameter("Week Status", DefaultValue = WeekStatus.Neutral, Group = "Risk Desk Parameters")]
        public WeekStatus CurrentWeekStatus { get; set; }

        [Parameter("FX Cut-off Hour", DefaultValue = 17, MinValue = 0, MaxValue = 23, Group = "Cut-offs (BROKER TIME)")]
        public int FxCutoffHour { get; set; }

        [Parameter("FX Cut-off Min", DefaultValue = 30, MinValue = 0, MaxValue = 59, Group = "Cut-offs (BROKER TIME)")]
        public int FxCutoffMin { get; set; }

        [Parameter("XAU Cut-off Hour", DefaultValue = 17, MinValue = 0, MaxValue = 23, Group = "Cut-offs (BROKER TIME)")]
        public int XauCutoffHour { get; set; }

        [Parameter("XAU Cut-off Min", DefaultValue = 0, MinValue = 0, MaxValue = 59, Group = "Cut-offs (BROKER TIME)")]
        public int XauCutoffMin { get; set; }

        [Parameter("Warning Window (Mins)", DefaultValue = 30, MinValue = 5, Group = "Cut-offs (BROKER TIME)")]
        public int WarningMins { get; set; }

        // UI Elements
        private StackPanel _hud;
        private TextBlock _txtAsset, _txtCutoff, _txtStatus;
        private TextBlock _txtL4, _txtR4, _txtL5, _txtR5, _txtL6, _txtR6, _txtL7, _txtR7;
        
        // Colors
        private Color _clrBg = Color.FromArgb(240, 30, 34, 45); // Dark Slate
        private Color _clrBorder = Color.FromArgb(255, 67, 70, 81);
        private Color _clrLabel = Color.FromArgb(255, 138, 145, 165);
        private Color _clrWhite = Color.White;
        private Color _clrGreen = Color.FromHex("#00e676");
        private Color _clrOrange = Color.FromHex("#ffaa00");
        private Color _clrRed = Color.FromHex("#ff5252");

        // Logic variables
        private bool _isMetals;
        private int _baseHour, _baseMin, _protectFactor;
        private int _currentState = -1; // 0=Normal, 1=Warning, 2=Locked
        private ChartRectangle _bgRectangle;

        protected override void Initialize()
        {
            // Auto-detect metals (stricter liquidity profile)
            _isMetals = Symbol.Name.Contains("XAU") || Symbol.Name.Contains("GOLD");
            _baseHour = _isMetals ? XauCutoffHour : FxCutoffHour;
            _baseMin = _isMetals ? XauCutoffMin : FxCutoffMin;
            
            // Context logic
            _protectFactor = (CurrentWeekStatus == WeekStatus.Green_Protect) ? 15 : 0;

            // Build UI HUD
            BuildHUD();
            
            // Start precision timer (runs every 1 second)
            Timer.Start(TimeSpan.FromSeconds(1));
        }

        public override void Calculate(int index)
        {
            // Calculation logic is bypassed; system relies entirely on OnTimer for accuracy.
        }

        protected override void OnTimer()
        {
            DateTime now = Server.Time;

            if (now.DayOfWeek != DayOfWeek.Friday)
            {
                _hud.IsVisible = false;
                if (_bgRectangle != null) _bgRectangle.IsVisible = false;
                return;
            }

            _hud.IsVisible = true;

            // Compute precise cutoff time for today
            DateTime cutoff = new DateTime(now.Year, now.Month, now.Day, _baseHour, _baseMin, 0).AddMinutes(-_protectFactor);
            TimeSpan remaining = cutoff - now;

            int prevState = _currentState;

            if (remaining.TotalSeconds <= 0)
                _currentState = 2; // Locked
            else if (remaining.TotalMinutes <= WarningMins)
                _currentState = 1; // Warning
            else
                _currentState = 0; // Active

            // Trigger sound alerts on transition
            if (_currentState == 1 && prevState == 0) Notifications.PlaySound(SoundType.Timeout);
            if (_currentState == 2 && prevState == 1) Notifications.PlaySound(SoundType.Error);

            // Update visuals
            UpdateHUD(remaining, cutoff);
            UpdateBackground(now, cutoff);
        }

        private void UpdateHUD(TimeSpan remaining, DateTime cutoff)
        {
            _txtAsset.Text = _isMetals ? "METALS (XAU)" : "FX MAJOR";
            _txtCutoff.Text = cutoff.ToString("HH:mm") + " BRK";

            if (_currentState == 2)
            {
                _txtStatus.Text = "LOCKED";
                _txtStatus.ForegroundColor = _clrRed;

                _txtL4.Text = "PROTOCOL";        _txtL4.ForegroundColor = _clrRed;
                _txtR4.Text = "HARD BLACKOUT";   _txtR4.ForegroundColor = _clrRed;

                _txtL5.Text = "✅ ALLOWED";      _txtL5.ForegroundColor = _clrGreen;
                _txtR5.Text = "Manage Open, Log Day"; _txtR5.ForegroundColor = _clrWhite;

                _txtL6.Text = "❌ DENIED";       _txtL6.ForegroundColor = _clrRed;
                _txtR6.Text = "Revenge Size, Ext."; _txtR6.ForegroundColor = _clrWhite;

                _txtL7.Text = "RATIONALE";       _txtL7.ForegroundColor = _clrLabel;
                _txtR7.Text = CurrentWeekStatus == WeekStatus.Green_Protect ? "Protect the Week" : "Stop Digging"; 
                _txtR7.ForegroundColor = _clrWhite;
            }
            else
            {
                _txtStatus.Text = _currentState == 1 ? "WINDING DOWN" : "ACTIVE";
                _txtStatus.ForegroundColor = _currentState == 1 ? _clrOrange : _clrGreen;

                _txtL4.Text = "TIME REMAINING";  _txtL4.ForegroundColor = _clrLabel;
                _txtR4.Text = $"{(int)Math.Max(0, remaining.TotalMinutes)} MIN"; 
                _txtR4.ForegroundColor = _txtStatus.ForegroundColor;

                _txtL5.Text = "WEEK CONTEXT";    _txtL5.ForegroundColor = _clrLabel;
                _txtR5.Text = CurrentWeekStatus.ToString().Replace("_", " "); 
                _txtR5.ForegroundColor = _clrWhite;

                _txtL6.Text = ""; _txtR6.Text = "";
                _txtL7.Text = ""; _txtR7.Text = "";
            }
        }

        private void UpdateBackground(DateTime now, DateTime cutoff)
        {
            if (_currentState == 0)
            {
                if (_bgRectangle != null) _bgRectangle.IsVisible = false;
                return;
            }

            string rectName = "FriRiskBG";
            double topY = Chart.TopY;
            double botY = Chart.BottomY;
            
            // Expand rectangle into the future so it paints the right edge
            DateTime endOfDay = new DateTime(now.Year, now.Month, now.Day, 23, 59, 59);

            if (_bgRectangle == null)
            {
                _bgRectangle = Chart.DrawRectangle(rectName, now.AddHours(-1), botY, endOfDay, topY, Color.Transparent);
                _bgRectangle.IsFilled = true;
            }

            _bgRectangle.IsVisible = true;
            _bgRectangle.Time2 = endOfDay;
            _bgRectangle.Y1 = botY;
            _bgRectangle.Y2 = topY;
            
            // Alpha blended colors overlay the chart smoothly
            _bgRectangle.Color = _currentState == 2 
                ? Color.FromArgb(60, 255, 0, 0)     // Translucent Red
                : Color.FromArgb(60, 255, 140, 0);  // Translucent Orange
        }

        private void BuildHUD()
        {
            _hud = new StackPanel 
            {
                Orientation = Orientation.Vertical,
                BackgroundColor = _clrBg,
                Margin = new Thickness(15),
                Width = 260
            };

            var border = new Border 
            {
                BorderColor = _clrBorder,
                BorderThickness = new Thickness(1),
                Child = _hud,
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Top
            };

            // Initialize Rows
            _txtAsset = AddRow(_hud, "ASSET PROFILE");
            _txtCutoff = AddRow(_hud, "DESK CUT-OFF");
            _txtStatus = AddRow(_hud, "STATUS");
            _txtR4 = AddRow(_hud, "", out _txtL4);
            _txtR5 = AddRow(_hud, "", out _txtL5);
            _txtR6 = AddRow(_hud, "", out _txtL6);
            _txtR7 = AddRow(_hud, "", out _txtL7);

            Chart.AddControl(border);
        }

        private TextBlock AddRow(StackPanel parent, string leftText)
        {
            TextBlock dummyLabel;
            return AddRow(parent, leftText, out dummyLabel);
        }

        private TextBlock AddRow(StackPanel parent, string leftText, out TextBlock leftLabelBlock)
        {
            var grid = new Grid { Margin = new Thickness(10, 5, 10, 5) };
            grid.AddColumn(GridLength.Auto);
            grid.AddColumn(GridLength.Star);

            leftLabelBlock = new TextBlock 
            {
                Text = leftText,
                ForegroundColor = _clrLabel,
                FontWeight = FontWeight.Bold,
                FontSize = 11
            };

            var rightValueBlock = new TextBlock 
            {
                Text = "-",
                ForegroundColor = _clrWhite,
                FontWeight = FontWeight.ExtraBold,
                FontSize = 11,
                HorizontalAlignment = HorizontalAlignment.Right
            };

            grid.AddChild(leftLabelBlock, 0, 0);
            grid.AddChild(rightValueBlock, 0, 1);
            
            parent.AddChild(grid);
            return rightValueBlock;
        }
    }
}

Re: Friday flat-by rule: my cut-off clock and why I keep it rigid

Posted: Mon Sep 14, 2026 9:06 pm
by PTScalper
Why this shines in cTrader

Native GUI Integration: Unlike MT4/MT5 where you have to calculate X and Y pixel distances to fake a panel, this script uses cTrader's native .NET StackPanel and Grid classes. The HUD acts like a built-in cTrader menu. It is perfectly aligned, cleanly boxed, and floats smoothly in the top right corner.

Translucent Alpha-Blending: The warning/locked background colors (_bgRectangle) use alpha-channels (Color.FromArgb). This creates a tinted "glass" overlay over your candlesticks (instead of MT4’s solid opaque boxes), allowing you to clearly see price action without the colors blinding you.

Time-Safe Background Extension: In thin conditions, cTrader can sometimes compress the right margin of the chart. The code specifically anchors the visual cut-off block to stretch forward in time to 23:59, guaranteeing the red zone visually locks down the rest of your trading day.

Note: As with MT4/MT5, input the cut-off hours according to your cTrader Broker Time (Server Time) displayed at the bottom right corner of your platform, as the server handles executions.