"""Pure domain for the token-unlock dilution-short edge — supply-shock sizing + risk-managed,
market-neutral short weights. No I/O.

Economic thesis: scheduled vesting cliffs to insiders/investors force dilution selling; the market
front-runs the known cliff date, so the name underperforms in the ~10 days BEFORE the cliff. Harvested
market-neutral (short the alt perp + long BTC), it is alpha (survives BTC-hedge), uncorrelated to the
funding edge (corr ~0), and a different driver (supply, not leverage). The naive equal-weight book runs
at huge vol (−74% DD); this module supplies the risk controls that tame it: inverse-vol per-name sizing,
a per-name weight cap, and a per-position squeeze stop (the short-book killer).
"""
from __future__ import annotations

BEARISH_BUCKETS = {"insiders", "privateSale"}   # VC/team/investor = structurally-selling cliffs


def supply_shock(unlock_tokens: float, price: float, adv_usd: float) -> float | None:
    """Supply-shock magnitude = unlock notional / average daily volume (in days-of-volume).
    Better than %-of-float for tradeability: it measures selling pressure relative to liquidity.
    None if inputs are unusable."""
    if price <= 0 or adv_usd <= 0 or unlock_tokens <= 0:
        return None
    return (unlock_tokens * price) / adv_usd


def inverse_vol_capped_weights(vols: dict[str, float], max_name_weight: float) -> dict[str, float]:
    """Inverse-vol weights over the active short names, each capped at `max_name_weight`, renormalized to
    gross 1 (iterative cap-and-redistribute). Thin/volatile alts (squeeze-prone) get LESS; the cap stops
    any single short dominating. Empty / all-zero-vol -> equal weight."""
    inv = {s: (1.0 / v) for s, v in vols.items() if v and v > 1e-12}
    if not inv:
        n = len(vols)
        return {s: 1.0 / n for s in vols} if n else {}
    g = sum(inv.values())
    w = {s: x / g for s, x in inv.items()}
    for _ in range(len(w)):
        over = {s: x for s, x in w.items() if x > max_name_weight + 1e-12}
        if not over:
            break
        free = {s: x for s, x in w.items() if s not in over}
        free_tot = sum(free.values())
        remaining = max(0.0, 1.0 - max_name_weight * len(over))
        w = {s: (max_name_weight if s in over else (free[s] / free_tot * remaining if free_tot > 1e-12 else 0.0))
             for s in w}
    return w


def short_leg_pnl(name_ret: float, btc_ret: float, funding: float, *, daily_stop: float) -> float:
    """Per-unit-weight daily P&L of one market-neutral unlock short: short the name (+ when it falls),
    long BTC hedge, plus funding the short RECEIVES (positive funding = longs pay shorts). The combined
    per-name return is floored at -daily_stop — the SQUEEZE STOP: if the name rips, the position is
    stopped out at -daily_stop rather than bleeding unboundedly (the short-book killer)."""
    raw = (-name_ret) + btc_ret + funding         # short name + long BTC + funding received
    return raw if raw > -daily_stop else -daily_stop
