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.
Choosing NAS100 or XAUUSD when both scream risk-off
-
LondonScalper
- Posts: 701
- Joined: Sat Sep 05, 2026 7:54 am
Re: Choosing NAS100 or XAUUSD when both scream risk-off
Hi LondonScalper,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.
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.
Re: Choosing NAS100 or XAUUSD when both scream risk-off
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.
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)Re: Choosing NAS100 or XAUUSD when both scream risk-off
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.
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.
Re: Choosing NAS100 or XAUUSD when both scream risk-off
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.
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.
Re: Choosing NAS100 or XAUUSD when both scream risk-off
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);
}Re: Choosing NAS100 or XAUUSD when both scream risk-off
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;
}Re: Choosing NAS100 or XAUUSD when both scream risk-off
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;
}
}
}