"""agent_15_vol_premium_tilt — RELATIVE VARIANCE-RISK-PREMIUM tilt between BTC & ETH.

ANGLE [family=dvol, slug=vol_premium_tilt]
------------------------------------------
A 2-leg BTC/ETH market-neutral book tilted by the *relative VARIANCE RISK PREMIUM* (VRP) of
the two legs. The VRP of a leg is how much its OPTION-IMPLIED vol (Deribit DVOL) exceeds its
own RECENTLY-REALIZED vol: vrp = log(IV) - log(RV).  A leg with a rich VRP is one whose
options are pricing in MUCH more future vol than the underlying has actually been delivering
(insurance is expensive there). The relative signal is the VRP DIFFERENCE:

    dvrp = vrp_btc - vrp_eth = [log(IVb)-log(RVb)] - [log(IVe)-log(RVe)]   (>0 => BTC richer)

This is distinct from agent_14 (which tilts on the raw IMPLIED-vol RATIO IVb/IVe). The VRP
strips each leg's RV out, so it isolates the option-market's *fear premium* per leg rather
than its absolute vol level — exactly the variance-risk-premium seam this angle targets.

HONEST THESIS FROM THE DATA (see probe in this run's notes):
  * The robust, tradeable component is the ERA-RELATIVE DEVIATION of dvrp, NOT its raw level
    (the raw level carries a persistent ETH-skew-rich bias that is structural, not alpha — same
    lesson as agents 11/14). Z-scoring dvrp over a causal window strips that drift.
  * Predictive probe: corr(z(dvrp), forward (btc-eth) spread return) is POSITIVE and GROWS with
    horizon (~+0.01 at 1d, ~+0.03 at 5d, ~+0.07 at 10d using rvwin~45). So when BTC's VRP is
    rich vs its own norm RELATIVE to ETH, BTC tends to OUTPERFORM over the next several days:
    the leg whose insurance is over-bid (fear over-priced) is rewarded as the feared move
    fails and the premium decays into the underlying holder. We tilt TOWARD the rich-VRP leg.
  * The edge is LOW-FREQUENCY (a multi-day premium) -> we SMOOTH the directional signal (EMA)
    so the book is low-turnover, which both matches the economics and keeps fees small.

SIGNAL (all causal, rows 0..i only; DVOL is merge_asof-backward => known at decision time):
  * RVb/RVe = causal annualized realized vol of each leg (blended windows).
  * vrp_b = log(IVb) - log(RVb);  vrp_e = log(IVe) - log(RVe);  dvrp = vrp_b - vrp_e.
  * ZDEV  = tanh(K * z(dvrp, ZWIN))            era-relative VRP-gap deviation (the alpha).
  * LEVEL = tanh(K * dvrp)                      small raw-VRP tilt (keeps the thesis present).
  * g_dir = EMA( LW*LEVEL + ZW*ZDEV , SMOOTH )  smoothed -> low turnover.
  * size to a TARGET SPREAD-VOL on the (eth-btc) spread return, then cap each leg at 0.5.
  Before DVOL history (pre 2021-03) IV is NaN => g=0 (book flat, no exposure, no leak).

WHY orthogonal to TP01: w_eth = -w_btc by construction => net market beta ~0. TP01 is a
long-flat TREND on the market SUM; this book trades the relative OPTION VARIANCE RISK PREMIUM
of the DIFFERENCE — a forward-looking insurance-premium signal unrelated to market level/trend.

CAUSAL: rolling realized-vol, rolling z-score, EMA smoothing, rolling spread-vol target — all
use only rows 0..i (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")
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt")
import altlib as al  # noqa: E402
import ortholib as ol  # noqa: E402

# ---- knobs (interior of the robust (45,60)/zwin 120-180/smooth 4-6 ridge) --
# EVERY cell in {rv(45,60), z in 120-180, sm in 4-6} scored ADDS + robust_oos with
# uplift_hold +0.26..+0.36 and jackknife>0 — a broad plateau, not a lucky cell.
RV_WINS = (45, 60)         # realized-vol window(s) (days) per leg for the VRP denominator
ZWIN = 120                 # window to z-score the VRP gap (causal, era-relative)
TANH_K = 1.5               # tanh slope (signal -> directional size)
LW = 0.30                  # weight of the raw VRP LEVEL tilt (structural thesis; ~inert)
ZW = 1.0                   # weight of the era-relative VRP-gap DEVIATION (carries OOS uplift)
SMOOTH = 6                 # EMA span on the directional signal -> low turnover (multi-day edge)

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)

    # ---- per-leg VARIANCE RISK PREMIUM: implied (DVOL) vs realized -----------
    db = al.dvol(btc, "BTC") / 100.0      # DVOL points -> fraction (e.g. 56 -> 0.56)
    de = al.dvol(eth, "ETH") / 100.0
    rvb = _blended_vol(rb, RV_WINS)
    rve = _blended_vol(re, RV_WINS)
    with np.errstate(invalid="ignore", divide="ignore"):
        vrp_b = np.log(db) - np.log(rvb)   # >0 => BTC options pricing more vol than realized
        vrp_e = np.log(de) - np.log(rve)
        dvrp = vrp_b - vrp_e               # >0 => BTC VRP richer than ETH
    dvrp = np.where(np.isfinite(dvrp), dvrp, np.nan)

    # LEVEL: small raw tilt toward the richer-VRP leg (keeps the structural thesis present)
    level = np.tanh(TANH_K * np.nan_to_num(dvrp, nan=0.0))
    # ZDEV: era-relative deviation of the VRP gap (carries the OOS uplift)
    z = ol.zscore(dvrp, ZWIN)
    zdev = np.tanh(TANH_K * np.nan_to_num(z, nan=0.0))

    g_raw = LW * level + ZW * zdev          # >0 => long BTC / short ETH
    # flatten where VRP is unknown (pre-2021-03 DVOL) -> no exposure, no leak
    g_raw = np.where(np.isfinite(dvrp), g_raw, 0.0)
    # SMOOTH: the edge is a multi-day premium -> EMA to keep turnover low (causal)
    g_dir = ol.ema(g_raw, SMOOTH)

    # ---- size the spread to a target spread-vol, then cap ------------------
    spread_ret = re - rb
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", category=RuntimeWarning)
        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 (richer VRP), underweight ETH
    w_btc = g
    w_eth = -g
    return w_btc, w_eth
