"""XS06b — Correlation-to-market diversifier. Score = -rolling_corr(asset_ret, market_ret, 60) Long the assets LEAST correlated to the equal-weight market (the "divergers"), short the most-correlated ones. win=60 days. Idea: if cross-sectional momentum (XS01) selects by recent past return, this selects by structural independence from the pack — a fundamentally different axis. The two should be weakly correlated. """ import sys sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/xsec") import xslib as xs import numpy as np import pandas as pd def score_corr_diversifier(P, win=60): """Score = -rolling_corr(asset_ret, market_ret, win). Causal.""" n, A = P.ret.shape mkt = xs.market_ret(P.ret) # (n,) equal-weight market out = np.full((n, A), np.nan) mkt_s = pd.Series(mkt) for a in range(A): asset_s = pd.Series(P.ret[:, a]) # rolling correlation — pandas rolling corr is causal corr = asset_s.rolling(win, min_periods=max(10, win // 2)).corr(mkt_s) # score = NEGATIVE correlation: higher => less correlated => long out[:, a] = -corr.values return out # --------------------------------------------------------------------------- # Grid: 5 study_xs calls max # - vary universe (all vs majors) # - vary H (rebalance freq) # - vary long_short # --------------------------------------------------------------------------- print("=" * 70) print("XS06b — Correlation-to-market diversifier (score = -roll_corr_60)") print("=" * 70) best = None best_earns = False best_ho = -999 configs = [ # (label_suffix, universe, H, k, long_short) ("all_H10_k5_ls", "all", 10, 5, True), ("maj_H10_k5_ls", "majors", 10, 5, True), ("all_H5_k5_ls", "all", 5, 5, True), ("all_H10_k5_lo", "all", 10, 5, False), ("all_H20_k5_ls", "all", 20, 5, True), ] results = [] for (suffix, universe, H, k, ls) in configs: name = f"XS06b_{suffix}" print(f"\n--- {name} ---") rep = xs.study_xs( name, lambda P: score_corr_diversifier(P, win=60), universe=universe, H=H, k=k, long_short=ls, target_vol=0.20, ) print(xs.fmt(rep)) print("JSON:", xs.as_json(rep)) results.append(rep) # track best: earns_slot first, then hold-out sharpe earns = rep.get("earns_slot", False) ho_sh = rep.get("holdout", {}).get("sharpe", -999) if (earns and not best_earns) or (earns == best_earns and ho_sh > best_ho): best = rep best_earns = earns best_ho = ho_sh print("\n" + "=" * 70) print("BEST CONFIG:") print(xs.fmt(best)) print("JSON:", xs.as_json(best))