Advertisement IC Markets

Desk rule: one metal or one yen theme, not both oversized

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: Desk rule: one metal or one yen theme, not both oversized

Post by PTScalper »

Ctrader version 1.0

Code: Select all

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

namespace cAlgo
{
    public enum PrimaryTheme
    {
        XAU_Dominant,
        JPY_Dominant,
        FLAT
    }

    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ThematicRiskMatrix : Indicator
    {
        // =========================================================================
        // INPUTS
        // =========================================================================
        [Parameter("Gold Ticker", DefaultValue = "XAUUSD", Group = "1. Instruments")]
        public string TkrXAU { get; set; }

        [Parameter("Yen Ticker", DefaultValue = "USDJPY", Group = "1. Instruments")]
        public string TkrJPY { get; set; }

        [Parameter("Max Session Risk (%)", DefaultValue = 1.0, MinValue = 0.1, Step = 0.1, Group = "2. Risk Limits")]
        public double MaxSessionRiskPct { get; set; }

        [Parameter("Secondary Scalp Cap (%)", DefaultValue = 15.0, MinValue = 0.0, MaxValue = 50.0, Group = "2. Risk Limits")]
        public double SecondaryCapPct { get; set; }

        [Parameter("The 07:00 Fix", DefaultValue = PrimaryTheme.XAU_Dominant, Group = "3. Theme Selection")]
        public PrimaryTheme SessionTheme { get; set; }

        [Parameter("ATR Period", DefaultValue = 14, Group = "4. Volatility Engine")]
        public int AtrPeriod { get; set; }

        [Parameter("Stop Multiplier (ATR)", DefaultValue = 1.5, MinValue = 0.5, Step = 0.1, Group = "4. Volatility Engine")]
        public double AtrMultiplier { get; set; }

        // =========================================================================
        // INTERNAL VARIABLES & UI
        // =========================================================================
        private Symbol _symXau;
        private Symbol _symJpy;
        private Bars _barsXau;
        private Bars _barsJpy;
        private AverageTrueRange _atrXau;
        private AverageTrueRange _atrJpy;

        private Grid _riskGrid;
        private TextBlock _tbXauRole, _tbXauRisk, _tbXauLots;
        private TextBlock _tbJpyRole, _tbJpyRisk, _tbJpyLots;
        private TextBlock _tbTotalRisk;

        protected override void Initialize()
        {
            // Load external symbols and bars
            _symXau = Symbols.GetSymbol(TkrXAU);
            _symJpy = Symbols.GetSymbol(TkrJPY);
            _barsXau = MarketData.GetBars(TimeFrame, TkrXAU);
            _barsJpy = MarketData.GetBars(TimeFrame, TkrJPY);

            // Initialize cross-symbol ATRs
            _atrXau = Indicators.AverageTrueRange(_barsXau, AtrPeriod, MovingAverageType.Simple);
            _atrJpy = Indicators.AverageTrueRange(_barsJpy, AtrPeriod, MovingAverageType.Simple);

            DrawDashboard();
        }

        public override void Calculate(int index)
        {
            // Only update UI on the latest live tick
            if (!IsLastBar) return;

            UpdateDashboard();
        }

        // =========================================================================
        // VOLATILITY & CAPITAL MATH
        // =========================================================================
        private double CalculateMaxLots(Symbol sym, double riskCapital, double atrValue)
        {
            if (riskCapital <= 0 || atrValue <= 0 || sym.TickValue == 0 || sym.TickSize == 0)
                return 0.0;

            // Step 1: Stop distance in absolute price terms based on real-time ATR
            double stopDistancePrice = atrValue * AtrMultiplier;

            // Step 2: Dollar loss per 1 unit of volume (not lots)
            double lossPerUnit = stopDistancePrice * (sym.TickValue / sym.TickSize);
            if (lossPerUnit == 0) return 0;

            // Step 3: Allowed Volume (Units)
            double rawUnits = riskCapital / lossPerUnit;

            // Step 4: Broker normalization & Lot conversion
            double normalizedUnits = sym.NormalizeVolumeInUnits(rawUnits, RoundingMode.Down);
            return Math.Round(normalizedUnits / sym.LotSize, 2);
        }

        private void UpdateDashboard()
        {
            double equity = Account.Equity;
            double totalRiskCapital = equity * (MaxSessionRiskPct / 100.0);
            double primaryCap = totalRiskCapital * (1.0 - (SecondaryCapPct / 100.0));
            double secondaryCap = totalRiskCapital * (SecondaryCapPct / 100.0);

            double xauRisk = 0, jpyRisk = 0;
            string xauRole = "FLAT", jpyRole = "FLAT";
            Color xauColor = Color.Gray, jpyColor = Color.Gray;

            // Apply Sticky Note Rules
            if (SessionTheme == PrimaryTheme.XAU_Dominant)
            {
                xauRisk = primaryCap; xauRole = "DOMINANT"; xauColor = Color.Gold;
                jpyRisk = secondaryCap; jpyRole = "SCALP ONLY"; jpyColor = Color.DeepSkyBlue;
            }
            else if (SessionTheme == PrimaryTheme.JPY_Dominant)
            {
                jpyRisk = primaryCap; jpyRole = "DOMINANT"; jpyColor = Color.DeepSkyBlue;
                xauRisk = secondaryCap; xauRole = "SCALP ONLY"; xauColor = Color.Gold;
            }

            // Get Current ATR Values (using the last completed bar)
            double xauAtr = _atrXau.Result.Last(1);
            double jpyAtr = _atrJpy.Result.Last(1);

            // Output updates to UI
            _tbXauRole.Text = xauRole;
            _tbXauRole.ForegroundColor = xauColor;
            _tbXauRisk.Text = $"${Math.Round(xauRisk)}";
            _tbXauRisk.ForegroundColor = xauColor;
            _tbXauLots.Text = $"{CalculateMaxLots(_symXau, xauRisk, xauAtr):F2}";
            _tbXauLots.ForegroundColor = xauColor;

            _tbJpyRole.Text = jpyRole;
            _tbJpyRole.ForegroundColor = jpyColor;
            _tbJpyRisk.Text = $"${Math.Round(jpyRisk)}";
            _tbJpyRisk.ForegroundColor = jpyColor;
            _tbJpyLots.Text = $"{CalculateMaxLots(_symJpy, jpyRisk, jpyAtr):F2}";
            _tbJpyLots.ForegroundColor = jpyColor;

            _tbTotalRisk.Text = $"${Math.Round(xauRisk + jpyRisk)} ({MaxSessionRiskPct}%)";
        }

        // =========================================================================
        // UI CONSTRUCTION (WPF-Style Grid)
        // =========================================================================
        private void DrawDashboard()
        {
            var styleHeader = new Style();
            styleHeader.Set(ControlProperty.ForegroundColor, Color.Gray);
            styleHeader.Set(ControlProperty.FontWeight, FontWeight.Bold);
            styleHeader.Set(ControlProperty.Margin, new Thickness(5));

            var styleCell = new Style();
            styleCell.Set(ControlProperty.Margin, new Thickness(5));
            styleCell.Set(ControlProperty.FontWeight, FontWeight.Bold);

            _riskGrid = new Grid(5, 4)
            {
                BackgroundColor = Color.FromArgb(200, 15, 15, 15),
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Top,
                Margin = new Thickness(10)
            };

            var border = new Border
            {
                BorderColor = Color.FromArgb(100, 100, 100, 100),
                BorderThickness = new Thickness(1),
                Child = _riskGrid
            };

            // Headers
            _riskGrid.AddChild(new TextBlock { Text = "ASSET", Style = styleHeader }, 0, 0);
            _riskGrid.AddChild(new TextBlock { Text = "THEME ROLE", Style = styleHeader }, 0, 1);
            _riskGrid.AddChild(new TextBlock { Text = "CAPITAL ($)", Style = styleHeader }, 0, 2);
            _riskGrid.AddChild(new TextBlock { Text = "MAX LOTS", Style = styleHeader }, 0, 3);

            // XAU Row
            _riskGrid.AddChild(new TextBlock { Text = TkrXAU, Style = styleCell, ForegroundColor = Color.White }, 1, 0);
            _tbXauRole = new TextBlock { Style = styleCell };
            _tbXauRisk = new TextBlock { Style = styleCell };
            _tbXauLots = new TextBlock { Style = styleCell };
            _riskGrid.AddChild(_tbXauRole, 1, 1);
            _riskGrid.AddChild(_tbXauRisk, 1, 2);
            _riskGrid.AddChild(_tbXauLots, 1, 3);

            // JPY Row
            _riskGrid.AddChild(new TextBlock { Text = TkrJPY, Style = styleCell, ForegroundColor = Color.White }, 2, 0);
            _tbJpyRole = new TextBlock { Style = styleCell };
            _tbJpyRisk = new TextBlock { Style = styleCell };
            _tbJpyLots = new TextBlock { Style = styleCell };
            _riskGrid.AddChild(_tbJpyRole, 2, 1);
            _riskGrid.AddChild(_tbJpyRisk, 2, 2);
            _riskGrid.AddChild(_tbJpyLots, 2, 3);

            // Total Risk Row
            var tbTotalHeader = new TextBlock { Text = "SESSION TOTAL:", Style = styleCell, ForegroundColor = Color.LightGray };
            _tbTotalRisk = new TextBlock { Style = styleCell, ForegroundColor = Color.White };
            
            _riskGrid.AddChild(tbTotalHeader, 3, 0);
            _riskGrid.AddChild(_tbTotalRisk, 3, 2);

            // Operational Rule Warning
            var warningLabel = new TextBlock 
            { 
                Text = "DESK RULE: FLAT ALL RISK BEFORE CHANGING THEME", 
                ForegroundColor = Color.Red, 
                FontWeight = FontWeight.Bold,
                Margin = new Thickness(5),
                HorizontalAlignment = HorizontalAlignment.Center
            };
            
            _riskGrid.AddChild(warningLabel, 4, 0);
            
            // Span the warning text across all 4 columns (cTrader specific API implementation)
            // cTrader API doesn't support Grid.SetColumnSpan directly on AddChild like WPF, 
            // so we wrap it inside a specialized row or let it overflow naturally.
            // Using a StackPanel to handle the spanning smoothly.
            
            var mainPanel = new StackPanel { Orientation = Orientation.Vertical, BackgroundColor = Color.FromArgb(200, 15, 15, 15) };
            mainPanel.AddChild(_riskGrid);
            mainPanel.AddChild(new Border 
            { 
                BorderColor = Color.Red, 
                BorderThickness = new Thickness(0, 1, 0, 0), 
                Child = warningLabel 
            });

            border.Child = mainPanel;
            Chart.AddControl(border);
        }
    }
}
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: Desk rule: one metal or one yen theme, not both oversized

Post by PTScalper »

Key cTrader Advantages in this Build:

True Cross-Symbol Analysis: Using MarketData.GetBars(TimeFrame, TkrXAU) allows cTrader to natively pull exact multi-timeframe ATRs for other assets without the buggy "waiting for tick data" issues commonly seen in MT4/MT5.

NormalizeVolumeInUnits: This cTrader-specific API method completely removes the need to manually calculate broker-specific Min Lot, Max Lot, and Lot Steps. You feed it a raw dollar-risk derived volume, and it automatically snaps it to a valid, broker-compliant lot size using RoundingMode.Down to ensure you never violate your risk cap.

UI StackPanel: The UI is anchored cleanly to the chart without drawing on top of your candles, and resizes dynamically.
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: Desk rule: one metal or one yen theme, not both oversized

Post by PTScalper »

To elevate this from a static calculator to a Tier-1 Institutional Desk Tool, we must cross the gap from theoretical limits to live exposure tracking.

On a professional desk, a risk matrix doesn't just tell you what you can trade; it monitors what you are actually holding in real-time, compares it to your limits, and flags breaches.

Here is the cTrader "Pro" Upgrade.

The Upgrades:

Live Book Sync: It actively reads your open positions (Positions.FindAll) and compares your Current Lots vs. your Allowed Lots.

Risk Utilization Metrics: Calculates exactly what percentage of your thematic budget is currently deployed.

Interactive UI Controls: You no longer need to open the indicator settings to change your theme. You click interactive buttons directly on the chart to declare your 07:00 intent.

Breach Alerts: If you exceed the fractional cap on your secondary theme, the HUD flashes red, signaling a risk mandate violation.
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: Desk rule: one metal or one yen theme, not both oversized

Post by PTScalper »

The Institutional cTrader Code (C#)

Save this as a new Indicator named InstitutionalRiskMatrix.

Code: Select all

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

namespace cAlgo
{
    public enum PrimaryTheme { XAU_Dominant, JPY_Dominant, FLAT }

    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class InstitutionalRiskMatrix : Indicator
    {
        // =========================================================================
        // RISK MANDATE INPUTS
        // =========================================================================
        [Parameter("Gold Ticker", DefaultValue = "XAUUSD", Group = "1. Instruments")]
        public string TkrXAU { get; set; }

        [Parameter("Yen Ticker", DefaultValue = "USDJPY", Group = "1. Instruments")]
        public string TkrJPY { get; set; }

        [Parameter("Max Session Risk (%)", DefaultValue = 1.0, MinValue = 0.1, Step = 0.1, Group = "2. Risk Mandate")]
        public double MaxSessionRiskPct { get; set; }

        [Parameter("Secondary Scalp Cap (%)", DefaultValue = 15.0, MinValue = 0.0, MaxValue = 50.0, Group = "2. Risk Mandate")]
        public double SecondaryCapPct { get; set; }

        [Parameter("ATR Period", DefaultValue = 14, Group = "3. Volatility Engine")]
        public int AtrPeriod { get; set; }

        [Parameter("Stop Multiplier (ATR)", DefaultValue = 1.5, MinValue = 0.5, Step = 0.1, Group = "3. Volatility Engine")]
        public double AtrMultiplier { get; set; }

        // =========================================================================
        // INTERNAL STATE & UI COMPONENTS
        // =========================================================================
        private Symbol _symXau, _symJpy;
        private Bars _barsXau, _barsJpy;
        private AverageTrueRange _atrXau, _atrJpy;
        private PrimaryTheme _currentTheme = PrimaryTheme.FLAT;

        // UI Elements
        private TextBlock _tbXauCurrent, _tbXauLimit, _tbXauUtil;
        private TextBlock _tbJpyCurrent, _tbJpyLimit, _tbJpyUtil;
        private TextBlock _tbTotalRisk;
        private Button _btnThemeXau, _btnThemeJpy, _btnThemeFlat;
        private Border _mainBorder;

        protected override void Initialize()
        {
            _symXau = Symbols.GetSymbol(TkrXAU);
            _symJpy = Symbols.GetSymbol(TkrJPY);
            _barsXau = MarketData.GetBars(TimeFrame, TkrXAU);
            _barsJpy = MarketData.GetBars(TimeFrame, TkrJPY);
            
            _atrXau = Indicators.AverageTrueRange(_barsXau, AtrPeriod, MovingAverageType.Simple);
            _atrJpy = Indicators.AverageTrueRange(_barsJpy, AtrPeriod, MovingAverageType.Simple);

            // Hook into live position changes to update instantly
            Positions.Opened += OnPositionChanged;
            Positions.Closed += OnPositionChanged;
            Positions.Modified += OnPositionChanged;

            DrawInteractiveDashboard();
            UpdateDashboard();
        }

        public override void Calculate(int index)
        {
            if (IsLastBar) UpdateDashboard();
        }

        private void OnPositionChanged(PositionEventArgs args)
        {
            UpdateDashboard();
        }

        // =========================================================================
        // CORE RISK MATH & LIVE BOOK SYNC
        // =========================================================================
        private double CalculateMaxLots(Symbol sym, double riskCapital, double atrValue)
        {
            if (riskCapital <= 0 || atrValue <= 0 || sym.TickValue == 0 || sym.TickSize == 0) return 0.0;
            double stopDistancePrice = atrValue * AtrMultiplier;
            double lossPerUnit = stopDistancePrice * (sym.TickValue / sym.TickSize);
            if (lossPerUnit == 0) return 0;
            double rawUnits = riskCapital / lossPerUnit;
            double normalizedUnits = sym.NormalizeVolumeInUnits(rawUnits, RoundingMode.Down);
            return Math.Round(normalizedUnits / sym.LotSize, 2);
        }

        private double GetOpenLots(string ticker)
        {
            var volumeInUnits = Positions.Where(p => p.SymbolName == ticker).Sum(p => p.VolumeInUnits);
            var sym = Symbols.GetSymbol(ticker);
            return Math.Round(volumeInUnits / sym.LotSize, 2);
        }

        private void UpdateDashboard()
        {
            double equity = Account.Equity;
            double totalRiskCapital = equity * (MaxSessionRiskPct / 100.0);
            double primaryCap = totalRiskCapital * (1.0 - (SecondaryCapPct / 100.0));
            double secondaryCap = totalRiskCapital * (SecondaryCapPct / 100.0);

            double xauRisk = 0, jpyRisk = 0;

            if (_currentTheme == PrimaryTheme.XAU_Dominant)
            {
                xauRisk = primaryCap; jpyRisk = secondaryCap;
                _mainBorder.BorderColor = Color.Gold;
            }
            else if (_currentTheme == PrimaryTheme.JPY_Dominant)
            {
                jpyRisk = primaryCap; xauRisk = secondaryCap;
                _mainBorder.BorderColor = Color.DeepSkyBlue;
            }
            else
            {
                _mainBorder.BorderColor = Color.Gray;
            }

            double xauAtr = _atrXau.Result.Last(1);
            double jpyAtr = _atrJpy.Result.Last(1);

            double xauMaxLots = CalculateMaxLots(_symXau, xauRisk, xauAtr);
            double jpyMaxLots = CalculateMaxLots(_symJpy, jpyRisk, jpyAtr);

            double xauOpenLots = GetOpenLots(TkrXAU);
            double jpyOpenLots = GetOpenLots(TkrJPY);

            double xauUtilPct = xauMaxLots > 0 ? (xauOpenLots / xauMaxLots) * 100 : (xauOpenLots > 0 ? 999 : 0);
            double jpyUtilPct = jpyMaxLots > 0 ? (jpyOpenLots / jpyMaxLots) * 100 : (jpyOpenLots > 0 ? 999 : 0);

            // Update UI Fields
            UpdateRow(_tbXauCurrent, _tbXauLimit, _tbXauUtil, xauOpenLots, xauMaxLots, xauUtilPct);
            UpdateRow(_tbJpyCurrent, _tbJpyLimit, _tbJpyUtil, jpyOpenLots, jpyMaxLots, jpyUtilPct);

            _tbTotalRisk.Text = $"${Math.Round(xauRisk + jpyRisk):N0} ({MaxSessionRiskPct}% Eq)";
            
            // Button States
            _btnThemeXau.BackgroundColor = _currentTheme == PrimaryTheme.XAU_Dominant ? Color.Gold : Color.FromArgb(50, 50, 50);
            _btnThemeXau.ForegroundColor = _currentTheme == PrimaryTheme.XAU_Dominant ? Color.Black : Color.Gray;
            _btnThemeJpy.BackgroundColor = _currentTheme == PrimaryTheme.JPY_Dominant ? Color.DeepSkyBlue : Color.FromArgb(50, 50, 50);
            _btnThemeJpy.ForegroundColor = _currentTheme == PrimaryTheme.JPY_Dominant ? Color.Black : Color.Gray;
        }

        private void UpdateRow(TextBlock tbCurrent, TextBlock tbLimit, TextBlock tbUtil, double open, double limit, double utilPct)
        {
            tbCurrent.Text = $"{open:F2} Lots";
            tbLimit.Text = $"{limit:F2} Lots";
            tbUtil.Text = utilPct > 100 ? "BREACH!" : $"{utilPct:F0}%";

            Color statusColor = utilPct > 100 ? Color.Red : (utilPct > 80 ? Color.DarkOrange : Color.LimeGreen);
            if (utilPct == 0) statusColor = Color.Gray;

            tbCurrent.ForegroundColor = statusColor;
            tbUtil.ForegroundColor = statusColor;
        }

        // =========================================================================
        // INTERACTIVE UI CONSTRUCTION
        // =========================================================================
        private void DrawInteractiveDashboard()
        {
            var grid = new Grid(4, 4) { BackgroundColor = Color.FromArgb(220, 10, 10, 10), Margin = new Thickness(10) };
            
            var styleHeader = new Style();
            styleHeader.Set(ControlProperty.ForegroundColor, Color.DimGray);
            styleHeader.Set(ControlProperty.FontWeight, FontWeight.Bold);
            styleHeader.Set(ControlProperty.Margin, new Thickness(5, 5, 15, 5));

            var styleCell = new Style();
            styleCell.Set(ControlProperty.Margin, new Thickness(5, 5, 15, 5));
            styleCell.Set(ControlProperty.FontWeight, FontWeight.Bold);

            // Headers
            grid.AddChild(new TextBlock { Text = "ASSET", Style = styleHeader }, 0, 0);
            grid.AddChild(new TextBlock { Text = "OPEN (LIVE)", Style = styleHeader }, 0, 1);
            grid.AddChild(new TextBlock { Text = "LIMIT (MAX)", Style = styleHeader }, 0, 2);
            grid.AddChild(new TextBlock { Text = "UTILIZATION", Style = styleHeader }, 0, 3);

            // XAU Row
            grid.AddChild(new TextBlock { Text = TkrXAU, Style = styleCell, ForegroundColor = Color.Gold }, 1, 0);
            _tbXauCurrent = new TextBlock { Style = styleCell };
            _tbXauLimit = new TextBlock { Style = styleCell, ForegroundColor = Color.White };
            _tbXauUtil = new TextBlock { Style = styleCell };
            grid.AddChild(_tbXauCurrent, 1, 1); grid.AddChild(_tbXauLimit, 1, 2); grid.AddChild(_tbXauUtil, 1, 3);

            // JPY Row
            grid.AddChild(new TextBlock { Text = TkrJPY, Style = styleCell, ForegroundColor = Color.DeepSkyBlue }, 2, 0);
            _tbJpyCurrent = new TextBlock { Style = styleCell };
            _tbJpyLimit = new TextBlock { Style = styleCell, ForegroundColor = Color.White };
            _tbJpyUtil = new TextBlock { Style = styleCell };
            grid.AddChild(_tbJpyCurrent, 2, 1); grid.AddChild(_tbJpyLimit, 2, 2); grid.AddChild(_tbJpyUtil, 2, 3);

            // Bottom Footer
            grid.AddChild(new TextBlock { Text = "TOTAL CAP:", Style = styleCell, ForegroundColor = Color.DimGray }, 3, 0);
            _tbTotalRisk = new TextBlock { Style = styleCell, ForegroundColor = Color.White };
            grid.AddChild(_tbTotalRisk, 3, 1);

            // Interactive Theme Selectors
            var controlPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(10, 0, 10, 10), HorizontalAlignment = HorizontalAlignment.Center };
            
            _btnThemeXau = new Button { Text = "SET XAU THEME", Margin = new Thickness(0, 0, 5, 0), Width = 110, FontWeight = FontWeight.Bold };
            _btnThemeXau.Click += args => { _currentTheme = PrimaryTheme.XAU_Dominant; UpdateDashboard(); };
            
            _btnThemeJpy = new Button { Text = "SET JPY THEME", Margin = new Thickness(5, 0, 5, 0), Width = 110, FontWeight = FontWeight.Bold };
            _btnThemeJpy.Click += args => { _currentTheme = PrimaryTheme.JPY_Dominant; UpdateDashboard(); };

            _btnThemeFlat = new Button { Text = "FLAT MODE", Margin = new Thickness(5, 0, 0, 0), Width = 90, BackgroundColor = Color.Crimson, ForegroundColor = Color.White, FontWeight = FontWeight.Bold };
            _btnThemeFlat.Click += args => { _currentTheme = PrimaryTheme.FLAT; UpdateDashboard(); };

            controlPanel.AddChild(_btnThemeXau);
            controlPanel.AddChild(_btnThemeJpy);
            controlPanel.AddChild(_btnThemeFlat);

            var mainPanel = new StackPanel { Orientation = Orientation.Vertical, BackgroundColor = Color.FromArgb(230, 15, 15, 15) };
            
            var header = new TextBlock { Text = "DESK RULE: THEMATIC RISK OVERLAP", Margin = new Thickness(10, 10, 10, 0), FontWeight = FontWeight.ExtraBold, ForegroundColor = Color.White, HorizontalAlignment = HorizontalAlignment.Center };
            
            mainPanel.AddChild(header);
            mainPanel.AddChild(grid);
            mainPanel.AddChild(controlPanel);

            _mainBorder = new Border { BorderColor = Color.Gray, BorderThickness = new Thickness(2), Child = mainPanel, HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Top, Margin = new Thickness(20) };

            Chart.AddControl(_mainBorder);
        }
    }
}
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: Desk rule: one metal or one yen theme, not both oversized

Post by PTScalper »

Why this is a True Professional Framework:

Interactive Control Panel: Instead of digging into indicator settings, you have a physical switchboard on your chart. Clicking "SET XAU THEME" instantly updates the border color to Gold and recalculates your lot allowances dynamically.

The "Live Sync" Engine: The code hooks into Positions.Opened and Positions.Closed. If you open a 0.50 lot position on Gold, the dashboard updates instantly—without waiting for the next candle to form.

Breach Enforcement: The dashboard calculates Utilization (%).

0% - 80%: Your live position text is Green (Safe).

80% - 100%: The text turns Orange (Warning: Approaching Mandate Limit).

> 100%: The text turns Red, the Utilization box screams "BREACH!".

The "FLAT MODE" Kill Switch: If you realize you are emotionally double-dipping, clicking the Crimson "FLAT MODE" button instantly zeroes out your allowed thematic risk for the session, forcing you to step back and evaluate your book.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply