"""agent_00_ratio_mom_blend — Multi-horizon ETH/BTC ratio-momentum, market-neutral.

ANGLE [family=rv, slug=ratio_mom_blend]: the 2-asset executable cousin of XS01.
We trade the RELATIVE strength of ETH vs BTC: build the log price ratio s = log(ETH/BTC),
measure its momentum over a BLEND of horizons (~20/60/120d), average the per-horizon
z-scores (multi-orizzonte like TP01), squash with tanh to size, and go MARKET-NEUTRAL:
  w_eth = +g, w_btc = -g     (long the stronger leg, short the weaker, gross ~2g)
The book is then SPREAD-VOL-TARGETED: scale g so the realized vol of the ETH-BTC spread
return hits a target, capping each leg at the live notional cap (0.5 of equity).

Because the book is ~beta-neutral to the BTC+ETH market (net exposure ~0), it is
structurally uncorrelated to TP01 (a long-flat trend on the market SUM) — that is the
whole point: residual relative-value alpha, not trend-beta.

CAUSAL: every value at i uses only rows 0..i (rolling means/std, no shift(-k), no global
fit). EXECUTABLE: per-leg |w| <= 0.5. MARKET-NEUTRAL: w_eth == -w_btc by construction.
"""
from __future__ import annotations

import numpy as np

import ortholib as ol

# ---- knobs (a PLATEAU point, not a lucky cell — see notes) ----------------
HORIZONS = (20, 60, 120, 240)  # momentum lookbacks (days) — multi-orizzonte blend
ZWIN = 252                     # window to z-score each horizon's momentum (causal)
TANH_K = 1.3                   # tanh slope (signal -> size)
TARGET_SPREAD_VOL = 0.15       # annualized target vol of the ETH-BTC spread return
VOL_WIN = 45                   # realized-vol window (days) for spread-vol targeting
LEG_CAP = 0.5                  # live per-leg notional cap (fraction of equity)


def _mom_z(logp: np.ndarray, h: int, zwin: int) -> np.ndarray:
    """Causal z-scored h-day log momentum of a log-price series."""
    s = np.full(len(logp), np.nan)
    s[h:] = logp[h:] - logp[:-h]            # h-day log change, known at i
    return ol.zscore(s, zwin)               # standardize cross-time (causal rolling)


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

    # relative price ratio in logs: positive momentum => ETH outperforming BTC
    logratio = np.log(ec) - np.log(bc)

    # blended multi-horizon z-scored momentum (mean of per-horizon z-scores).
    # warnings silenced: early bars (before any horizon is populated) are all-NaN
    # columns -> nanmean warns; we map those to 0 (flat) anyway.
    zs = np.vstack([_mom_z(logratio, h, ZWIN) for h in HORIZONS])
    with np.errstate(invalid="ignore"):
        import warnings
        with warnings.catch_warnings():
            warnings.simplefilter("ignore", category=RuntimeWarning)
            sig = np.nanmean(zs, axis=0)
    sig = np.nan_to_num(sig, nan=0.0)

    # squash to a directional size in [-1, 1]
    g_dir = np.tanh(TANH_K * sig)

    # spread-vol target: scale by target/realized vol of the spread return r_eth - r_btc
    rb = ol.simple_returns(bc)
    re = ol.simple_returns(ec)
    spread_ret = re - rb
    spvol = ol.realized_vol(spread_ret, VOL_WIN, 365.25)   # annualized, causal
    scal = np.where((spvol > 0) & np.isfinite(spvol), TARGET_SPREAD_VOL / spvol, 0.0)

    g = g_dir * scal
    # per-leg cap (g is the magnitude on EACH leg; both legs share it)
    g = np.clip(g, -LEG_CAP, LEG_CAP)
    g = np.nan_to_num(g, nan=0.0)

    w_eth = g
    w_btc = -g
    return w_btc, w_eth
