INVITE-ONLY SCRIPT

Fortunato Lead-Lag Multi-Asset (POC) v5_fix2

8
//version=6
indicator("Fortunato Lead-Lag Multi-Asset (POC) v5_fix2", shorttitle="FLL Multi POC v5_fix2", overlay=false, max_lines_count=200, max_labels_count=200)

// ========== USER CONFIG ==========
res = input.timeframe("1", "Resolution for analysis (ex: 1, 5, 3)")
corr_length = input.int(60, "Rolling window (bars) for correlation", minval=10, maxval=500)
max_lag = input.int(5, "Max lag to test (bars)", minval=1, maxval=20)
corr_threshold = input.float(0.60, "Correlation threshold (abs)", step=0.01)
min_lag_for_signal = input.int(1, "Min lag to consider (bars)", minval=0)
plot_lag_as_columns = input.bool(true, "Plot lag as columns")

// --- symbols (change to the exact tickers your feed uses) ---
sym_ndx = input.symbol("NASDAQ:NDX", "NDX (leader candidate) - change if needed")
sym_spx = input.symbol("SPX:SPX", "SPX (follower candidate) - change if needed")

// Optional add-ons
sym_vix = input.symbol("CBOE:VIX", "VIX (volatility index) - optional")
sym_dxy = input.symbol("ICEUS:DXY", "DXY (Dollar Index) - optional")
sym_xau = input.symbol("OANDA:XAUUSD","Gold (XAU/USD) - optional")
sym_oil = input.symbol("NYMEX:CL1!", "Crude Oil (continuous) - optional")
sym_btc = input.symbol("BINANCE:BTCUSDT","Bitcoin (BTC) - optional")

// ========== DATA FETCH (selected resolution) ==========
ndx = request.security(sym_ndx, res, close)
spx = request.security(sym_spx, res, close)
vix = request.security(sym_vix, res, close)
dxy = request.security(sym_dxy, res, close)
xau = request.security(sym_xau, res, close)
oil = request.security(sym_oil, res, close)
btc = request.security(sym_btc, res, close)

// ========== HELPERS ==========
has_history(len) => bar_index >= len

// rolling Pearson correlation implemented with ta.cum differences (replaces ta.sum)
rolling_corr(a, b, n) =>
if not has_history(n)
na
else
// compute rolling sums via cumulative sums
// sum_ab = sum_{k=0..n-1} a[k]*b[k]
float cum_ab = ta.cum(a * b)
float cum_ab_lag = cum_ab[n]
float sum_ab = cum_ab - cum_ab_lag

float cum_a = ta.cum(a)
float cum_a_lag = cum_a[n]
float sum_a = cum_a - cum_a_lag

float cum_b = ta.cum(b)
float cum_b_lag = cum_b[n]
float sum_b = cum_b - cum_b_lag

float cum_a2 = ta.cum(a * a)
float cum_a2_lag = cum_a2[n]
float sum_a2 = cum_a2 - cum_a2_lag

float cum_b2 = ta.cum(b * b)
float cum_b2_lag = cum_b2[n]
float sum_b2 = cum_b2 - cum_b2_lag

float nn = n * 1.0
float num = sum_ab - (sum_a * sum_b) / nn
float den_part_a = sum_a2 - (sum_a * sum_a) / nn
float den_part_b = sum_b2 - (sum_b * sum_b) / nn
float den = den_part_a * den_part_b
if den <= 0.0
na
else
num / math.sqrt(den)

// ========== COMPUTE CORRELATIONS FOR ALL LAGS (USING rolling_corr) ==========
var float[] corr_dir1 = array.new_float()
var float[] corr_dir2 = array.new_float()

// ensure arrays sized correctly each bar
if array.size(corr_dir1) != (max_lag + 1)
array.clear(corr_dir1)
for i = 0 to max_lag
array.push(corr_dir1, na)

if array.size(corr_dir2) != (max_lag + 1)
array.clear(corr_dir2)
for i = 0 to max_lag
array.push(corr_dir2, na)

// fill arrays with correlation values (call rolling_corr every bar for consistency)
for i = 0 to max_lag
float val1 = na
if has_history(corr_length + i) and not na(ndx) and not na(spx)
// ndx aligned with spx shifted by +i (ndx leads spx by i)
val1 := rolling_corr(ndx, spx, corr_length)
array.set(corr_dir1, i, val1)

float val2 = na
if i > 0 and has_history(corr_length + i) and not na(ndx) and not na(spx)
// spx leads ndx by i
val2 := rolling_corr(ndx, spx, corr_length)
array.set(corr_dir2, i, val2)

// ========== FIND BEST ABSOLUTE CORRELATION AND DIRECTION ==========
float best_corr = na
int best_lag = 0
int best_dir = 0 // 1 = ndx -> spx, -1 = spx -> ndx

// scan dir1 (i = 0..max_lag)
for i = 0 to max_lag
float c = array.get(corr_dir1, i)
if not na(c)
if na(best_corr) or math.abs(c) > math.abs(best_corr)
best_corr := c
best_lag := i
best_dir := 1

// scan dir2 (i = 1..max_lag)
for i = 1 to max_lag
float c = array.get(corr_dir2, i)
if not na(c)
if na(best_corr) or math.abs(c) > math.abs(best_corr)
best_corr := c
best_lag := i
best_dir := -1

// ========== MULTI-ASSET LIGHT CONFIRMATION (explicit calls with rolling_corr) ==========
float sum_corr = 0.0
int count_corr = 0

// VIX
float local_best_vix = na
if not na(vix)
for j = 0 to max_lag
if has_history(corr_length + j)
float cc = rolling_corr(ndx, vix[j], corr_length)
if not na(cc)
if na(local_best_vix) or math.abs(cc) > math.abs(local_best_vix)
local_best_vix := cc
if not na(local_best_vix)
sum_corr := sum_corr + local_best_vix
count_corr := count_corr + 1

// DXY
float local_best_dxy = na
if not na(dxy)
for j = 0 to max_lag
if has_history(corr_length + j)
float cc = rolling_corr(ndx, dxy[j], corr_length)
if not na(cc)
if na(local_best_dxy) or math.abs(cc) > math.abs(local_best_dxy)
local_best_dxy := cc
if not na(local_best_dxy)
sum_corr := sum_corr + local_best_dxy
count_corr := count_corr + 1

// XAU
float local_best_xau = na
if not na(xau)
for j = 0 to max_lag
if has_history(corr_length + j)
float cc = rolling_corr(ndx, xau[j], corr_length)
if not na(cc)
if na(local_best_xau) or math.abs(cc) > math.abs(local_best_xau)
local_best_xau := cc
if not na(local_best_xau)
sum_corr := sum_corr + local_best_xau
count_corr := count_corr + 1

// OIL
float local_best_oil = na
if not na(oil)
for j = 0 to max_lag
if has_history(corr_length + j)
float cc = rolling_corr(ndx, oil[j], corr_length)
if not na(cc)
if na(local_best_oil) or math.abs(cc) > math.abs(local_best_oil)
local_best_oil := cc
if not na(local_best_oil)
sum_corr := sum_corr + local_best_oil
count_corr := count_corr + 1

// BTC
float local_best_btc = na
if not na(btc)
for j = 0 to max_lag
if has_history(corr_length + j)
float cc = rolling_corr(ndx, btc[j], corr_length)
if not na(cc)
if na(local_best_btc) or math.abs(cc) > math.abs(local_best_btc)
local_best_btc := cc
if not na(local_best_btc)
sum_corr := sum_corr + local_best_btc
count_corr := count_corr + 1

float confirm_avg = na
if count_corr > 0
confirm_avg := sum_corr / count_corr

// ========== SIGNAL LOGIC ==========
bool lead_detected = false
string lead_direction_text = "NoLeader"
if not na(best_corr) and math.abs(best_corr) >= corr_threshold and best_lag >= min_lag_for_signal
lead_detected := true
lead_direction_text := best_dir == 1 ? "NDX -> SPX" : (best_dir == -1 ? "SPX -> NDX" : "NoLeader")

// ========== PLOTS (GLOBAL) ==========
plot_best_corr = best_corr
plot_best_lag = (lead_detected ? best_lag : na)
plot_confirm_avg = confirm_avg

plot(plot_best_corr, title="Best Corr (signed)", linewidth=2)
hline(0, "zero", linestyle=hline.style_dashed)
hline(corr_threshold, "threshold +", linestyle=hline.style_solid)
hline(-corr_threshold, "threshold -", linestyle=hline.style_solid)

plot(plot_lag_as_columns ? plot_best_lag : na, title="Best Lag (bars)", style=plot.style_columns, linewidth=2)
plot(not na(plot_confirm_avg) ? plot_confirm_avg : na, title="Multi-asset confirm (avg)", linewidth=1, style=plot.style_line)

// ========== LABEL MANAGEMENT ==========
var label lbl = na
if lead_detected and barstate.isconfirmed
if not na(lbl)
label.delete(lbl)
lbl := label.new(bar_index, plot_best_corr, text="Lead: " + lead_direction_text + " lag:" + str.tostring(best_lag) + " corr:" + str.tostring(best_corr, "#.##"),
style=label.style_label_left, color=color.new(color.green, 75), textcolor=color.white, size=size.small)

// ========== ALERTS ==========
alertcondition(lead_detected and best_dir == 1, title="NDX leads SPX detected", message="NDX leads SPX — lag: {{plot_1}} corr: {{plot_0}}")
alertcondition(lead_detected and best_dir == -1, title="SPX leads NDX detected", message="SPX leads NDX — lag: {{plot_1}} corr: {{plot_0}}")

// ========== INFORMATION TABLE ==========
var table t = table.new(position.top_right, 1, 5, border_width=1)
if barstate.islast
table.cell(t, 0, 0, "Resolution: " + res)
table.cell(t, 0, 1, "Best corr: " + (na(best_corr) ? "na" : str.tostring(best_corr, "#.##")))
table.cell(t, 0, 2, "Best lag: " + (na(best_lag) ? "na" : str.tostring(best_lag)))
table.cell(t, 0, 3, "Direction: " + lead_direction_text)
table.cell(t, 0, 4, "Confirm avg: " + (na(confirm_avg) ? "na" : str.tostring(confirm_avg, "#.##")))

免責事項

この情報および投稿は、TradingViewが提供または推奨する金融、投資、トレード、その他のアドバイスや推奨を意図するものではなく、それらを構成するものでもありません。詳細は利用規約をご覧ください。