feat(live): conto USDC -> strumenti lineari; entrata/uscita da Old; dashboard LIVE separato da PAPER
Correzione post-micro-test (il conto e' USDC, non BTC/ETH): - deribit.py: INSTRUMENT -> BTC/ETH_USDC-PERPETUAL (lineari, gli unici eseguibili sul conto USDC); notional_to_amount gestisce i lineari (amount in base-coin = notional/price); + quantize_price; trade_history (read-only) per i trade reali. build_rebalance_order passa il prezzo. - shadow.py: sizing col prezzo; espone live_trades (trade reali eseguiti su Deribit). Entrata/uscita verificate (logica presa da Old/src/live/execution.py): - execution.py: open() market verificato (state=='filled' + trade, fill/fee reali, filled_amount autorevole), close() market reduce_only (le CHIUSURE si tentano SEMPRE, senza cap), disaster-SL STOP_MARKET reduce_only. Cap di size SOLO sulle aperture. Fill dataclass. - microtest.py: usa open()/close(); safe-close se l'apertura non e' verificata. Dashboard: sezione PAPER (backtest+forward) separata da sezione LIVE (conto reale Deribit: shadow TP01 + Trades REALI eseguiti). Test 27/27. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+31
-7
@@ -30,7 +30,8 @@ _CONTRACT = {
|
||||
"BTC_USDC-PERPETUAL": {"min": 0.0001, "step": 0.0001, "tick": 0.5, "settle": "USDC", "linear": True},
|
||||
"ETH_USDC-PERPETUAL": {"min": 0.001, "step": 0.001, "tick": 0.05, "settle": "USDC", "linear": True},
|
||||
}
|
||||
INSTRUMENT = {"BTC": "BTC-PERPETUAL", "ETH": "ETH-PERPETUAL"}
|
||||
# Il conto reale e' USDC -> mappiamo gli asset sui perp LINEARI USDC (gli unici eseguibili qui).
|
||||
INSTRUMENT = {"BTC": "BTC_USDC-PERPETUAL", "ETH": "ETH_USDC-PERPETUAL"}
|
||||
|
||||
|
||||
# ----------------------------- costruzione ordini (pura, testabile, NIENTE rete) -----------------------------
|
||||
@@ -41,16 +42,32 @@ def _quantize_step(value: float, step: float, mn: float) -> float:
|
||||
return float(max(n * Decimal(str(step)), Decimal(str(mn))))
|
||||
|
||||
|
||||
def notional_to_amount(instrument: str, notional_usd: float) -> float:
|
||||
"""USD notional -> `amount` Deribit (inverse: amount in USD), arrotondato allo step e clampato
|
||||
al minimo. Ritorna 0.0 se |notional| < mezzo step (sotto-soglia: niente ordine)."""
|
||||
def notional_to_amount(instrument: str, notional_usd: float, price: float | None = None) -> float:
|
||||
"""USD notional -> `amount` Deribit, arrotondato allo step e clampato al minimo. Ritorna 0.0 se
|
||||
sotto mezzo step. INVERSE: amount in USD (price ignorato). LINEAR USDC: amount in base-coin
|
||||
(units = notional/price -> serve il `price`; senza price ritorna 0.0)."""
|
||||
spec = _CONTRACT[instrument]
|
||||
step, mn = spec["step"], spec["min"]
|
||||
if spec.get("linear"):
|
||||
if not price:
|
||||
return 0.0
|
||||
units = abs(notional_usd) / price
|
||||
if units < step / 2:
|
||||
return 0.0
|
||||
return _quantize_step(units, step, mn)
|
||||
if abs(notional_usd) < step / 2:
|
||||
return 0.0
|
||||
return _quantize_step(abs(notional_usd), step, mn)
|
||||
|
||||
|
||||
def quantize_price(instrument: str, price: float) -> float:
|
||||
"""Arrotonda il prezzo al tick dello strumento (per gli ordini stop/limit)."""
|
||||
tick = _CONTRACT[instrument].get("tick")
|
||||
if not tick or price <= 0:
|
||||
return price
|
||||
return float(round(price / tick) * Decimal(str(tick)))
|
||||
|
||||
|
||||
def target_notional_usd(target_fraction: float, weight: float, equity_usd: float) -> float:
|
||||
"""Notional bersaglio (USD) di un asset = peso nel book * frazione-di-equity TP01 * equity.
|
||||
Coerente col paper trader (esposizione asset = WEIGHT * target * equity)."""
|
||||
@@ -58,12 +75,13 @@ def target_notional_usd(target_fraction: float, weight: float, equity_usd: float
|
||||
|
||||
|
||||
def build_rebalance_order(instrument: str, target_fraction: float, weight: float,
|
||||
equity_usd: float, current_pos_usd: float) -> dict | None:
|
||||
equity_usd: float, current_pos_usd: float, price: float | None = None) -> dict | None:
|
||||
"""COSTRUISCE (non invia) l'ordine di ribilancio verso il target. Ritorna un dict-ordine o None
|
||||
se sotto-soglia. Long-only TP01 -> target_notional >= 0; delta = target - posizione corrente."""
|
||||
se sotto-soglia. Long-only TP01 -> target_notional >= 0; delta = target - posizione corrente.
|
||||
`price` (mark) serve a convertire il notional in base-coin per gli strumenti LINEARI USDC."""
|
||||
tgt = target_notional_usd(target_fraction, weight, equity_usd)
|
||||
delta = tgt - current_pos_usd
|
||||
amount = notional_to_amount(instrument, delta)
|
||||
amount = notional_to_amount(instrument, delta, price=price)
|
||||
if amount == 0.0:
|
||||
return None
|
||||
is_exit = abs(tgt) < 1e-9 and abs(current_pos_usd) > 0
|
||||
@@ -141,3 +159,9 @@ class DeribitRead:
|
||||
if p.get("instrument_name") == instrument or p.get("instrument") == instrument:
|
||||
return float(p.get("size") or p.get("size_currency") or 0.0)
|
||||
return 0.0
|
||||
|
||||
def trade_history(self, instrument: str, limit: int = 20) -> list[dict]:
|
||||
"""Trade REALMENTE eseguiti sul conto per `instrument` (fonte autorevole fee/fill)."""
|
||||
out = self._unwrap(self._post("/mcp-deribit/tools/get_trade_history",
|
||||
{"limit": limit, "instrument_name": instrument}))
|
||||
return out if isinstance(out, list) else (out.get("trades", []) if isinstance(out, dict) else [])
|
||||
|
||||
Reference in New Issue
Block a user