live/execution: tetto di apertura per strumento da config, non solo MAX_AMOUNT fisso (#18)
DeribitTrader.set_open_cap(instrument, usd_cap, mark) deriva amount_max = 1,10*usd_cap/mark dal cap di config del libro; _submit lo usa quando presente, MAX_AMOUNT resta il DEFAULT per chi non lo chiama (il micro-test). Firma per chi la richiama prima di rebalance_signed (U2): trader.set_open_cap(inst, r["cap_per_asset"] * r["scala"], mark). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,13 +4,17 @@ che dovrebbe farle — non discusse.
|
||||
#17. `DeribitRead.position_usd` leggeva `size`/`size_currency` in ASSOLUTO e ignorava
|
||||
`direction`: uno short risultava un long, e close()/ensure_disaster_sl decidevano lato
|
||||
ordine e prezzo dello stop dal segno sbagliato.
|
||||
#18. Tetto per ordine d'apertura: `MAX_AMOUNT` era un tetto FISSO uguale per tutti i conti;
|
||||
ora il libro deriva il proprio tetto dal cap di config via `set_open_cap()`.
|
||||
|
||||
Nessun test tocca la rete.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from src.live.deribit import DeribitRead
|
||||
from src.live.execution import DeribitTrader, Fill
|
||||
from src.live.execution import MAX_AMOUNT, DeribitTrader, Fill, GuardrailError
|
||||
|
||||
ETH = "ETH_USDC-PERPETUAL"
|
||||
|
||||
@@ -106,3 +110,51 @@ def test_ensure_disaster_sl_su_short_mette_lo_stop_sopra_il_mark_lato_buy():
|
||||
assert call["side"] == "buy"
|
||||
assert call["order_type"] == "stop_market"
|
||||
assert call["price"] > t._mark # stop SOPRA il mark per uno short
|
||||
|
||||
|
||||
# =================================================================================================
|
||||
# #18 — set_open_cap(): tetto di apertura per strumento, derivato dal cap di config.
|
||||
# =================================================================================================
|
||||
class _FakeTraderCap(DeribitTrader):
|
||||
"""DeribitTrader senza rete: `_post` risponde con un ordine fillato finto, cosi' la parte di
|
||||
_submit DOPO il guardrail (che qui non ci interessa) non tocca la rete."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._open_cap: dict[str, float] = {}
|
||||
|
||||
def _post(self, path, payload):
|
||||
amt = payload["amount"]
|
||||
return {"order": {"order_id": "1", "order_state": "filled", "filled_amount": amt},
|
||||
"trades": [{"price": 1.0, "amount": amt, "fee": 0.0}]}
|
||||
|
||||
|
||||
def test_set_open_cap_valida_gli_argomenti():
|
||||
t = _FakeTraderCap()
|
||||
with pytest.raises(ValueError):
|
||||
t.set_open_cap(ETH, 0.0, 2694.0)
|
||||
with pytest.raises(ValueError):
|
||||
t.set_open_cap(ETH, 2291.5, 0.0)
|
||||
|
||||
|
||||
def test_set_open_cap_apertura_entro_il_tetto_non_solleva():
|
||||
"""Equity 4583, cap/asset 2291.5, ETH mark 2694 -> apertura di 0,2807 ETH e' ben dentro il
|
||||
tetto (1,10 * 2291.5 / 2694 ~= 0,9355 ETH)."""
|
||||
t = _FakeTraderCap()
|
||||
t.set_open_cap(ETH, 2291.5, 2694.0)
|
||||
t._submit(ETH, "buy", 0.2807, reduce_only=False, label="book-open") # non deve solllevare
|
||||
|
||||
|
||||
def test_set_open_cap_3x_il_tetto_solleva_guardrail():
|
||||
t = _FakeTraderCap()
|
||||
t.set_open_cap(ETH, 2291.5, 2694.0)
|
||||
cap = t._open_cap[ETH]
|
||||
with pytest.raises(GuardrailError):
|
||||
t._submit(ETH, "buy", cap * 3, reduce_only=False, label="book-open")
|
||||
|
||||
|
||||
def test_senza_set_open_cap_resta_il_default_max_amount():
|
||||
t = _FakeTraderCap()
|
||||
dentro = MAX_AMOUNT[ETH] * 0.5
|
||||
t._submit(ETH, "buy", dentro, reduce_only=False, label="book-open") # non deve solllevare
|
||||
with pytest.raises(GuardrailError):
|
||||
t._submit(ETH, "buy", MAX_AMOUNT[ETH] * 3, reduce_only=False, label="book-open")
|
||||
|
||||
Reference in New Issue
Block a user