To transition this tool to TradingView (Pine Script v5), we have to address a fundamental difference between the platforms: TradingView cannot read your live brokerage account or open positions.
Because Pine Script runs securely in a web browser, it doesn't know what you are currently trading on your broker. To replicate the exact same logic—including the lot size filter—you will need to tell the indicator which pairs you are trading and what your position sizes are via the indicator's settings menu.
Here is the complete Pine Script adaptation. It creates a sleek, visual table (HUD) on your chart, calculates the correlation dynamically, applies your lot size filter, and uses TradingView's native alerting system.
The Pine Script (v5)
Code: Select all
//@version=5
indicator("Portfolio Correlation Monitor [HUD]", overlay=true, max_labels_count=0)
// --- Settings & Filters ---
lookback = input.int(100, "Correlation Periods", group="Core Settings")
minLot = input.float(0.50, "Minimum Lot Size Filter", group="Core Settings", tooltip="Positions smaller than this will be ignored in the calculation.")
alertThresh = input.float(0.80, "Alert Threshold", group="Core Settings")
// --- Manual Position Inputs ---
// Because TradingView cannot read your live broker data, you toggle your active trades here.
g1 = "Position 1"
u1 = input.bool(true, "Enable", inline="p1", group=g1)
s1 = input.symbol("FX:EURUSD", "", inline="p1", group=g1)
l1 = input.float(1.0, "Lot Size", inline="p1", group=g1)
g2 = "Position 2"
u2 = input.bool(true, "Enable", inline="p2", group=g2)
s2 = input.symbol("FX:GBPUSD", "", inline="p2", group=g2)
l2 = input.float(1.0, "Lot Size", inline="p2", group=g2)
g3 = "Position 3"
u3 = input.bool(false, "Enable", inline="p3", group=g3)
s3 = input.symbol("FX:USDJPY", "", inline="p3", group=g3)
l3 = input.float(0.1, "Lot Size", inline="p3", group=g3)
g4 = "Position 4"
u4 = input.bool(false, "Enable", inline="p4", group=g4)
s4 = input.symbol("FX:AUDUSD", "", inline="p4", group=g4)
l4 = input.float(1.0, "Lot Size", inline="p4", group=g4)
g5 = "Position 5"
u5 = input.bool(false, "Enable", inline="p5", group=g5)
s5 = input.symbol("FX:USDCAD", "", inline="p5", group=g5)
l5 = input.float(1.0, "Lot Size", inline="p5", group=g5)
// --- Fetch Price Data ---
c1 = request.security(s1, timeframe.period, close)
c2 = request.security(s2, timeframe.period, close)
c3 = request.security(s3, timeframe.period, close)
c4 = request.security(s4, timeframe.period, close)
c5 = request.security(s5, timeframe.period, close)
// --- Filter Logic ---
v1 = u1 and l1 >= minLot
v2 = u2 and l2 >= minLot
v3 = u3 and l3 >= minLot
v4 = u4 and l4 >= minLot
v5 = u5 and l5 >= minLot
// --- Calculate Correlations (Pearson) ---
corr12 = ta.correlation(c1, c2, lookback)
corr13 = ta.correlation(c1, c3, lookback)
corr14 = ta.correlation(c1, c4, lookback)
corr15 = ta.correlation(c1, c5, lookback)
corr23 = ta.correlation(c2, c3, lookback)
corr24 = ta.correlation(c2, c4, lookback)
corr25 = ta.correlation(c2, c5, lookback)
corr34 = ta.correlation(c3, c4, lookback)
corr35 = ta.correlation(c3, c5, lookback)
corr45 = ta.correlation(c4, c5, lookback)
// Build arrays to dynamically size the table
var pair_names = array.new_string()
var pair_corrs = array.new_float()
array.clear(pair_names)
array.clear(pair_corrs)
if v1 and v2
array.push(pair_names, s1 + " & " + s2)
array.push(pair_corrs, corr12)
if v1 and v3
array.push(pair_names, s1 + " & " + s3)
array.push(pair_corrs, corr13)
if v1 and v4
array.push(pair_names, s1 + " & " + s4)
array.push(pair_corrs, corr14)
if v1 and v5
array.push(pair_names, s1 + " & " + s5)
array.push(pair_corrs, corr15)
if v2 and v3
array.push(pair_names, s2 + " & " + s3)
array.push(pair_corrs, corr23)
if v2 and v4
array.push(pair_names, s2 + " & " + s4)
array.push(pair_corrs, corr24)
if v2 and v5
array.push(pair_names, s2 + " & " + s5)
array.push(pair_corrs, corr25)
if v3 and v4
array.push(pair_names, s3 + " & " + s4)
array.push(pair_corrs, corr34)
if v3 and v5
array.push(pair_names, s3 + " & " + s5)
array.push(pair_corrs, corr35)
if v4 and v5
array.push(pair_names, s4 + " & " + s5)
array.push(pair_corrs, corr45)
// --- Averages & Alerts ---
float sum_corr = 0.0
int cnt = array.size(pair_corrs)
if cnt > 0
for i = 0 to cnt - 1
sum_corr += array.get(pair_corrs, i)
avg_corr = cnt > 0 ? sum_corr / cnt : na
// Send TradingView Alert if correlation crosses the threshold
if ta.crossover(avg_corr, alertThresh)
alert("RISK WARNING: Portfolio correlation crossed above " + str.tostring(alertThresh) + " (Currently: " + str.tostring(avg_corr, "#.##") + ")", alert.freq_once_per_bar)
// --- Table UI (HUD) ---
var table hud = table.new(position.top_right, 2, 12, border_width=1, border_color=color.new(color.gray, 50), frame_color=color.new(color.gray, 50), frame_width=1)
if barstate.islast
if cnt < 1
table.cell(hud, 0, 0, "Correlation Monitor", text_color=color.white, bgcolor=color.new(color.blue, 30), text_halign=text.align_center)
table.cell(hud, 0, 1, "Not enough valid positions.\nCheck toggles & Lot Size filter.", text_color=color.gray, bgcolor=color.new(color.black, 10))
else
table.cell(hud, 0, 0, "Pair", text_color=color.white, bgcolor=color.new(color.blue, 30))
table.cell(hud, 1, 0, "Correlation", text_color=color.white, bgcolor=color.new(color.blue, 30))
for i = 0 to cnt - 1
c_val = array.get(pair_corrs, i)
// Clean up the prefix "FX:" or "OANDA:" to make the table readable
clean_name = str.replace_all(array.get(pair_names, i), "FX:", "")
clean_name := str.replace_all(clean_name, "OANDA:", "")
row_color = c_val >= alertThresh ? color.new(color.red, 70) : color.new(color.black, 40)
table.cell(hud, 0, i+1, clean_name, text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(hud, 1, i+1, str.tostring(c_val, "#.##"), text_color=color.white, bgcolor=row_color)
// Average Row
avg_color = avg_corr >= alertThresh ? color.new(color.red, 30) : color.new(color.blue, 30)
table.cell(hud, 0, cnt+1, "AVERAGE", text_color=color.white, bgcolor=color.new(color.blue, 30))
table.cell(hud, 1, cnt+1, str.tostring(avg_corr, "#.##"), text_color=color.white, bgcolor=avg_color)