Page 2 of 2

Re: Christmas week session map: when I simply do not trade

Posted: Mon Sep 28, 2026 9:55 pm
by PTScalper
Why this C# Implementation Shines

Responsive UI: Because it uses StackPanel and Border controls natively, the UI card scales perfectly and centers itself automatically. You don't have to fiddle with X and Y pixel coordinates if you resize the cTrader window.

Flawless Pip Calculation: Calculating pips in MT4/MT5 requires checking if the broker is 3-digit, 4-digit, or 5-digit. In cTrader, Symbol.PipSize is universally reliable regardless of whether you are looking at EURUSD, Gold (XAUUSD), or an Equity index.

Server Time Safety: The indicator specifically calls Server.Time instead of DateTime.Now. This ensures the script evaluates the true broker time (bypassing any timezone offsets or local PC clock issues) to determine if it is officially "the holiday window" in the market.

Re: Christmas week session map: when I simply do not trade

Posted: Mon Sep 28, 2026 9:56 pm
by PTScalper
Here is a production-grade implementation for cTrader.

This version is refactored to align with enterprise C# standards, separating state evaluation from UI rendering, optimizing the tick-level processing, and upgrading the visuals to a modern, dark-mode terminal aesthetic.

It is also structured so the core logic can be cleanly extracted into a cBot if you decide to fully automate the execution lock-out in the future.

Re: Christmas week session map: when I simply do not trade

Posted: Mon Sep 28, 2026 9:56 pm
by PTScalper
MarketConditionMonitor.cs

Code: Select all

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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class MarketConditionMonitor : Indicator
    {
        #region Parameters

        [Parameter("Start Month", DefaultValue = 12, Group = "Holiday Lockdown", MinValue = 1, MaxValue = 12)]
        public int StartMonth { get; set; }

        [Parameter("Start Day", DefaultValue = 20, Group = "Holiday Lockdown", MinValue = 1, MaxValue = 31)]
        public int StartDay { get; set; }

        [Parameter("End Month", DefaultValue = 1, Group = "Holiday Lockdown", MinValue = 1, MaxValue = 12)]
        public int EndMonth { get; set; }

        [Parameter("End Day", DefaultValue = 3, Group = "Holiday Lockdown", MinValue = 1, MaxValue = 31)]
        public int EndDay { get; set; }

        [Parameter("Max Spread (Pips)", DefaultValue = 2.5, Group = "Liquidity Monitoring", MinValue = 0.1)]
        public double MaxSpreadPips { get; set; }

        #endregion

        #region Constants & Fields

        // Professional Dark Theme Palette
        private const string ColorBackground = "#E6111111"; // 90% Opacity Dark Charcoal
        private const string ColorBorderHoliday = "#FFD32F2F"; // Material Red 700
        private const string ColorBorderSpread = "#FFFBC02D";  // Material Yellow 700
        private const string ColorTextPrimary = "#FFFFFFFF";
        private const string ColorTextSecondary = "#FFB0B0B0";
        private const string ColorSpreadSafe = "#FF81C784";    // Material Green 300
        private const string ColorSpreadDanger = "#FFE57373";  // Material Red 300

        private Border _mainContainer;
        private TextBlock _lblHeader;
        private TextBlock _lblSubtext;
        private TextBlock _lblSpreadData;

        private MarketState _currentState = MarketState.Normal;
        
        // Cache to prevent redundant UI thread updates
        private double _lastRenderedSpread = -1;
        private MarketState _lastRenderedState = MarketState.Normal;

        private enum MarketState
        {
            Normal,
            SpreadBlown,
            HolidayLockdown
        }

        #endregion

        #region Initialization

        protected override void Initialize()
        {
            ConstructUI();
            EvaluateMarketConditions();
        }

        private void ConstructUI()
        {
            _lblHeader = new TextBlock
            {
                TextAlignment = TextAlignment.Center,
                FontWeight = FontWeight.ExtraBold,
                FontSize = 14,
                ForegroundColor = Color.FromHex(ColorTextPrimary),
                Margin = new Thickness(0, 0, 0, 4)
            };

            _lblSubtext = new TextBlock
            {
                TextAlignment = TextAlignment.Center,
                FontWeight = FontWeight.Normal,
                FontSize = 11,
                ForegroundColor = Color.FromHex(ColorTextSecondary),
                Margin = new Thickness(0, 0, 0, 10)
            };

            _lblSpreadData = new TextBlock
            {
                TextAlignment = TextAlignment.Center,
                FontWeight = FontWeight.Bold,
                FontSize = 12
            };

            var layoutGrid = new StackPanel
            {
                Orientation = Orientation.Vertical,
                HorizontalAlignment = HorizontalAlignment.Center,
                Margin = new Thickness(20, 15, 20, 15)
            };

            layoutGrid.AddChild(_lblHeader);
            layoutGrid.AddChild(_lblSubtext);
            layoutGrid.AddChild(_lblSpreadData);

            _mainContainer = new Border
            {
                Child = layoutGrid,
                BackgroundColor = Color.FromHex(ColorBackground),
                BorderThickness = new Thickness(0, 3, 0, 0), // Top-heavy border for sleek tech look
                CornerRadius = 3,
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Top,
                Margin = new Thickness(0, 60, 0, 0),
                IsVisible = false 
            };

            Chart.AddControl(_mainContainer);
        }

        #endregion

        #region Core Logic

        public override void Calculate(int index)
        {
            // Abort processing for historical bars to preserve CPU cycles
            if (!IsLastIndex) return;

            EvaluateMarketConditions();
        }

        private void EvaluateMarketConditions()
        {
            double currentSpread = Math.Round((Symbol.Ask - Symbol.Bid) / Symbol.PipSize, 1);
            bool isHoliday = IsWithinHolidayWindow(Server.Time);
            bool isSpreadBlown = currentSpread >= MaxSpreadPips;

            MarketState newState = MarketState.Normal;

            // Hierarchy of states: Holiday lockdown supersedes a blown spread
            if (isHoliday)
                newState = MarketState.HolidayLockdown;
            else if (isSpreadBlown)
                newState = MarketState.SpreadBlown;

            // Update UI only on state change or significant spread change to prevent layout thrashing
            if (newState != _lastRenderedState || Math.Abs(currentSpread - _lastRenderedSpread) >= 0.1)
            {
                UpdateInterface(newState, currentSpread);
                
                _lastRenderedState = newState;
                _lastRenderedSpread = currentSpread;
            }
        }

        private bool IsWithinHolidayWindow(DateTime currentTime)
        {
            bool isPastStart = (currentTime.Month > StartMonth) || (currentTime.Month == StartMonth && currentTime.Day >= StartDay);
            bool isBeforeEnd = (currentTime.Month < EndMonth) || (currentTime.Month == EndMonth && currentTime.Day <= EndDay);

            return StartMonth > EndMonth 
                ? (isPastStart || isBeforeEnd) 
                : (isPastStart && isBeforeEnd);
        }

        #endregion

        #region UI Rendering

        private void UpdateInterface(MarketState state, double currentSpread)
        {
            if (state == MarketState.Normal)
            {
                _mainContainer.IsVisible = false;
                return;
            }

            _mainContainer.IsVisible = true;
            _lblSpreadData.Text = $"LIVE SPREAD: {currentSpread:F1} PIPS  |  MAX: {MaxSpreadPips:F1} PIPS";
            _lblSpreadData.ForegroundColor = currentSpread >= MaxSpreadPips 
                ? Color.FromHex(ColorSpreadDanger) 
                : Color.FromHex(ColorSpreadSafe);

            switch (state)
            {
                case MarketState.HolidayLockdown:
                    _mainContainer.BorderColor = Color.FromHex(ColorBorderHoliday);
                    _lblHeader.Text = "SYSTEM LOCKED: HOLIDAY CONDITIONS";
                    _lblSubtext.Text = "MARKET MICROSTRUCTURE COMPROMISED • JOURNAL ONLY";
                    break;

                case MarketState.SpreadBlown:
                    _mainContainer.BorderColor = Color.FromHex(ColorBorderSpread);
                    _lblHeader.Text = "WARNING: LIQUIDITY VACUUM DETECTED";
                    _lblSubtext.Text = "SPREAD EXCEEDS ACCEPTABLE EXECUTION THRESHOLD";
                    break;
            }
        }

        #endregion
    }
}

Re: Christmas week session map: when I simply do not trade

Posted: Mon Sep 28, 2026 9:56 pm
by PTScalper
Architectural Upgrades

State Machine Hierarchy: The indicator now evaluates states hierarchically (MarketState.Normal, SpreadBlown, HolidayLockdown). Holiday rules override everything else.

UI Thread Optimization: The EvaluateMarketConditions method caches the previous tick's state and spread. It only forces a UI repaint if the state changes or the spread moves by a full 0.1 pip increment, eliminating unnecessary layout thrashing.

Encapsulation: The UI construction, layout rendering, and mathematical evaluation are cleanly isolated into distinct regions.

Modern Aesthetics: Uses a top-heavy accent border (like modern trading terminals) against a 90% opacity charcoal background. The color palette relies on subdued Material Design hex codes rather than aggressive base colors, keeping it visible but clean.