Advertisement IC Markets

Choosing NAS100 or XAUUSD when both scream risk-off

Discuss 1-minute to 15-minute price action setups, fading intraday momentum, key support/resistance zones, and proven short-term trading methodologies.
Post Reply
LondonScalper
Posts: 701
Joined: Sat Sep 05, 2026 7:54 am

Choosing NAS100 or XAUUSD when both scream risk-off

Post by LondonScalper »

When NAS100 and XAUUSD both scream risk-off, picking both is usually the same bet twice.

I used to "diversify" into index and gold because each chart had a level. Correlation does not care about my narrative. On those mornings I choose one primary expression, size it sanely, and treat the other as confirmation or noise — not as a second full risk unit.

Decision rule
1. Which venue has the cleaner spread and clearer invalidation right now?
2. Which fits my session budget without stacking correlation?
3. If unsure, take neither until one market is clearly leading.

Risk-off days feel urgent. Urgency is not a setup.

How do you choose between NAS100 and gold when both are moving hard — lead market, cost, or simple one-ticket discipline?

On pure risk-off mornings I sometimes choose gold solely because my metals invalidation list is tighter that day. The choice is operational, not prophetic. If both markets are messy, cash is a position.

Correlated risk notes from the overlap playbook apply here too — one thesis, one primary ticket.

I revisit this on the Sunday review with costs in the same pass as entries. Process without cost is half a conversation, and cost without process is just a spreadsheet hobby.
Recommended broker for automated trading & scalping IC Markets
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Choosing NAS100 or XAUUSD when both scream risk-off

Post by FTtrader »

LondonScalper wrote: Tue Sep 22, 2026 1:47 pm When NAS100 and XAUUSD both scream risk-off, picking both is usually the same bet twice.

I used to "diversify" into index and gold because each chart had a level. Correlation does not care about my narrative. On those mornings I choose one primary expression, size it sanely, and treat the other as confirmation or noise — not as a second full risk unit.

Decision rule
1. Which venue has the cleaner spread and clearer invalidation right now?
2. Which fits my session budget without stacking correlation?
3. If unsure, take neither until one market is clearly leading.

Risk-off days feel urgent. Urgency is not a setup.

How do you choose between NAS100 and gold when both are moving hard — lead market, cost, or simple one-ticket discipline?

On pure risk-off mornings I sometimes choose gold solely because my metals invalidation list is tighter that day. The choice is operational, not prophetic. If both markets are messy, cash is a position.

Correlated risk notes from the overlap playbook apply here too — one thesis, one primary ticket.

I revisit this on the Sunday review with costs in the same pass as entries. Process without cost is half a conversation, and cost without process is just a spreadsheet hobby.
Hi LondonScalper,

The "illusion of diversification" is one of the most expensive traps in retail trading. When a macroeconomic catalyst hits, the entire board becomes a single trade. Shorting the Nasdaq and buying Gold during a panic isn't hedging or diversifying; it is doubling your exposure to the exact same underlying theme—fear.

Your approach—treating the secondary market as confirmation noise rather than a second risk unit—is the exact mathematical antidote to overleveraging a macro move.

Here is why your decision matrix works at a professional level:

Operational Selection over Prediction: Choosing a market because its invalidation level is 15 ticks away instead of 40 ticks away is a mathematically sound edge. You aren't predicting which will run further; you are choosing the one that costs less to find out if you are wrong.

The Urgency Trap: Amateurs feel urgency and smash the market order button. Professionals recognize urgency as a spike in volatility (ATR) and a widening of the spread, which means position sizes must actually shrink or the trade must be skipped entirely.

Cost as Part of the Process: A brilliant technical setup in a low-liquidity, high-spread environment is a losing system. Factoring slippage and spread into the Sunday review is what separates a trading business from a chart-reading hobby.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Choosing NAS100 or XAUUSD when both scream risk-off

Post by FTtrader »

The "One Ticket" Decision Dashboard (Pine Script v5)

To automate your decision rule, this Pine Script builds a real-time Risk-Off Correlation Matrix on your chart.
Instead of guessing which market is "cleaner," the script pulls in your secondary ticker (e.g., Gold) and compares three things live:

Rolling Correlation: Warns you if the two assets are highly correlated (inversely or directly), signaling that you are about to take the "same bet twice."

Normalized Volatility (ATR %): Shows which asset is currently moving more erratically, helping you choose the tighter invalidation.

Tick Volatility: Helps gauge which asset is sizing better into your session budget.

Code: Select all

//@version=5
indicator("One Ticket: Correlation & Invalidation Matrix", overlay=true)

// ==============================================================================
// USER INPUTS
// ==============================================================================
secTicker = input.symbol("OANDA:XAUUSD", title="Secondary Ticker (e.g., Gold)")
corrLen   = input.int(20, title="Correlation Period", minval=5)
atrLen    = input.int(14, title="ATR Period", minval=1)
warnThresh= input.float(0.75, title="Correlation Warning Threshold (Absolute)", step=0.05)

// ==============================================================================
// DATA FETCHING & CALCULATIONS
// ==============================================================================
// Fetch Secondary Ticker Data
[secC, secH, secL, secPC] = request.security(secTicker, timeframe.period, [close, high, low, close[1]])

// 1. Correlation (using standard Pearson correlation)
// Note: In a risk-off environment, NAS100 (down) and XAUUSD (up) will have a highly NEGATIVE correlation.
// We use math.abs() to measure the pure strength of the relationship.
rawCorr = ta.correlation(close, secC, corrLen)
absCorr = math.abs(rawCorr)

// 2. Normalized ATR (Volatility as a % of asset price for apples-to-apples comparison)
priAtr    = ta.atr(atrLen)
priAtrPct = (priAtr / close) * 100

secTr     = math.max(secH - secL, math.max(math.abs(secH - secPC), math.abs(secL - secPC)))
secAtr    = ta.rma(secTr, atrLen)
secAtrPct = (secAtr / secC) * 100

// ==============================================================================
// DASHBOARD LOGIC & RENDERING
// ==============================================================================
var table panel = table.new(position.bottom_right, 3, 5, border_width = 1, border_color = color.new(color.gray, 50))

if barstate.islast
    // Define Warning Colors
    corrColor = absCorr >= warnThresh ? color.new(color.red, 70) : color.new(color.green, 70)
    
    // Which has tighter volatility (cleaner invalidation)?
    tighterAsset = priAtrPct < secAtrPct ? syminfo.ticker : secTicker
    
    // Header
    table.cell(panel, 0, 0, "METRIC", text_color=color.white, bgcolor=color.new(color.black, 0))
    table.cell(panel, 1, 0, syminfo.ticker + " (Pri)", text_color=color.white, bgcolor=color.new(color.black, 0))
    table.cell(panel, 2, 0, secTicker + " (Sec)", text_color=color.white, bgcolor=color.new(color.black, 0))
    
    // Correlation Status
    table.cell(panel, 0, 1, "Correlation", text_color=color.white, bgcolor=color.new(color.black, 40))
    table.cell(panel, 1, 1, str.tostring(rawCorr, "#.##"), text_color=color.white, bgcolor=corrColor)
    table.cell(panel, 2, 1, absCorr >= warnThresh ? "STACKED RISK" : "DIVERSIFIED", text_color=color.white, bgcolor=corrColor)

    // ATR / Volatility
    table.cell(panel, 0, 2, "Volatility (ATR %)", text_color=color.white, bgcolor=color.new(color.black, 40))
    table.cell(panel, 1, 2, str.tostring(priAtrPct, "#.###") + "%", text_color=color.white, bgcolor=color.new(color.black, 40))
    table.cell(panel, 2, 2, str.tostring(secAtrPct, "#.###") + "%", text_color=color.white, bgcolor=color.new(color.black, 40))
    
    // Raw ATR (For calculating tick risk / session budget)
    table.cell(panel, 0, 3, "Avg Invalidation (Points)", text_color=color.white, bgcolor=color.new(color.black, 40))
    table.cell(panel, 1, 3, str.tostring(priAtr, "#.##"), text_color=color.white, bgcolor=color.new(color.black, 40))
    table.cell(panel, 2, 3, str.tostring(secAtr, "#.##"), text_color=color.white, bgcolor=color.new(color.black, 40))

    // Actionable Decision Output
    table.cell(panel, 0, 4, "SYSTEM RULE", text_color=color.white, bgcolor=color.new(color.blue, 70))
    
    actionText = absCorr >= warnThresh ? "CHOOSE ONE: " + tighterAsset + " is currently tighter." : "Markets decoupled. Trade individual setups."
    table.cell(panel, 1, 4, actionText, text_color=color.white, bgcolor=color.new(color.blue, 70), text_halign=text.align_center)
    table.merge_cells(panel, 1, 4, 2, 4)
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Choosing NAS100 or XAUUSD when both scream risk-off

Post by FTtrader »

How to use this operationally

When things start moving hard and you feel that "urgent" pull to get involved in both NAS100 and Gold, glance at the bottom right of the chart. If the correlation cell is flashing red (STACKED RISK), look at the row below it. The script normalizes the volatility (ATR %) of both assets to explicitly tell you which one currently offers the mathematically tighter invalidation environment. You pick that one, apply your session budget, and ignore the other.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Choosing NAS100 or XAUUSD when both scream risk-off

Post by FTtrader »

Here are the complete source files for both MetaTrader 4 and MetaTrader 5.

Because MQL does not natively compute Pearson correlation across mismatched assets the way Pine Script does, these ports manually synchronize the time series. In MT4, this is handled via iBarShift to match index timestamps. In MT5, it is handled via strict CopyTime indexing to ensure the arrays perfectly align even if one market is missing a tick.

Note on Volatility Math: Pine Script's native ATR uses a smoothed moving average (RMA). Native MQL uses a simple moving average (SMA) of True Range. The absolute values will differ slightly from TradingView, but the relative percentage comparison between assets remains mathematically equivalent.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Choosing NAS100 or XAUUSD when both scream risk-off

Post by FTtrader »

1. MetaTrader 4 Version (.mq4)

Code: Select all

//+------------------------------------------------------------------+
//|                                     OneTicket_Matrix_MT4.mq4     |
//+------------------------------------------------------------------+
#property copyright "Risk-Off Correlation Matrix"
#property strict
#property indicator_chart_window
#property indicator_plots 0

input string   SecTicker  = "XAUUSD";      // Secondary Ticker (Match Market Watch)
input int      CorrLen    = 20;            // Correlation Period
input int      AtrLen     = 14;            // ATR Period
input double   WarnThresh = 0.75;          // Warning Threshold

void OnInit() {
    ObjectsDeleteAll(0, "OT_");
}

void OnDeinit(const int reason) {
    ObjectsDeleteAll(0, "OT_");
}

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 < CorrLen) return 0;

    // 1. Data Fetching & Time Synchronization
    double meanX = 0, meanY = 0;
    int validBars = 0;
    double x[], y[];
    ArrayResize(x, CorrLen);
    ArrayResize(y, CorrLen);

    for(int i = 0; i < CorrLen; i++) {
        datetime timeP = iTime(Symbol(), 0, i);
        int idxSec = iBarShift(SecTicker, 0, timeP, false);
        
        if(idxSec >= 0) {
            double valX = iClose(Symbol(), 0, i);
            double valY = iClose(SecTicker, 0, idxSec);
            
            if(valX > 0 && valY > 0) { 
                x[validBars] = valX;
                y[validBars] = valY;
                meanX += valX;
                meanY += valY;
                validBars++;
            }
        }
    }

    // 2. Pearson Correlation Calculation
    double rawCorr = 0;
    if(validBars > 1) {
        meanX /= validBars;
        meanY /= validBars;
        double cov = 0, varX = 0, varY = 0;
        
        for(int i = 0; i < validBars; i++) {
            cov += (x[i] - meanX) * (y[i] - meanY);
            varX += MathPow(x[i] - meanX, 2);
            varY += MathPow(y[i] - meanY, 2);
        }
        
        if(varX > 0 && varY > 0) {
            rawCorr = cov / MathSqrt(varX * varY);
        }
    }
    double absCorr = MathAbs(rawCorr);

    // 3. Volatility / ATR Calculations
    double priAtr = iATR(Symbol(), 0, AtrLen, 0);
    double secAtr = iATR(SecTicker, 0, AtrLen, 0);
    double priC   = iClose(Symbol(), 0, 0);
    double secC   = iClose(SecTicker, 0, 0);

    double priAtrPct = (priC > 0) ? (priAtr / priC) * 100.0 : 0;
    double secAtrPct = (secC > 0) ? (secAtr / secC) * 100.0 : 0;

    // 4. GUI Rendering
    UpdateDashboard(rawCorr, absCorr, priAtrPct, secAtrPct, priAtr, secAtr);

    return rates_total;
}

void UpdateDashboard(double rawCorr, double absCorr, double priAtrPct, double secAtrPct, double priAtr, double secAtr) 
{
    color bgTitle   = clrBlack;
    color bgData    = C'40,40,40';
    color fg        = clrWhite;
    color corrColor = (absCorr >= WarnThresh) ? clrCrimson : clrForestGreen;
    color ruleBg    = clrDodgerBlue;
    
    string tighterAsset = (priAtrPct < secAtrPct) ? Symbol() : SecTicker;
    
    int priDig = (int)MarketInfo(Symbol(), MODE_DIGITS);
    int secDig = (int)MarketInfo(SecTicker, MODE_DIGITS);

    // Row 0: Headers
    DrawCell(0, 0, "METRIC", bgTitle, fg);
    DrawCell(1, 0, Symbol() + " (Pri)", bgTitle, fg);
    DrawCell(2, 0, SecTicker + " (Sec)", bgTitle, fg);
    
    // Row 1: Correlation Status
    DrawCell(0, 1, "Correlation", bgData, fg);
    DrawCell(1, 1, DoubleToString(rawCorr, 2), corrColor, fg);
    DrawCell(2, 1, (absCorr >= WarnThresh ? "STACKED RISK" : "DIVERSIFIED"), corrColor, fg);
    
    // Row 2: Normalized Volatility
    DrawCell(0, 2, "Volatility (ATR %)", bgData, fg);
    DrawCell(1, 2, DoubleToString(priAtrPct, 3) + "%", bgData, fg);
    DrawCell(2, 2, DoubleToString(secAtrPct, 3) + "%", bgData, fg);
    
    // Row 3: Raw Price Invalidation
    DrawCell(0, 3, "Avg Inval. (Price)", bgData, fg);
    DrawCell(1, 3, DoubleToString(priAtr, priDig), bgData, fg);
    DrawCell(2, 3, DoubleToString(secAtr, secDig), bgData, fg);
    
    // Row 4: Actionable Output
    DrawCell(0, 4, "SYSTEM RULE", ruleBg, fg);
    string actionText = (absCorr >= WarnThresh) ? "CHOOSE ONE: " + tighterAsset + " is tighter." : "Markets decoupled. Trade independent setups.";
    DrawCell(1, 4, actionText, ruleBg, fg, 2);
}

void DrawCell(int col, int row, string text, color bg, color fg, int colspan=1) 
{
    int w = 150, h = 25;
    int cellW = w * colspan;
    int xDist = 10 + (2 - (col + colspan - 1)) * w;
    int yDist = 10 + (4 - row) * h;
    
    string bgName = "OT_BG_" + IntegerToString(col) + "_" + IntegerToString(row);
    if(ObjectFind(0, bgName) < 0) {
        ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
        ObjectSetInteger(0, bgName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, bgName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
        ObjectSetInteger(0, bgName, OBJPROP_BACK, false);
        ObjectSetInteger(0, bgName, OBJPROP_ZORDER, 0);
    }
    ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, xDist);
    ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, yDist);
    ObjectSetInteger(0, bgName, OBJPROP_XSIZE, cellW);
    ObjectSetInteger(0, bgName, OBJPROP_YSIZE, h);
    ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, bg);
    ObjectSetInteger(0, bgName, OBJPROP_COLOR, clrDimGray); 
    
    string txtName = "OT_TXT_" + IntegerToString(col) + "_" + IntegerToString(row);
    if(ObjectFind(0, txtName) < 0) {
        ObjectCreate(0, txtName, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, txtName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, txtName, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
        ObjectSetString(0, txtName, OBJPROP_FONT, "Arial");
        ObjectSetInteger(0, txtName, OBJPROP_FONTSIZE, 9);
        ObjectSetInteger(0, txtName, OBJPROP_BACK, false);
        ObjectSetInteger(0, txtName, OBJPROP_ZORDER, 1);
    }
    ObjectSetInteger(0, txtName, OBJPROP_XDISTANCE, xDist + cellW - 10);
    ObjectSetInteger(0, txtName, OBJPROP_YDISTANCE, yDist + h - 5);
    ObjectSetString(0, txtName, OBJPROP_TEXT, text);
    ObjectSetInteger(0, txtName, OBJPROP_COLOR, fg);
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Choosing NAS100 or XAUUSD when both scream risk-off

Post by FTtrader »

2. MetaTrader 5 Version (.mq5)

Code: Select all

//+------------------------------------------------------------------+
//|                                     OneTicket_Matrix_MT5.mq5     |
//+------------------------------------------------------------------+
#property copyright "Risk-Off Correlation Matrix"
#property indicator_chart_window
#property indicator_plots 0

input string   SecTicker  = "XAUUSD";      // Secondary Ticker (Match Market Watch)
input int      CorrLen    = 20;            // Correlation Period
input int      AtrLen     = 14;            // ATR Period
input double   WarnThresh = 0.75;          // Warning Threshold

int atrPriHandle;
int atrSecHandle;

int OnInit() {
    atrPriHandle = iATR(_Symbol, PERIOD_CURRENT, AtrLen);
    atrSecHandle = iATR(SecTicker, PERIOD_CURRENT, AtrLen);
    
    if(atrPriHandle == INVALID_HANDLE || atrSecHandle == INVALID_HANDLE) {
        Print("Failed to load indicators. Check if Secondary Ticker exists.");
        return INIT_FAILED;
    }
    
    ObjectsDeleteAll(0, "OT_");
    return INIT_SUCCEEDED;
}

void OnDeinit(const int reason) {
    ObjectsDeleteAll(0, "OT_");
    IndicatorRelease(atrPriHandle);
    IndicatorRelease(atrSecHandle);
}

void DrawCell(int col, int row, string text, color bg, color fg, int colspan=1) 
{
    int w = 150, h = 25;
    int cellW = w * colspan;
    int xDist = 10 + (2 - (col + colspan - 1)) * w;
    int yDist = 10 + (4 - row) * h;
    
    string bgName = "OT_BG_" + IntegerToString(col) + "_" + IntegerToString(row);
    if(ObjectFind(0, bgName) < 0) {
        ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
        ObjectSetInteger(0, bgName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, bgName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
        ObjectSetInteger(0, bgName, OBJPROP_BACK, false);
        ObjectSetInteger(0, bgName, OBJPROP_ZORDER, 0);
    }
    ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, xDist);
    ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, yDist);
    ObjectSetInteger(0, bgName, OBJPROP_XSIZE, cellW);
    ObjectSetInteger(0, bgName, OBJPROP_YSIZE, h);
    ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, bg);
    ObjectSetInteger(0, bgName, OBJPROP_COLOR, clrDimGray); 
    
    string txtName = "OT_TXT_" + IntegerToString(col) + "_" + IntegerToString(row);
    if(ObjectFind(0, txtName) < 0) {
        ObjectCreate(0, txtName, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, txtName, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, txtName, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
        ObjectSetString(0, txtName, OBJPROP_FONT, "Arial");
        ObjectSetInteger(0, txtName, OBJPROP_FONTSIZE, 9);
        ObjectSetInteger(0, txtName, OBJPROP_BACK, false);
        ObjectSetInteger(0, txtName, OBJPROP_ZORDER, 1);
    }
    ObjectSetInteger(0, txtName, OBJPROP_XDISTANCE, xDist + cellW - 10);
    ObjectSetInteger(0, txtName, OBJPROP_YDISTANCE, yDist + h - 5);
    ObjectSetString(0, txtName, OBJPROP_TEXT, text);
    ObjectSetInteger(0, txtName, OBJPROP_COLOR, fg);
}

void UpdateDashboard(double rawCorr, double absCorr, double priAtrPct, double secAtrPct, double priAtr, double secAtr) 
{
    color bgTitle   = clrBlack;
    color bgData    = C'40,40,40';
    color fg        = clrWhite;
    color corrColor = (absCorr >= WarnThresh) ? clrCrimson : clrForestGreen;
    color ruleBg    = clrDodgerBlue;
    
    string tighterAsset = (priAtrPct < secAtrPct) ? _Symbol : SecTicker;
    
    int priDig = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
    int secDig = (int)SymbolInfoInteger(SecTicker, SYMBOL_DIGITS);

    DrawCell(0, 0, "METRIC", bgTitle, fg);
    DrawCell(1, 0, _Symbol + " (Pri)", bgTitle, fg);
    DrawCell(2, 0, SecTicker + " (Sec)", bgTitle, fg);
    
    DrawCell(0, 1, "Correlation", bgData, fg);
    DrawCell(1, 1, DoubleToString(rawCorr, 2), corrColor, fg);
    DrawCell(2, 1, (absCorr >= WarnThresh ? "STACKED RISK" : "DIVERSIFIED"), corrColor, fg);
    
    DrawCell(0, 2, "Volatility (ATR %)", bgData, fg);
    DrawCell(1, 2, DoubleToString(priAtrPct, 3) + "%", bgData, fg);
    DrawCell(2, 2, DoubleToString(secAtrPct, 3) + "%", bgData, fg);
    
    DrawCell(0, 3, "Avg Inval. (Price)", bgData, fg);
    DrawCell(1, 3, DoubleToString(priAtr, priDig), bgData, fg);
    DrawCell(2, 3, DoubleToString(secAtr, secDig), bgData, fg);
    
    DrawCell(0, 4, "SYSTEM RULE", ruleBg, fg);
    string actionText = (absCorr >= WarnThresh) ? "CHOOSE ONE: " + tighterAsset + " is tighter." : "Markets decoupled. Trade independent setups.";
    DrawCell(1, 4, actionText, ruleBg, fg, 2);
}

int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
    if(rates_total < CorrLen) return 0;

    // 1. Data Fetching & Strict Time Synchronization
    datetime times[];
    double priC[];
    if(CopyTime(_Symbol, PERIOD_CURRENT, 0, CorrLen, times) < CorrLen) return 0;
    if(CopyClose(_Symbol, PERIOD_CURRENT, 0, CorrLen, priC) < CorrLen) return 0;

    double meanX = 0, meanY = 0;
    int validBars = 0;
    double x[], y[];
    ArrayResize(x, CorrLen);
    ArrayResize(y, CorrLen);

    for(int i = 0; i < CorrLen; i++) {
        double secCTemp[];
        if(CopyClose(SecTicker, PERIOD_CURRENT, times[i], 1, secCTemp) > 0) {
            x[validBars] = priC[i];
            y[validBars] = secCTemp[0];
            meanX += priC[i];
            meanY += secCTemp[0];
            validBars++;
        }
    }

    // 2. Pearson Correlation Calculation
    double rawCorr = 0;
    if(validBars > 1) {
        meanX /= validBars;
        meanY /= validBars;
        double cov = 0, varX = 0, varY = 0;
        
        for(int i = 0; i < validBars; i++) {
            cov += (x[i] - meanX) * (y[i] - meanY);
            varX += MathPow(x[i] - meanX, 2);
            varY += MathPow(y[i] - meanY, 2);
        }
        
        if(varX > 0 && varY > 0) {
            rawCorr = cov / MathSqrt(varX * varY);
        }
    }
    double absCorr = MathAbs(rawCorr);

    // 3. Volatility / ATR Calculations via Handles
    double priAtrArr[1], secAtrArr[1], curPriArr[1], curSecArr[1];
    if(CopyBuffer(atrPriHandle, 0, 0, 1, priAtrArr) <= 0) return 0;
    if(CopyBuffer(atrSecHandle, 0, 0, 1, secAtrArr) <= 0) return 0;
    if(CopyClose(_Symbol, PERIOD_CURRENT, 0, 1, curPriArr) <= 0) return 0;
    if(CopyClose(SecTicker, PERIOD_CURRENT, 0, 1, curSecArr) <= 0) return 0;

    double priAtr = priAtrArr[0];
    double secAtr = secAtrArr[0];
    double priC   = curPriArr[0];
    double secC   = curSecArr[0];

    double priAtrPct = (priC > 0) ? (priAtr / priC) * 100.0 : 0;
    double secAtrPct = (secC > 0) ? (secAtr / secC) * 100.0 : 0;

    // 4. GUI Rendering
    UpdateDashboard(rawCorr, absCorr, priAtrPct, secAtrPct, priAtr, secAtr);

    return rates_total;
}
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

Re: Choosing NAS100 or XAUUSD when both scream risk-off

Post by FTtrader »

And this is last version, Ctrader version:

Code: Select all

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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class OneTicketMatrix : Indicator
    {
        [Parameter("Secondary Ticker", DefaultValue = "XAUUSD")]
        public string SecTicker { get; set; }

        [Parameter("Correlation Period", DefaultValue = 20, MinValue = 5)]
        public int CorrLen { get; set; }

        [Parameter("ATR Period", DefaultValue = 14, MinValue = 1)]
        public int AtrLen { get; set; }

        [Parameter("Warning Threshold", DefaultValue = 0.75, Step = 0.05)]
        public double WarnThresh { get; set; }

        private Bars _secBars;
        private AverageTrueRange _priAtr;
        private AverageTrueRange _secAtr;

        private Grid _grid;
        private TextBlock[,] _cells;
        private Border[,] _borders;

        protected override void Initialize()
        {
            // Load Secondary Symbol & Data
            var secSymbol = Symbols.GetSymbol(SecTicker);
            _secBars = MarketData.GetBars(TimeFrame, SecTicker);

            // Initialize ATRs
            _priAtr = Indicators.AverageTrueRange(Bars, AtrLen, MovingAverageType.Simple);
            _secAtr = Indicators.AverageTrueRange(_secBars, AtrLen, MovingAverageType.Simple);

            // Initialize UI
            InitializeGrid();
        }

        public override void Calculate(int index)
        {
            // Only update on the last bar to save CPU cycles (live tick updates)
            if (!IsLastBar) return;

            if (index < CorrLen) return;

            // 1. Data Fetching & Time Synchronization
            double meanX = 0, meanY = 0;
            int validBars = 0;
            List<double> x = new List<double>();
            List<double> y = new List<double>();

            for (int i = 0; i < CorrLen; i++)
            {
                int priIndex = index - i;
                DateTime timeP = Bars.OpenTimes[priIndex];
                
                // Find matching bar in secondary timeframe
                int secIndex = _secBars.OpenTimes.GetIndexByTime(timeP);

                if (secIndex >= 0)
                {
                    double valX = Bars.ClosePrices[priIndex];
                    double valY = _secBars.ClosePrices[secIndex];

                    x.Add(valX);
                    y.Add(valY);
                    meanX += valX;
                    meanY += valY;
                    validBars++;
                }
            }

            // 2. Pearson Correlation Calculation
            double rawCorr = 0;
            if (validBars > 1)
            {
                meanX /= validBars;
                meanY /= validBars;
                double cov = 0, varX = 0, varY = 0;

                for (int i = 0; i < validBars; i++)
                {
                    cov += (x[i] - meanX) * (y[i] - meanY);
                    varX += Math.Pow(x[i] - meanX, 2);
                    varY += Math.Pow(y[i] - meanY, 2);
                }

                if (varX > 0 && varY > 0)
                {
                    rawCorr = cov / Math.Sqrt(varX * varY);
                }
            }
            double absCorr = Math.Abs(rawCorr);

            // 3. Volatility / ATR Calculations
            double priC = Bars.ClosePrices[index];
            double secC = _secBars.ClosePrices.LastValue;

            double currentPriAtr = _priAtr.Result[index];
            double currentSecAtr = _secAtr.Result[_secBars.ClosePrices.Count - 1];

            double priAtrPct = (priC > 0) ? (currentPriAtr / priC) * 100.0 : 0;
            double secAtrPct = (secC > 0) ? (currentSecAtr / secC) * 100.0 : 0;

            // 4. Update UI
            UpdateDashboard(rawCorr, absCorr, priAtrPct, secAtrPct, currentPriAtr, currentSecAtr);
        }

        private void InitializeGrid()
        {
            _grid = new Grid
            {
                HorizontalAlignment = HorizontalAlignment.Right,
                VerticalAlignment = VerticalAlignment.Bottom,
                BackgroundColor = Color.FromArgb(200, 20, 20, 20),
                Margin = new Thickness(10)
            };

            for (int i = 0; i < 5; i++) _grid.AddRow();
            for (int i = 0; i < 3; i++) _grid.AddColumn();

            _cells = new TextBlock[5, 3];
            _borders = new Border[5, 3];

            for (int r = 0; r < 5; r++)
            {
                for (int c = 0; c < 3; c++)
                {
                    _cells[r, c] = new TextBlock
                    {
                        ForegroundColor = Color.White,
                        Margin = new Thickness(10, 5, 10, 5),
                        VerticalAlignment = VerticalAlignment.Center,
                        HorizontalAlignment = HorizontalAlignment.Center,
                        FontWeight = FontWeight.Bold
                    };

                    _borders[r, c] = new Border
                    {
                        BorderColor = Color.FromArgb(100, 100, 100, 100),
                        BorderThickness = new Thickness(1),
                        Child = _cells[r, c]
                    };

                    _grid.AddChild(_borders[r, c], r, c);
                }
            }

            Chart.AddControl(_grid);
        }

        private void UpdateDashboard(double rawCorr, double absCorr, double priAtrPct, double secAtrPct, double priAtr, double secAtr)
        {
            Color bgTitle = Color.Black;
            Color bgData = Color.FromArgb(255, 40, 40, 40);
            Color corrColor = (absCorr >= WarnThresh) ? Color.Crimson : Color.SeaGreen;
            Color ruleBg = Color.DodgerBlue;

            string tighterAsset = (priAtrPct < secAtrPct) ? Symbol.Name : SecTicker;

            // Row 0: Headers
            SetCell(0, 0, "METRIC", bgTitle);
            SetCell(0, 1, Symbol.Name + " (Pri)", bgTitle);
            SetCell(0, 2, SecTicker + " (Sec)", bgTitle);

            // Row 1: Correlation Status
            SetCell(1, 0, "Correlation", bgData);
            SetCell(1, 1, rawCorr.ToString("F2"), corrColor);
            SetCell(1, 2, (absCorr >= WarnThresh ? "STACKED RISK" : "DIVERSIFIED"), corrColor);

            // Row 2: Normalized Volatility
            SetCell(2, 0, "Volatility (ATR %)", bgData);
            SetCell(2, 1, priAtrPct.ToString("F3") + "%", bgData);
            SetCell(2, 2, secAtrPct.ToString("F3") + "%", bgData);

            // Row 3: Raw Price Invalidation
            SetCell(3, 0, "Avg Inval. (Price)", bgData);
            SetCell(3, 1, priAtr.ToString("F" + Symbol.Digits), bgData);
            
            var secSymbol = Symbols.GetSymbol(SecTicker);
            int secDigits = secSymbol != null ? secSymbol.Digits : 2;
            SetCell(3, 2, secAtr.ToString("F" + secDigits), bgData);

            // Row 4: Actionable Output
            SetCell(4, 0, "SYSTEM RULE", ruleBg);
            string actionText = (absCorr >= WarnThresh) ? $"CHOOSE ONE: {tighterAsset} is tighter." : "Markets decoupled. Trade independent setups.";
            
            SetCell(4, 1, actionText, ruleBg);
            
            // Span the action text across columns 1 and 2
            _cells[4, 2].IsVisible = false;
            _grid.Child(4, 2).IsVisible = false;
            Grid.SetColumnSpan(_borders[4, 1], 2);
        }

        private void SetCell(int row, int col, string text, Color bgColor)
        {
            _cells[row, col].Text = text;
            _borders[row, col].BackgroundColor = bgColor;
            _cells[row, col].IsVisible = true;
            _borders[row, col].IsVisible = true;
        }
    }
}
Post Reply