fix(xsfunding): guard live snapshot against decoupled-basis tickers (LIT-type artifacts)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,8 +14,9 @@ _SPOT = "https://api.binance.com"
|
||||
|
||||
|
||||
class BinanceXsFundingLive:
|
||||
def __init__(self, funding: Any | None = None) -> None:
|
||||
def __init__(self, funding: Any | None = None, *, max_basis: float = 0.30) -> None:
|
||||
self._f = funding if funding is not None else BinanceFundingClient()
|
||||
self._max_basis = max_basis
|
||||
|
||||
def _get(self, url: str, tries: int = 4) -> Any:
|
||||
"""Single live HTTP GET with backoff retry (mirrors BinanceFundingClient._get):
|
||||
@@ -38,11 +39,14 @@ class BinanceXsFundingLive:
|
||||
|
||||
def snapshot(self, prev: dict[str, float]) -> dict[str, tuple[float, float, float, float, float]]:
|
||||
"""{sym: (trailing_funding, today_funding, perp_price, spot_price, qvol)} over the liquid
|
||||
universe; coins lacking a spot price (can't model basis) are omitted."""
|
||||
universe; coins lacking a spot price (can't model basis) are omitted, and coins whose
|
||||
perp/spot basis exceeds max_basis (decoupled/relisted ticker-reuse artifacts, e.g.
|
||||
LITUSDT) are dropped — the historical panel applies the same |basis| guard."""
|
||||
liq, tf30, last24 = self._f.scan(set(prev))
|
||||
perp, spot = self._perp_prices(), self._spot_prices()
|
||||
out: dict[str, tuple[float, float, float, float, float]] = {}
|
||||
for s in liq:
|
||||
if s in perp and s in spot and perp[s] > 0 and spot[s] > 0:
|
||||
if s in perp and s in spot and perp[s] > 0 and spot[s] > 0 \
|
||||
and abs((perp[s] - spot[s]) / spot[s]) <= self._max_basis:
|
||||
out[s] = (tf30.get(s, 0.0), last24.get(s, 0.0), perp[s], spot[s], liq[s])
|
||||
return out
|
||||
|
||||
@@ -45,3 +45,11 @@ def test_live_source_omits_coin_without_spot(monkeypatch):
|
||||
monkeypatch.setattr(src, "_spot_prices", lambda: {"AAAUSDT": 99.5}) # BBB has no spot
|
||||
snap = src.snapshot(prev={})
|
||||
assert set(snap) == {"AAAUSDT"} # BBB omitted (no spot leg -> can't model basis)
|
||||
|
||||
|
||||
def test_live_source_rejects_decoupled_basis(monkeypatch):
|
||||
src = BinanceXsFundingLive(funding=_FakeFunding()) # AAAUSDT, BBBUSDT
|
||||
monkeypatch.setattr(src, "_perp_prices", lambda: {"AAAUSDT": 100.0, "BBBUSDT": 1.574})
|
||||
monkeypatch.setattr(src, "_spot_prices", lambda: {"AAAUSDT": 99.5, "BBBUSDT": 0.743}) # BBB |basis| huge
|
||||
snap = src.snapshot(prev={})
|
||||
assert set(snap) == {"AAAUSDT"} # BBB rejected (decoupled ticker-reuse artifact)
|
||||
|
||||
Reference in New Issue
Block a user