chore(reset): v2.0.0 — storico certificato Deribit mainnet, ripartenza pulita
Reset del progetto su fondamenta verificate dopo la scoperta che l'intera libreria "validata OOS" era artefatto di feed contaminato (print fantasma del feed Cerbero TESTNET + storico Binance/USDT). - Storico ricostruito da Deribit MAINNET (ccxt pubblico, tokenless) e CERTIFICATO (certify_feed.py): BTC/ETH puliti su TUTTA la storia (mediana 2-6 bps vs Coinbase USD), integrita' OHLC + coerenza resample (maxΔ 0.00) + cross-venue OK. Alt esclusi (illiquidi/divergenti: LTC/DOGE 50-82% barre flat; XRP/BNB non certificabili). - Verdetto sul feed pulito: FADE / PAIRS / XS01 / TSM01 morti (ogni portafoglio Sharpe -2.3..-3.0, DD ~40%); solo SH01 e frammenti HONEST con segnale residuo, da ri-validare in isolamento. - Cleanup "restart pulito": strategie, stack live (src/live, src/portfolio, runner/executor, yml, docker), ~100 script ricerca/gate, waste/games/ portfolios, dati non certificati + cache e 60+ diari -> archiviati in Old/ (preservati, non cancellati). Diario consolidato in un unico documento. - Skeleton ricerca tenuto: Strategy ABC + indicatori + src/fractal + src/backtest/engine + load_data; tool dati certificati (rebuild_history, certify_feed, audit_feed, multi_source_check). - Universo dati ATTIVO: solo BTC/ETH (5m/15m/1h); guardrail fisico (load_data su alt -> FileNotFoundError). Esecuzione DISABILITATA, conto flat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
"""Netting delle chiusure market (v1.1.25): close_amount tenta il reduce-only e
|
||||
riesegue il RESIDUO in market puro quando il netting di conto lo cappa/respinge
|
||||
(audit 2026-06-11: 3 gambe pairs orfane + 1 close cappato). Il chiamante riceve
|
||||
UN Fill combinato. Nessuna rete: _submit monkeypatchato."""
|
||||
from src.live.execution import ExecutionClient, Fill
|
||||
|
||||
|
||||
def _fill(inst, side, amount, filled, px, verified=True, state="filled", fee=0.05):
|
||||
return Fill(inst, side, 0.0, amount, px if filled else None, 0.0, fee,
|
||||
f"oid-{side}-{filled}", state, verified, filled_amount=filled)
|
||||
|
||||
|
||||
def _client_with(submits, allowance=float("inf")):
|
||||
"""ExecutionClient con _submit finto e guard del netting stubbato (il guard
|
||||
reale legge conto+libri via HTTP/filesystem; qui si inietta il gap)."""
|
||||
ec = ExecutionClient()
|
||||
calls = []
|
||||
|
||||
def fake_submit(instrument, side, amount, notional, reduce_only, label=None,
|
||||
order_type="market", price=None):
|
||||
calls.append(dict(amount=amount, reduce_only=reduce_only, label=label))
|
||||
return submits.pop(0)(instrument, side, amount)
|
||||
|
||||
ec._submit = fake_submit
|
||||
ec._net_close_allowance = lambda *a, **k: allowance
|
||||
return ec, calls
|
||||
|
||||
|
||||
INST = "ETH_USDC-PERPETUAL"
|
||||
|
||||
|
||||
def test_full_reduce_only_no_fallback():
|
||||
# caso normale: il reduce-only filla tutto -> NESSUN secondo ordine
|
||||
ec, calls = _client_with([
|
||||
lambda i, s, a: _fill(i, s, a, filled=a, px=1700.0),
|
||||
])
|
||||
f = ec.close_amount(INST, "sell", 0.103, label="w1")
|
||||
assert len(calls) == 1 and calls[0]["reduce_only"] is True
|
||||
assert f.verified and abs(f.filled_amount - 0.103) < 1e-9
|
||||
assert "netting" not in (f.notes or "")
|
||||
|
||||
|
||||
def test_capped_reduce_only_nets_residual():
|
||||
# reduce-only CAPPATO (filla 0.078 su 0.105) -> residuo 0.027 in market puro
|
||||
ec, calls = _client_with([
|
||||
lambda i, s, a: _fill(i, s, a, filled=0.078, px=1700.0),
|
||||
lambda i, s, a: _fill(i, s, a, filled=a, px=1702.0),
|
||||
])
|
||||
f = ec.close_amount(INST, "sell", 0.105, label="w1")
|
||||
assert len(calls) == 2
|
||||
assert calls[0]["reduce_only"] is True and calls[1]["reduce_only"] is False
|
||||
assert abs(calls[1]["amount"] - 0.027) < 1e-9
|
||||
assert calls[1]["label"] == "w1|net"
|
||||
assert f.verified and abs(f.filled_amount - 0.105) < 1e-9
|
||||
# prezzo medio pesato: (0.078*1700 + 0.027*1702) / 0.105
|
||||
exp_px = (0.078 * 1700.0 + 0.027 * 1702.0) / 0.105
|
||||
assert abs(f.fill_price - exp_px) < 1e-6
|
||||
assert abs(f.fee_usd - 0.10) < 1e-9 # fee sommate
|
||||
assert "netting" in f.notes
|
||||
|
||||
|
||||
def test_rejected_reduce_only_nets_full_amount():
|
||||
# reduce-only RESPINTO (error, 0 fill) -> tutto l'amount in market puro
|
||||
ec, calls = _client_with([
|
||||
lambda i, s, a: _fill(i, s, a, filled=0.0, px=None, verified=False, state="error", fee=0.0),
|
||||
lambda i, s, a: _fill(i, s, a, filled=a, px=1701.0),
|
||||
])
|
||||
f = ec.close_amount(INST, "buy", 0.027, label="pair")
|
||||
assert len(calls) == 2 and abs(calls[1]["amount"] - 0.027) < 1e-9
|
||||
assert f.verified and abs(f.filled_amount - 0.027) < 1e-9
|
||||
assert f.fill_price == 1701.0
|
||||
assert "netting" in f.notes
|
||||
|
||||
|
||||
def test_net_leg_also_fails_reports_partial():
|
||||
# anche il market puro fallisce -> Fill combinato NON verified, filled = solo r-o
|
||||
ec, calls = _client_with([
|
||||
lambda i, s, a: _fill(i, s, a, filled=0.05, px=1700.0),
|
||||
lambda i, s, a: _fill(i, s, a, filled=0.0, px=None, verified=False, state="error", fee=0.0),
|
||||
])
|
||||
f = ec.close_amount(INST, "sell", 0.105, label="w1")
|
||||
assert len(calls) == 2
|
||||
assert not f.verified
|
||||
assert abs(f.filled_amount - 0.05) < 1e-9 # solo il fillato vero nel ledger
|
||||
|
||||
|
||||
def test_stale_state_guard_blocks_naked_order():
|
||||
# quota gia' chiusa (DSL in outage / flatten manuale): reduce-only filla 0 e
|
||||
# il gap conto-vs-libri e' 0 -> NESSUN ordine market nudo (code-review 2026-06-11)
|
||||
ec, calls = _client_with([
|
||||
lambda i, s, a: _fill(i, s, a, filled=0.0, px=None, verified=False, state="error", fee=0.0),
|
||||
], allowance=0.0)
|
||||
f = ec.close_amount(INST, "buy", 0.105, label="w1")
|
||||
assert len(calls) == 1 # solo il reduce-only, niente fallback
|
||||
assert not f.verified and f.filled_amount == 0.0
|
||||
assert "netting NEGATO" in f.notes
|
||||
|
||||
|
||||
def test_guard_unknown_fails_safe():
|
||||
# guard non calcolabile (rete): FAIL-SAFE, niente ordine nudo
|
||||
ec, calls = _client_with([
|
||||
lambda i, s, a: _fill(i, s, a, filled=0.0, px=None, verified=False, state="error", fee=0.0),
|
||||
], allowance=None)
|
||||
f = ec.close_amount(INST, "buy", 0.105, label="w1")
|
||||
assert len(calls) == 1
|
||||
assert "non calcolabile" in f.notes
|
||||
|
||||
|
||||
def test_guard_caps_residual_to_allowance():
|
||||
# gap parziale: il residuo nudo e' cappato a quanto i libri giustificano
|
||||
ec, calls = _client_with([
|
||||
lambda i, s, a: _fill(i, s, a, filled=0.0, px=None, verified=False, state="error", fee=0.0),
|
||||
lambda i, s, a: _fill(i, s, a, filled=a, px=1700.0),
|
||||
], allowance=0.027)
|
||||
f = ec.close_amount(INST, "sell", 0.105, label="w1")
|
||||
assert len(calls) == 2
|
||||
assert abs(calls[1]["amount"] - 0.027) < 1e-9
|
||||
assert not f.verified # 0.027 < 0.105: chiusura parziale onesta
|
||||
assert abs(f.filled_amount - 0.027) < 1e-9
|
||||
|
||||
|
||||
def test_dust_residual_no_fallback():
|
||||
# residuo da artefatto float (1e-17): NON deve diventare un lotto minimo nudo
|
||||
ec, calls = _client_with([
|
||||
lambda i, s, a: _fill(i, s, a, filled=a - 1e-17, px=1700.0),
|
||||
])
|
||||
f = ec.close_amount(INST, "sell", 0.105, label="w1")
|
||||
assert len(calls) == 1
|
||||
assert f.verified
|
||||
Reference in New Issue
Block a user