test: cross-market connectedness = diversification, not alpha
User intuited markets are connected -> exploitable? Measured equity/bond/gold/commod/trend/crypto (2019-26): contemporaneous corr low (+0.03 avg; equity-crypto +0.43, equity-trend -0.28), crisis corr STABLE (didn't spike -> diversification held in stress), lead-lag ~0 (trade OOS Sharpe -1.01). Connectedness is priced-in -> value is diversification (already in the book, validates it beating 60/40 low-DD), not a predictive signal. Same efficient wall for cross-market alpha. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
72
scripts/surfer/xcorr_leadlag.py
Normal file
72
scripts/surfer/xcorr_leadlag.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Cross-market connectedness: contemporaneous correlation, crisis-correlation spike, and lead-lag.
|
||||
|
||||
Streams: equity, bond, gold, commod(=energy proxy CL), trend, crypto. Three questions:
|
||||
1. CONTEMPORANEOUS corr matrix — how connected are they (the diversification basis)?
|
||||
2. CRISIS corr — do correlations spike on risk-off days (diversification breaks)?
|
||||
3. LEAD-LAG — does market A's return predict B's NEXT return (a tradeable cross-market signal)?
|
||||
+ a simple OOS test of the best lead-lag pair net of cost. Honest prior: ~0 (efficient).
|
||||
"""
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from multistrat_book import build_streams # raw stream daily returns # noqa: E402
|
||||
|
||||
|
||||
def main():
|
||||
streams, days, year = build_streams()
|
||||
names = list(streams)
|
||||
R = np.column_stack([streams[n] for n in names])
|
||||
R = np.nan_to_num(R)
|
||||
T, S = R.shape
|
||||
eq = names.index("equity")
|
||||
|
||||
print(f"CROSS-MARKET CONNECTEDNESS — {S} streams, {T} days ({names})")
|
||||
print("\n (1) CONTEMPORANEOUS correlation:")
|
||||
C = np.corrcoef(R.T)
|
||||
print(" " + " ".join(f"{n[:5]:>6}" for n in names))
|
||||
for i, n in enumerate(names):
|
||||
print(f" {n[:5]:>5} " + " ".join(f"{C[i,j]:>+6.2f}" for j in range(S)))
|
||||
off = (C.sum() - S) / (S * S - S)
|
||||
print(f" avg off-diagonal: {off:+.2f} (low = good diversification)")
|
||||
|
||||
# (2) crisis correlation: risk-off days (equity < -1%)
|
||||
riskoff = R[:, eq] < -0.01
|
||||
Cn = np.corrcoef(R[~riskoff].T); Cc = np.corrcoef(R[riskoff].T)
|
||||
offn = (Cn.sum() - S) / (S * S - S); offc = (Cc.sum() - S) / (S * S - S)
|
||||
print(f"\n (2) avg corr NORMAL days: {offn:+.2f} RISK-OFF days (equity<-1%): {offc:+.2f}")
|
||||
print(f" -> correlations {'SPIKE (diversification breaks in crises)' if offc > offn + 0.1 else 'stable'}")
|
||||
|
||||
# (3) lead-lag: corr(A[t], B[t+1])
|
||||
print("\n (3) LEAD-LAG corr(A[t] -> B[t+1]) (rows=A leads, cols=B follows):")
|
||||
LL = np.zeros((S, S))
|
||||
for i in range(S):
|
||||
for j in range(S):
|
||||
a = R[:-1, i]; b = R[1:, j]
|
||||
LL[i, j] = np.corrcoef(a, b)[0, 1]
|
||||
print(" " + " ".join(f"{n[:5]:>6}" for n in names))
|
||||
for i, n in enumerate(names):
|
||||
print(f" {n[:5]:>5} " + " ".join(f"{LL[i,j]:>+6.2f}" for j in range(S)))
|
||||
# strongest |lead-lag| pair (excluding self)
|
||||
LLm = LL.copy(); np.fill_diagonal(LLm, 0)
|
||||
i, j = np.unravel_index(np.argmax(np.abs(LLm)), LLm.shape)
|
||||
print(f" strongest: {names[i]}[t] -> {names[j]}[t+1] = {LL[i,j]:+.3f}")
|
||||
|
||||
# OOS test of the strongest lead-lag pair: trade B by sign of A, net 10bp
|
||||
split = int(0.6 * T)
|
||||
sig = np.sign(R[:-1, i]) * np.sign(LL[i, j]) # follow the lead direction
|
||||
pnl = sig * R[1:, j] - 0.0010 * np.abs(np.diff(np.concatenate([[0], sig])))
|
||||
def sh(x):
|
||||
x = x[np.isfinite(x)]; return x.mean() / (x.std() + 1e-9) * math.sqrt(252) if x.std() > 0 else float("nan")
|
||||
print(f"\n lead-lag trade ({names[i]}->{names[j]}) net 10bp: IS Sharpe {sh(pnl[:split]):+.2f} OOS Sharpe {sh(pnl[split:]):+.2f}")
|
||||
print("\n VERDICT: contemporaneous corr low (diversification basis) + crisis-spike (risk to manage) = the")
|
||||
print(" connection's REAL value is risk/diversification. Lead-lag OOS Sharpe ~0 = no tradeable prediction")
|
||||
print(" (efficient: the connection is already priced). If OOS lead-lag >1, a real cross-market signal exists.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user