"""Pure delta-neutral funding-harvest math — faithful 1:1 port of foxhunt
scripts/surfer/crypto_funding_paper.py (qualify + cmd_run booking). No I/O.

Constants and logic match the original exactly:
  * HURDLE      = 5e-4  open a position when trailing-30d mean daily funding (tf30) > 5 bp/day
  * EXIT_HURDLE = 3e-4  hysteresis: keep a HELD coin until its tf30 falls below 3 bp/day
  * LIQ_USD     = 5e6   only coins with > $5M/day quote volume are in the liquid universe (in `liq`)
  * COST_RT     = 1e-3  10 bp round-trip; the run books realized − turnover * (COST_RT/2)

Original `qualify(liq, tf30, prev)` (lines 95-101): loops over the *liquid* universe `liq`
(already volume-filtered + crypto-native + spot-hedgeable in `universe()`/`scan()`), keeping `c` when
its TRAILING-30d-MEAN funding `tf30[c] > HURDLE` or (`c in prev and tf30[c] > EXIT_HURDLE`). Returns
`{c: tf30[c]}`. Held coins outside the liquid universe are NOT kept (the loop is over `liq` only).

Original booking (cmd_run lines 136-141):
  realized = sum(w * last24[c] for c,w in prev.items())   # equal-weight carry on prior book (TODAY's rate)
  turnover = sum(|newpos[c] - prev[c]| over union)
  net      = realized - turnover * (COST_RT / 2)
"""
from __future__ import annotations

HURDLE = 5e-4
EXIT_HURDLE = 3e-4
LIQ_USD = 5e6
COST_RT = 1e-3


def qualify(
    liq: dict[str, float],
    tf30: dict[str, float],
    prev: dict[str, float],
    hurdle: float = HURDLE,
    exit_hurdle: float = EXIT_HURDLE,
) -> dict[str, float]:
    """Coins to hold today, qualified on the TRAILING-30d-MEAN funding `tf30` (NOT today's rate).
    Liquidity/native/hedgeability are already enforced by `liq` (the keys of `liq`). Then hysteresis:
    open new coins whose tf30 > `hurdle`, keep held coins whose tf30 is still > `exit_hurdle`.
    Returns `{c: tf30[c]}` (faithful to original lines 95-101)."""
    q: dict[str, float] = {}
    for c in liq:
        t = tf30.get(c)
        if t is not None and (t > hurdle or (c in prev and t > exit_hurdle)):
            q[c] = t
    return q


def book_return(
    prev: dict[str, float],
    last24: dict[str, float],
    newpos: dict[str, float],
    cost_rt: float = COST_RT,
) -> float:
    """Net realized carry: equal-weight realized funding on the PRIOR book at TODAY's booking rate
    `last24`, minus the round-trip cost on turnover between the prior and new books. Faithful to
    cmd_run (lines 136-141): `net = realized - turnover * (COST_RT / 2)`."""
    realized = sum(w * last24.get(c, 0.0) for c, w in prev.items())
    turnover = sum(
        abs(newpos.get(c, 0.0) - prev.get(c, 0.0))
        for c in set(newpos) | set(prev)
    )
    return realized - turnover * (cost_rt / 2)
