Advertisement IC Markets

Inventory risk for gold specialists: weekly audit

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Inventory risk for gold specialists: weekly audit

Post by FTtrader »

cTrader Automate Implementation (C#)

Code: Select all

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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.EasternStandardTime, AccessRights = AccessRights.None)]
    public class GoldInventoryRiskAuditor : Indicator
    {
        // --- INPUTS: Risk Windows (New York Time) ---
        [Parameter("Tier-1 Start", DefaultValue = "08:25", Group = "Time Risk Zones")]
        public string Tier1StartStr { get; set; }
        [Parameter("Tier-1 End", DefaultValue = "08:45", Group = "Time Risk Zones")]
        public string Tier1EndStr { get; set; }

        [Parameter("Swap Start", DefaultValue = "16:45", Group = "Time Risk Zones")]
        public string SwapStartStr { get; set; }
        [Parameter("Swap End", DefaultValue = "17:15", Group = "Time Risk Zones")]
        public string SwapEndStr { get; set; }

        [Parameter("FOMC Start", DefaultValue = "13:55", Group = "Time Risk Zones")]
        public string FomcStartStr { get; set; }
        [Parameter("FOMC End", DefaultValue = "14:15", Group = "Time Risk Zones")]
        public string FomcEndStr { get; set; }

        // --- INPUTS: Correlation Tracking ---
        [Parameter("Correlation Lookback", DefaultValue = 20, Group = "Stacked Risk Matrix")]
        public int CorrLength { get; set; }
        [Parameter("Silver Symbol", DefaultValue = "XAGUSD", Group = "Stacked Risk Matrix")]
        public string SymbolAg { get; set; }
        [Parameter("USD/JPY Symbol", DefaultValue = "USDJPY", Group = "Stacked Risk Matrix")]
        public string SymbolUj { get; set; }
        [Parameter("Danger Threshold", DefaultValue = 0.80, Group = "Stacked Risk Matrix")]
        public double CorrThreshold { get; set; }

        // Core variables
        private Bars _barsAg;
        private Bars _barsUj;
        private TimeSpan _t1Start, _t1End, _swStart, _swEnd, _fmStart, _fmEnd;
        
        // UI and Alert State
        private Grid _auditBoard;
        private TextBlock _txtAgStatus;
        private TextBlock _txtUjStatus;
        private bool _wasAgDanger = false;
        private bool _wasUjDanger = false;

        protected override void Initialize()
        {
            // Parse Times
            _t1Start = TimeSpan.Parse(Tier1StartStr);
            _t1End = TimeSpan.Parse(Tier1EndStr);
            _swStart = TimeSpan.Parse(SwapStartStr);
            _swEnd = TimeSpan.Parse(SwapEndStr);
            _fmStart = TimeSpan.Parse(FomcStartStr);
            _fmEnd = TimeSpan.Parse(FomcEndStr);

            // Fetch Secondary Market Data
            _barsAg = MarketData.GetBars(TimeFrame, SymbolAg);
            _barsUj = MarketData.GetBars(TimeFrame, SymbolUj);

            // Initialize UI Dashboard
            BuildDashboard();
        }

        public override void Calculate(int index)
        {
            // 1. Time Zone Logic
            TimeSpan timeOfDay = Bars.OpenTimes[index].TimeOfDay;
            bool inTier1 = IsInTimeWindow(timeOfDay, _t1Start, _t1End);
            bool inSwap = IsInTimeWindow(timeOfDay, _swStart, _swEnd);
            bool inFomc = IsInTimeWindow(timeOfDay, _fmStart, _fmEnd);

            // 2. Visuals: Paint Danger Zones Over Bars
            if (inTier1 || inFomc)
            {
                DrawRiskBox(index, "Macro", Color.FromArgb(75, Color.Red));
            }
            else if (inSwap)
            {
                DrawRiskBox(index, "Swap", Color.FromArgb(75, Color.Blue));
            }

            // 3. Correlation Logic (Calculate on every tick for last bar, or historical for past bars)
            double corrAg = CalculateCorrelation(Bars.ClosePrices, _barsAg.ClosePrices, index, CorrLength, _barsAg);
            double corrUj = CalculateCorrelation(Bars.ClosePrices, _barsUj.ClosePrices, index, CorrLength, _barsUj);

            bool dangerAg = corrAg >= CorrThreshold;
            bool dangerUj = corrUj <= -CorrThreshold;

            // 4. Update UI & Alerts (Only on Live Last Bar)
            if (IsLastBar)
            {
                UpdateDashboard(corrAg, corrUj, dangerAg, dangerUj);

                // Alert Triggers (Only fires once when crossing into danger)
                if (dangerAg && !_wasAgDanger)
                {
                    Notifications.ShowPopup($"XAU/{SymbolAg} correlation breached ({corrAg:F2}). Do not stack risk.");
                    Notifications.PlaySound(SoundType.Warning);
                }
                if (dangerUj && !_wasUjDanger)
                {
                    Notifications.ShowPopup($"XAU/{SymbolUj} inverse correlation breached ({corrUj:F2}). Do not stack risk.");
                    Notifications.PlaySound(SoundType.Warning);
                }

                _wasAgDanger = dangerAg;
                _wasUjDanger = dangerUj;
            }
        }

        // --- HELPER: Correlation Math ---
        private double CalculateCorrelation(DataSeries mainSeries, DataSeries secSeries, int index, int lookback, Bars secBars)
        {
            double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, sumY2 = 0;
            int n = 0;

            for (int i = 0; i < lookback; i++)
            {
                int currIdx = index - i;
                if (currIdx < 0) break;

                // Sync secondary bar by main bar's open time to ensure data aligns perfectly
                DateTime time = Bars.OpenTimes[currIdx];
                int secIdx = secBars.OpenTimes.GetIndexByTime(time);
                if (secIdx < 0) continue; 

                double x = mainSeries[currIdx];
                double y = secSeries[secIdx];

                sumX += x;
                sumY += y;
                sumXY += x * y;
                sumX2 += x * x;
                sumY2 += y * y;
                n++;
            }

            if (n == 0) return 0;
            double denominator = Math.Sqrt((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY));
            if (denominator == 0) return 0;

            return (n * sumXY - sumX * sumY) / denominator;
        }

        // --- HELPER: Time Check ---
        private bool IsInTimeWindow(TimeSpan current, TimeSpan start, TimeSpan end)
        {
            if (start <= end) return current >= start && current <= end;
            return current >= start || current <= end; // Handles overnight wraps if needed
        }

        // --- HELPER: Draw Risk Box ---
        private void DrawRiskBox(int index, string prefix, Color color)
        {
            // Extends the box slightly above and below the bar so it acts like a background strip
            double top = Bars.HighPrices[index] + (10 * Symbol.PipSize);
            double bottom = Bars.LowPrices[index] - (10 * Symbol.PipSize);
            Chart.DrawRectangle($"{prefix}_{index}", index - 0.5, bottom, index + 0.5, top, color)
                 .IsFilled = true;
        }

        // --- UI DASHBOARD BUILDER ---
        private void BuildDashboard()
        {
            _auditBoard = new Grid(6, 2)
            {
                BackgroundColor = Color.FromArgb(220, 20, 20, 20),
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Bottom,
                Margin = new Thickness(10),
                ShowGridLines = true
            };

            // Headers
            AddCell(_auditBoard, 0, 0, "XAU DESK AUDIT", Color.FromArgb(255, 40, 40, 40), Color.White);
            AddCell(_auditBoard, 0, 1, "STATUS", Color.FromArgb(255, 40, 40, 40), Color.White);

            // Process Rules
            AddCell(_auditBoard, 1, 0, "Flat Before Tier-1", Color.Transparent, Color.Gray);
            AddCell(_auditBoard, 1, 1, "Review", Color.FromArgb(200, 150, 0, 0), Color.White);

            AddCell(_auditBoard, 2, 0, "Overnight Swap Leftovers", Color.Transparent, Color.Gray);
            AddCell(_auditBoard, 2, 1, "Review", Color.FromArgb(200, 0, 0, 150), Color.White);

            // Correlation Rows (Hold references to status textblocks so we can update them live)
            AddCell(_auditBoard, 3, 0, $"{SymbolAg} Correlation", Color.Transparent, Color.White);
            _txtAgStatus = AddCell(_auditBoard, 3, 1, "Calculating...", Color.Gray, Color.White);

            AddCell(_auditBoard, 4, 0, $"{SymbolUj} Correlation", Color.Transparent, Color.White);
            _txtUjStatus = AddCell(_auditBoard, 4, 1, "Calculating...", Color.Gray, Color.White);

            AddCell(_auditBoard, 5, 0, "Broker vs. Entry Costs", Color.Transparent, Color.Gray);
            AddCell(_auditBoard, 5, 1, "Pending", Color.FromArgb(200, 200, 100, 0), Color.White);

            Chart.AddControl(_auditBoard);
        }

        private TextBlock AddCell(Grid grid, int row, int col, string text, Color bgColor, Color fgColor)
        {
            var border = new Border
            {
                BackgroundColor = bgColor,
                BorderThickness = new Thickness(1),
                BorderColor = Color.FromArgb(100, 100, 100, 100)
            };

            var textBlock = new TextBlock
            {
                Text = text,
                ForegroundColor = fgColor,
                Margin = new Thickness(8, 4, 8, 4),
                VerticalAlignment = VerticalAlignment.Center,
                HorizontalAlignment = HorizontalAlignment.Left,
                FontWeight = FontWeight.Bold
            };

            border.Child = textBlock;
            grid.AddChild(border, row, col);
            return textBlock;
        }

        private void UpdateDashboard(double corrAg, double corrUj, bool dangerAg, bool dangerUj)
        {
            _txtAgStatus.Text = dangerAg ? $"STACKED ({corrAg:F2})" : $"{corrAg:F2}";
            _txtAgStatus.Parent.BackgroundColor = dangerAg ? Color.FromArgb(200, 150, 0, 0) : Color.FromArgb(150, 60, 60, 60);

            _txtUjStatus.Text = dangerUj ? $"STACKED ({corrUj:F2})" : $"{corrUj:F2}";
            _txtUjStatus.Parent.BackgroundColor = dangerUj ? Color.FromArgb(200, 150, 0, 0) : Color.FromArgb(150, 60, 60, 60);
        }
    }
}
Recommended broker for automated trading & scalping IC Markets
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Inventory risk for gold specialists: weekly audit

Post by FTtrader »

How to Install in cTrader

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

2.) Under the Indicators section, click New and name it GoldInventoryRiskAuditor.

3.) Paste the C# code above, replacing the default template entirely.

4.) Click Build (the hammer icon at the top).

5.) Return to your Gold chart, click the Indicators icon, go to Custom, and apply the script.

Ensure your secondary tickers in the parameters match your broker's exact names (e.g., XAGUSD and USDJPY). The correlations will immediately sync on the live chart.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Inventory risk for gold specialists: weekly audit

Post by FTtrader »

Both the MQL5 (MT5) and MQL4 (MT4) implementations reproduce the same three systems:

1.) Visual Danger Zones: Paints background session rectangles directly onto the chart behind candlesticks for Tier-1 data drops, late NY events (FOMC), and the overnight swap rollover window.

2.) Live Dynamic Correlation Engine: Calculates rolling Pearson correlation against Silver (XAGUSD) and USD/JPY (USDJPY) synchronized across timestamps.

3.) Desk Audit Dashboard & Alerts: Displays an on-chart matrix that flags STACKED exposure and triggers an alert when correlation limits are breached.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Inventory risk for gold specialists: weekly audit

Post by FTtrader »

MetaTrader 5 (MQL5)

Save this as GoldInventoryRiskAuditor.mq5 in your MQL5/Indicators/ folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                  GoldInventoryRiskAuditor.mq5    |
//|                                  Desk Inventory & Correlation    |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_plots 0

// --- INPUTS: Broker & Time Settings ---
input group "=== Timezone & Sessions (NY Time) ==="
input int      InpServerToNYHours = 7;           // Broker Server Time minus NY Time (Hours)
input string   InpTier1Window     = "08:25-08:45";// Tier-1 Macro (CPI/NFP)
input string   InpFomcWindow      = "13:55-14:15";// Late NY Event (FOMC)
input string   InpSwapWindow      = "16:45-17:15";// Overnight Swap / Rollover
input int      InpDaysBack        = 20;           // Days of session shading to draw

input group "=== Stacked Risk Matrix ==="
input int      InpCorrPeriod      = 20;           // Correlation Lookback Bars
input string   InpSymbolAg        = "XAGUSD";     // Silver Ticker
input string   InpSymbolUj        = "USDJPY";     // USD/JPY Ticker
input double   InpCorrThreshold   = 0.80;         // Danger Threshold (Absolute)

input group "=== Visuals & Styling ==="
input color    InpClrTier1        = C'120,30,30'; // Tier-1 Zone Color (Subtle Red)
input color    InpClrSwap         = C'20,40,90';  // Swap Zone Color (Subtle Blue)

// Global state
bool wasAgDanger = false;
bool wasUjDanger = false;
string gPrefix = "XAU_AUDIT_";

//+------------------------------------------------------------------+
int OnInit()
{
   EventSetTimer(2);
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   EventKillTimer();
   ObjectsDeleteAll(0, gPrefix);
}

//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total < InpCorrPeriod + 1) return(0);

   // Draw background risk zones once a day or on initial load
   if(prev_calculated == 0)
   {
      DrawAllSessions();
   }

   // Correlation Calculations
   double corrAg = CalcCorrelation(_Symbol, InpSymbolAg, _Period, InpCorrPeriod);
   double corrUj = CalcCorrelation(_Symbol, InpSymbolUj, _Period, InpCorrPeriod);

   bool dangerAg = (corrAg >= InpCorrThreshold);
   bool dangerUj = (corrUj <= -InpCorrThreshold);

   // Threshold Breach Alerts
   if(dangerAg && !wasAgDanger)
   {
      Alert(StringFormat("[DESK WARNING] XAU/%s Correlation breached limits (%.2f)! Stacked exposure detected.", InpSymbolAg, corrAg));
      PlaySound("alert.wav");
   }
   if(dangerUj && !wasUjDanger)
   {
      Alert(StringFormat("[DESK WARNING] XAU/%s Inverse Correlation breached limits (%.2f)! Stacked exposure detected.", InpSymbolUj, corrUj));
      PlaySound("alert.wav");
   }

   wasAgDanger = dangerAg;
   wasUjDanger = dangerUj;

   // Update HUD Table
   UpdateDashboard(corrAg, corrUj, dangerAg, dangerUj);

   return(rates_total);
}

//+------------------------------------------------------------------+
void OnTimer()
{
   // Keep dashboard synced with secondary ticks during market lulls
   double corrAg = CalcCorrelation(_Symbol, InpSymbolAg, _Period, InpCorrPeriod);
   double corrUj = CalcCorrelation(_Symbol, InpSymbolUj, _Period, InpCorrPeriod);
   UpdateDashboard(corrAg, corrUj, corrAg >= InpCorrThreshold, corrUj <= -InpCorrThreshold);
}

//+------------------------------------------------------------------+
//| Pearson Correlation Calculator                                   |
//+------------------------------------------------------------------+
double CalcCorrelation(string s1, string s2, ENUM_TIMEFRAMES tf, int len)
{
   double c1[], c2[];
   ArraySetAsSeries(c1, true);
   ArraySetAsSeries(c2, true);

   if(CopyClose(s1, tf, 0, len, c1) < len) return 0.0;

   // Sync timestamps of s2 to s1
   datetime t[];
   ArraySetAsSeries(t, true);
   if(CopyTime(s1, tf, 0, len, t) < len) return 0.0;

   ArrayResize(c2, len);
   for(int i = 0; i < len; i++)
   {
      int shift = iBarShift(s2, tf, t[i], false);
      if(shift < 0) return 0.0;
      c2[i] = iClose(s2, tf, shift);
   }

   double sX = 0, sY = 0, sXY = 0, sX2 = 0, sY2 = 0;
   for(int i = 0; i < len; i++)
   {
      sX += c1[i];
      sY += c2[i];
      sXY += c1[i] * c2[i];
      sX2 += c1[i] * c1[i];
      sY2 += c2[i] * c2[i];
   }

   double denom = MathSqrt((len * sX2 - sX * sX) * (len * sY2 - sY * sY));
   if(denom == 0) return 0.0;

   return (len * sXY - sX * sY) / denom;
}

//+------------------------------------------------------------------+
//| Draw Risk Session Windows                                       |
//+------------------------------------------------------------------+
void DrawAllSessions()
{
   datetime now = TimeCurrent();
   for(int d = 0; d < InpDaysBack; d++)
   {
      datetime dayTime = now - (d * 86400);
      MqlDateTime dt;
      TimeToStruct(dayTime, dt);

      // Skip Weekends
      if(dt.day_of_week == 0 || dt.day_of_week == 6) continue;

      CreateSessionBox(dt, InpTier1Window, "T1", InpClrTier1);
      CreateSessionBox(dt, InpFomcWindow, "FOMC", InpClrTier1);
      CreateSessionBox(dt, InpSwapWindow, "SWAP", InpClrSwap);
   }
}

void CreateSessionBox(MqlDateTime &dt, string window, string tag, color clr)
{
   string parts[];
   if(StringSplit(window, '-', parts) != 2) return;

   int startH = (int)StringToInteger(StringSubstr(parts[0], 0, 2)) + InpServerToNYHours;
   int startM = (int)StringToInteger(StringSubstr(parts[0], 3, 2));
   int endH   = (int)StringToInteger(StringSubstr(parts[1], 0, 2)) + InpServerToNYHours;
   int endM   = (int)StringToInteger(StringSubstr(parts[1], 3, 2));

   MqlDateTime s = dt; s.hour = startH % 24; s.min = startM; s.sec = 0;
   MqlDateTime e = dt; e.hour = endH % 24;   e.min = endM;   e.sec = 0;

   datetime t1 = StructToTime(s);
   datetime t2 = StructToTime(e);

   string name = gPrefix + "BOX_" + tag + "_" + TimeToString(t1, TIME_DATE|TIME_MINUTES);
   if(ObjectFind(0, name) < 0)
   {
      ObjectCreate(0, name, OBJ_RECTANGLE, 0, t1, 0, t2, 200000);
      ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
      ObjectSetInteger(0, name, OBJPROP_FILL, true);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   }
}

//+------------------------------------------------------------------+
//| Dashboard UI                                                     |
//+------------------------------------------------------------------+
void UpdateDashboard(double corrAg, double corrUj, bool dangAg, bool dangUj)
{
   int x = 20, y = 30, w = 180, h = 20;
   DrawCell("H0", x, y, w, h, "XAU DESK AUDIT", C'40,40,40', clrWhite);
   DrawCell("H1", x + w, y, 100, h, "STATUS", C'40,40,40', clrWhite);

   DrawCell("R1_0", x, y + 22, w, h, "Flat Before Tier-1", C'25,25,25', clrSilver);
   DrawCell("R1_1", x + w, y + 22, 100, h, "REVIEW", C'100,20,20', clrWhite);

   DrawCell("R2_0", x, y + 44, w, h, "Overnight Leftovers", C'25,25,25', clrSilver);
   DrawCell("R2_1", x + w, y + 44, 100, h, "REVIEW", C'20,40,100', clrWhite);

   string txtAg = dangAg ? StringFormat("STACK (%.2f)", corrAg) : DoubleToString(corrAg, 2);
   color bgAg = dangAg ? C'180,20,20' : C'35,35,35';
   DrawCell("R3_0", x, y + 66, w, h, InpSymbolAg + " Correlation", C'25,25,25', clrWhite);
   DrawCell("R3_1", x + w, y + 66, 100, h, txtAg, bgAg, clrWhite);

   string txtUj = dangUj ? StringFormat("STACK (%.2f)", corrUj) : DoubleToString(corrUj, 2);
   color bgUj = dangUj ? C'180,20,20' : C'35,35,35';
   DrawCell("R4_0", x, y + 88, w, h, InpSymbolUj + " Correlation", C'25,25,25', clrWhite);
   DrawCell("R4_1", x + w, y + 88, 100, h, txtUj, bgUj, clrWhite);

   DrawCell("R5_0", x, y + 110, w, h, "Broker vs Entry Costs", C'25,25,25', clrSilver);
   DrawCell("R5_1", x + w, y + 110, 100, h, "PENDING", C'120,70,0', clrWhite);

   ChartRedraw(0);
}

void DrawCell(string id, int x, int y, int w, int h, string text, color bg, color fg)
{
   string rectName = gPrefix + "BG_" + id;
   string lblName  = gPrefix + "TXT_" + id;

   if(ObjectFind(0, rectName) < 0)
   {
      ObjectCreate(0, rectName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
      ObjectSetInteger(0, rectName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
      ObjectSetInteger(0, rectName, OBJPROP_XDISTANCE, x + w);
      ObjectSetInteger(0, rectName, OBJPROP_YDISTANCE, y + h);
      ObjectSetInteger(0, rectName, OBJPROP_XSIZE, w);
      ObjectSetInteger(0, rectName, OBJPROP_YSIZE, h);
      ObjectSetInteger(0, rectName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
      ObjectSetInteger(0, rectName, OBJPROP_COLOR, C'60,60,60');
   }
   ObjectSetInteger(0, rectName, OBJPROP_BGCOLOR, bg);

   if(ObjectFind(0, lblName) < 0)
   {
      ObjectCreate(0, lblName, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, lblName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
      ObjectSetInteger(0, lblName, OBJPROP_FONTSIZE, 8);
      ObjectSetString(0, lblName, OBJPROP_FONT, "Segoe UI");
      ObjectSetInteger(0, lblName, OBJPROP_SELECTABLE, false);
   }
   ObjectSetInteger(0, lblName, OBJPROP_XDISTANCE, x + w - 8);
   ObjectSetInteger(0, lblName, OBJPROP_YDISTANCE, y + h - 3);
   ObjectSetString(0, lblName, OBJPROP_TEXT, text);
   ObjectSetInteger(0, lblName, OBJPROP_COLOR, fg);
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Inventory risk for gold specialists: weekly audit

Post by FTtrader »

MetaTrader 4 (MQL4)

Save this as GoldInventoryRiskAuditor.mq4 in your MQL4/Indicators/ folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                  GoldInventoryRiskAuditor.mq4    |
//|                                  Desk Inventory & Correlation    |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 0

// --- INPUTS: Broker & Time Settings ---
extern string   TimeSettings       = "=== Timezone & Sessions (NY Time) ===";
extern int      InpServerToNYHours = 7;             // Broker Server Time minus NY Time (Hours)
extern string   InpTier1Window     = "08:25-08:45"; // Tier-1 Macro (CPI/NFP)
extern string   InpFomcWindow      = "13:55-14:15"; // Late NY Event (FOMC)
extern string   InpSwapWindow      = "16:45-17:15"; // Overnight Swap / Rollover
extern int      InpDaysBack        = 20;             // Days of session shading to draw

extern string   CorrSettings       = "=== Stacked Risk Matrix ===";
extern int      InpCorrPeriod      = 20;             // Correlation Lookback Bars
extern string   InpSymbolAg        = "XAGUSD";       // Silver Ticker
extern string   InpSymbolUj        = "USDJPY";       // USD/JPY Ticker
extern double   InpCorrThreshold   = 0.80;           // Danger Threshold (Absolute)

extern string   VisualSettings     = "=== Visuals & Styling ===";
extern color    InpClrTier1        = C'120,30,30';   // Tier-1 Zone Color
extern color    InpClrSwap         = C'20,40,90';    // Swap Zone Color

// Global state
bool wasAgDanger = false;
bool wasUjDanger = false;
string gPrefix = "XAU_AUDIT4_";

//+------------------------------------------------------------------+
int init()
{
   DrawAllSessions();
   return(0);
}

//+------------------------------------------------------------------+
int deinit()
{
   ObjectsDeleteAll(0, OBJ_RECTANGLE);
   ObjectsDeleteAll(0, OBJ_RECTANGLE_LABEL);
   ObjectsDeleteAll(0, OBJ_LABEL);
   return(0);
}

//+------------------------------------------------------------------+
int start()
{
   // Rolling Pearson Correlation
   double corrAg = CalcCorrelation(_Symbol, InpSymbolAg, Period(), InpCorrPeriod);
   double corrUj = CalcCorrelation(_Symbol, InpSymbolUj, Period(), InpCorrPeriod);

   bool dangerAg = (corrAg >= InpCorrThreshold);
   bool dangerUj = (corrUj <= -InpCorrThreshold);

   // Edge-Triggered Alerts
   if(dangerAg && !wasAgDanger)
   {
      Alert(StringConcatenate("[DESK WARNING] XAU/", InpSymbolAg, " correlation breached (", DoubleToStr(corrAg, 2), ")!"));
      PlaySound("alert.wav");
   }
   if(dangerUj && !wasUjDanger)
   {
      Alert(StringConcatenate("[DESK WARNING] XAU/", InpSymbolUj, " inverse correlation breached (", DoubleToStr(corrUj, 2), ")!"));
      PlaySound("alert.wav");
   }

   wasAgDanger = dangerAg;
   wasUjDanger = dangerUj;

   UpdateDashboard(corrAg, corrUj, dangerAg, dangerUj);
   return(0);
}

//+------------------------------------------------------------------+
double CalcCorrelation(string s1, string s2, int tf, int len)
{
   double sX = 0, sY = 0, sXY = 0, sX2 = 0, sY2 = 0;
   int counted = 0;

   for(int i = 0; i < len; i++)
   {
      datetime t = iTime(s1, tf, i);
      int shift2 = iBarShift(s2, tf, t, false);
      if(shift2 < 0) continue;

      double x = iClose(s1, tf, i);
      double y = iClose(s2, tf, shift2);

      sX += x;
      sY += y;
      sXY += x * y;
      sX2 += x * x;
      sY2 += y * y;
      counted++;
   }

   if(counted < len / 2) return 0.0;

   double denom = MathSqrt((counted * sX2 - sX * sX) * (counted * sY2 - sY * sY));
   if(denom == 0) return 0.0;

   return (counted * sXY - sX * sY) / denom;
}

//+------------------------------------------------------------------+
void DrawAllSessions()
{
   datetime now = TimeCurrent();
   for(int d = 0; d < InpDaysBack; d++)
   {
      datetime dayTime = now - (d * 86400);
      int dow = TimeDayOfWeek(dayTime);
      if(dow == 0 || dow == 6) continue;

      CreateSessionBox(dayTime, InpTier1Window, "T1", InpClrTier1);
      CreateSessionBox(dayTime, InpFomcWindow, "FOMC", InpClrTier1);
      CreateSessionBox(dayTime, InpSwapWindow, "SWAP", InpClrSwap);
   }
}

void CreateSessionBox(datetime dayTime, string window, string tag, color clr)
{
   int startH = (int)StrToInteger(StringSubstr(window, 0, 2)) + InpServerToNYHours;
   int startM = (int)StrToInteger(StringSubstr(window, 3, 2));
   int endH   = (int)StrToInteger(StringSubstr(window, 6, 2)) + InpServerToNYHours;
   int endM   = (int)StrToInteger(StringSubstr(window, 9, 2));

   string dStr = TimeToStr(dayTime, TIME_DATE);
   datetime t1 = StrToTime(dStr + " " + (string)(startH % 24) + ":" + (string)startM);
   datetime t2 = StrToTime(dStr + " " + (string)(endH % 24) + ":" + (string)endM);

   string name = gPrefix + "BOX_" + tag + "_" + (string)t1;
   if(ObjectFind(name) < 0)
   {
      ObjectCreate(name, OBJ_RECTANGLE, 0, t1, 0.0, t2, 200000.0);
      ObjectSet(name, OBJPROP_COLOR, clr);
      ObjectSet(name, OBJPROP_BACK, true);
      ObjectSet(name, OBJPROP_SELECTABLE, false);
   }
}

//+------------------------------------------------------------------+
void UpdateDashboard(double corrAg, double corrUj, bool dangAg, bool dangUj)
{
   int x = 20, y = 30, w = 180, h = 20;

   DrawCell("H0", x, y, w, h, "XAU DESK AUDIT", C'40,40,40', clrWhite);
   DrawCell("H1", x + w, y, 100, h, "STATUS", C'40,40,40', clrWhite);

   DrawCell("R1_0", x, y + 22, w, h, "Flat Before Tier-1", C'25,25,25', clrSilver);
   DrawCell("R1_1", x + w, y + 22, 100, h, "REVIEW", C'100,20,20', clrWhite);

   DrawCell("R2_0", x, y + 44, w, h, "Overnight Leftovers", C'25,25,25', clrSilver);
   DrawCell("R2_1", x + w, y + 44, 100, h, "REVIEW", C'20,40,100', clrWhite);

   string txtAg = dangAg ? StringConcatenate("STACK (", DoubleToStr(corrAg, 2), ")") : DoubleToStr(corrAg, 2);
   color bgAg = dangAg ? C'180,20,20' : C'35,35,35';
   DrawCell("R3_0", x, y + 66, w, h, InpSymbolAg + " Correlation", C'25,25,25', clrWhite);
   DrawCell("R3_1", x + w, y + 66, 100, h, txtAg, bgAg, clrWhite);

   string txtUj = dangUj ? StringConcatenate("STACK (", DoubleToStr(corrUj, 2), ")") : DoubleToStr(corrUj, 2);
   color bgUj = dangUj ? C'180,20,20' : C'35,35,35';
   DrawCell("R4_0", x, y + 88, w, h, InpSymbolUj + " Correlation", C'25,25,25', clrWhite);
   DrawCell("R4_1", x + w, y + 88, 100, h, txtUj, bgUj, clrWhite);

   DrawCell("R5_0", x, y + 110, w, h, "Broker vs Entry Costs", C'25,25,25', clrSilver);
   DrawCell("R5_1", x + w, y + 110, 100, h, "PENDING", C'120,70,0', clrWhite);
}

void DrawCell(string id, int x, int y, int w, int h, string text, color bg, color fg)
{
   string rectName = gPrefix + "BG_" + id;
   string lblName  = gPrefix + "TXT_" + id;

   if(ObjectFind(rectName) < 0)
   {
      ObjectCreate(rectName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
      ObjectSet(rectName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
      ObjectSet(rectName, OBJPROP_XDISTANCE, x + w);
      ObjectSet(rectName, OBJPROP_YDISTANCE, y + h);
      ObjectSet(rectName, OBJPROP_XSIZE, w);
      ObjectSet(rectName, OBJPROP_YSIZE, h);
      ObjectSet(rectName, OBJPROP_COLOR, C'60,60,60');
   }
   ObjectSet(rectName, OBJPROP_BGCOLOR, bg);

   if(ObjectFind(lblName) < 0)
   {
      ObjectCreate(lblName, OBJ_LABEL, 0, 0, 0);
      ObjectSet(lblName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
      ObjectSet(lblName, OBJPROP_FONTSIZE, 8);
      ObjectSetText(lblName, text, 8, "Segoe UI", fg);
   }
   ObjectSet(lblName, OBJPROP_XDISTANCE, x + w - 8);
   ObjectSet(lblName, OBJPROP_YDISTANCE, y + h - 3);
   ObjectSetText(lblName, text, 8, "Segoe UI", fg);
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Inventory risk for gold specialists: weekly audit

Post by FTtrader »

Setup Notes

Broker Timezone Alignment: Most MetaTrader brokers run on GMT+2 (winter) / GMT+3 (summer) to ensure 5 daily candles per week. New York is GMT-5 / GMT-4. This leaves a constant 7-hour gap (InpServerToNYHours = 7). If your broker uses UTC/GMT, adjust InpServerToNYHours to 5.

Symbol Suffixes: If your broker appends suffixes (e.g., XAGUSD.pro or USDJPYm), update the InpSymbolAg and InpSymbolUj inputs in the indicator properties to match your Market Watch symbols exactly.

Data Pre-loading: Ensure you have opened the XAGUSD and USDJPY charts at least once on the timeframe you trade so MT4/MT5 downloads the historical bars needed for the Pearson correlation lookback.
Post Reply