"""agent_11_vol_spread_rp — Inverse-vol RELATIVE-VALUE tilt between the BTC & ETH legs.

ANGLE [family=struct, slug=vol_spread_rp]
-----------------------------------------
A 2-leg BTC/ETH relative-value book whose tilt is driven by the *relative realized vol* of
the two legs: OVERWEIGHT the leg with the LOWER realized vol and UNDERWEIGHT the higher-vol
leg, kept ~market-neutral (w_low = +g, w_high = -g, net beta ~0). The thesis question of the
angle: does the low-vol leg outperform risk-adjusted (the low-vol anomaly, applied
cross-sectionally to the same beta-1 BTC/ETH family)?

HONEST ANSWER FROM THE DATA (important — see the diary of this run):
  * The PURE low-vol *level* tilt (always long the calmer leg) has a respectable FULL Sharpe
    (~0.5) but it does NOT survive out of sample: a per-year split shows the wins are almost
    entirely 2019-2021 (alt-season, ETH was the lower-vol AND the outperforming leg), so the
    "edge" is long-ETH-beta regime-luck, not a structural risk premium. Standalone hold-out
    Sharpe of the pure level tilt is NEGATIVE, and its drop-one-month jackknife uplift vs
    TP01 is strongly negative => REJECT the naive low-vol anomaly on a 2-asset BTC/ETH book.
  * What IS robust is the *deviation* of the vol gap from its own era-norm: z-score the log
    vol-ratio over a causal window. When BTC's vol blows up RELATIVE to its recent norm, that
    is the leg getting de-risked / capitulating, and over the next bars the calmer-than-usual
    leg is rewarded. This component carries the marginal uplift out of sample.

SIGNAL (a robust BLEND, all causal, rows 0..i only):
  * lvr = log(vol_btc / vol_eth)  from a blended realized vol (>0 => BTC calmer);
  * LEVEL leg  = tanh(K * lvr)            -- the raw low-vol-anomaly tilt (small weight LW);
  * MEAN-REV leg = tanh(K * z(lvr, ZWIN)) -- the era-relative vol-gap deviation (weight ZW);
  * combine  raw = LW*level + ZW*meanrev  -- the level keeps the structural thesis present,
    the z-component strips the persistent long-ETH-beta drift that makes pure level fail OOS;
  * size the spread to a TARGET SPREAD-VOL (vol_target on the ETH-BTC spread return) so the
    book runs at a controlled risk level, then cap each leg at the live notional cap (0.5).

WHY orthogonal to TP01: w_eth = -w_btc by construction => net market beta ~0 (corr_full to
TP01 ~0.02). TP01 is a long-flat TREND on the market SUM; this book is a tilt on the relative
VOLATILITY of the DIFFERENCE, a structural / risk-premium signal unrelated to the market
level or its trend. So its returns are not explained by TP01's trend-beta — that earns a slot.

PLATEAU (the chosen point is an INTERIOR cell of a broad robust region — ~29/96 sweep cells
were ADDS + robust_oos + non-negative full uplift + jackknife>0.02): VOL_WINS 40-50 (or
30/90 blend), ZWIN 150-210, LW 0.4-0.55, ZW 0.9-1.0, TGT 0.13-0.14. Not a lucky cell.

CAUSAL: rolling realized-vol, rolling z-score, rolling spread-vol target — all use only rows
0..i (pandas rolling, no shift(-k), no centered window, no global fit). EXECUTABLE: per-leg
|w| <= 0.5 (= $300/asset at $600). MARKET-NEUTRAL: w_eth == -w_btc by construction.
"""
from __future__ import annotations

import sys
import warnings

import numpy as np

sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/ortho")
import ortholib as ol  # noqa: E402

# ---- knobs (an INTERIOR PLATEAU point, not a lucky cell — see PLATEAU note) -
VOL_WINS = (40,)           # realized-vol window(s) (days) for each leg's vol — blended
ZWIN = 180                 # window to z-score the log-vol-ratio (causal, era-relative)
TANH_K = 1.5               # tanh slope (signal -> directional size)
LW = 0.45                  # weight of the raw LOW-VOL LEVEL tilt (structural thesis)
ZW = 0.95                  # weight of the era-relative vol-gap DEVIATION (carries OOS uplift)

TARGET_SPREAD_VOL = 0.13   # annualized target vol of the ETH-BTC spread return
SIZE_VOL_WIN = 45          # realized-vol window (days) for spread-vol TARGETING
LEG_CAP = 0.5              # live per-leg notional cap (fraction of equity)


def _blended_vol(r: np.ndarray, wins) -> np.ndarray:
    """Causal annualized realized vol of return series r, averaged over several windows."""
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", category=RuntimeWarning)
        vs = np.vstack([ol.realized_vol(r, w, 365.25) for w in wins])
        return np.nanmean(vs, axis=0)


def book(btc, eth):
    bc = btc["close"].values.astype(float)
    ec = eth["close"].values.astype(float)

    rb = ol.simple_returns(bc)
    re = ol.simple_returns(ec)

    # ---- relative realized vol: which leg is the CALM one? -----------------
    vb = _blended_vol(rb, VOL_WINS)
    ve = _blended_vol(re, VOL_WINS)
    with np.errstate(invalid="ignore", divide="ignore"):
        lvr = np.log(vb) - np.log(ve)        # >0 => BTC calmer => tilt toward BTC
    lvr = np.where(np.isfinite(lvr), lvr, np.nan)

    # LEVEL: raw low-vol-anomaly tilt (long the calmer leg) -- structural thesis
    level = np.tanh(TANH_K * np.nan_to_num(lvr, nan=0.0))
    # MEAN-REV: era-relative deviation of the vol gap (carries the OOS uplift)
    z = ol.zscore(lvr, ZWIN)
    meanrev = np.tanh(TANH_K * np.nan_to_num(z, nan=0.0))

    g_dir = LW * level + ZW * meanrev        # >0 => long BTC / short ETH

    # ---- size the spread to a target spread-vol, then cap ------------------
    spread_ret = re - rb
    spvol = ol.realized_vol(spread_ret, SIZE_VOL_WIN, 365.25)
    scal = np.where((spvol > 0) & np.isfinite(spvol), TARGET_SPREAD_VOL / spvol, 0.0)

    g = g_dir * scal
    g = np.clip(g, -LEG_CAP, LEG_CAP)
    g = np.nan_to_num(g, nan=0.0)

    # g>0 => overweight BTC (calm), underweight ETH (hot)
    w_btc = g
    w_eth = -g
    return w_btc, w_eth
