feat: paper trader su USDC (ETH_USDC-PERPETUAL), pronto per operatività reale

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 09:57:26 +02:00
parent bf26eb0439
commit 8c4ddebe85
3 changed files with 20 additions and 17 deletions
+6 -6
View File
@@ -32,19 +32,19 @@ else:
print("\n--- ACCOUNT DERIBIT TESTNET ---") print("\n--- ACCOUNT DERIBIT TESTNET ---")
c = CerberoClient() c = CerberoClient()
try: try:
acc = c.get_account_summary() acc = c.get_account_summary("USDC")
print(f" Equity: {acc['equity']:.2f}") print(f" Equity: ${acc['equity']:,.2f}")
print(f" Balance: {acc['balance']:.2f}") print(f" Balance: ${acc['balance']:,.2f}")
print(f" PnL: {acc['total_pnl']:.2f}") print(f" PnL: ${acc['total_pnl']:,.2f}")
except Exception as e: except Exception as e:
print(f" Errore: {e}") print(f" Errore: {e}")
# Posizioni # Posizioni
try: try:
pos = c.get_positions() pos = c.get_positions("USDC")
print(f"\n Posizioni aperte: {len(pos)}") print(f"\n Posizioni aperte: {len(pos)}")
for p in pos: for p in pos:
print(f" {p}") print(f" {p.get('instrument','?')}: {p.get('size',0)} {p.get('direction','?')} @ ${p.get('avg_price',0)}")
except Exception as e: except Exception as e:
print(f" Errore: {e}") print(f" Errore: {e}")
+2 -2
View File
@@ -51,8 +51,8 @@ class CerberoClient:
# --- Account --- # --- Account ---
def get_account_summary(self) -> dict: def get_account_summary(self, currency: str = "USDC") -> dict:
return self._post("/mcp-deribit/tools/get_account_summary") return self._post("/mcp-deribit/tools/get_account_summary", {"currency": currency})
def get_positions(self, currency: str = "ETH") -> list[dict]: def get_positions(self, currency: str = "ETH") -> list[dict]:
return self._post("/mcp-deribit/tools/get_positions", {"currency": currency}) return self._post("/mcp-deribit/tools/get_positions", {"currency": currency})
+12 -9
View File
@@ -12,7 +12,9 @@ from src.live.cerbero_client import CerberoClient
from src.live.signal_engine import SignalEngine from src.live.signal_engine import SignalEngine
LOG_DIR = Path(__file__).resolve().parents[2] / "data" / "paper_trades" LOG_DIR = Path(__file__).resolve().parents[2] / "data" / "paper_trades"
INSTRUMENT = "ETH-PERPETUAL" INSTRUMENT = "ETH_USDC-PERPETUAL"
TRAIN_INSTRUMENT = "ETH-PERPETUAL" # storico più lungo per training
CURRENCY = "USDC"
RESOLUTION = "15" RESOLUTION = "15"
LEVERAGE = 3 LEVERAGE = 3
POSITION_PCT = 0.15 POSITION_PCT = 0.15
@@ -60,11 +62,11 @@ class PaperTrader:
with open(self.status_path, "w") as f: with open(self.status_path, "w") as f:
json.dump(status, f, indent=2) json.dump(status, f, indent=2)
def fetch_candles(self, days: int = LOOKBACK_DAYS) -> pd.DataFrame: def fetch_candles(self, days: int = LOOKBACK_DAYS, instrument: str | None = None) -> pd.DataFrame:
end = datetime.now(timezone.utc) end = datetime.now(timezone.utc)
start = end - timedelta(days=days) start = end - timedelta(days=days)
candles = self.client.get_historical( candles = self.client.get_historical(
INSTRUMENT, instrument or TRAIN_INSTRUMENT,
start.strftime("%Y-%m-%d"), start.strftime("%Y-%m-%d"),
end.strftime("%Y-%m-%d"), end.strftime("%Y-%m-%d"),
RESOLUTION, RESOLUTION,
@@ -77,8 +79,8 @@ class PaperTrader:
return df return df
def train_model(self): def train_model(self):
self.log("TRAINING", {"lookback_days": TRAIN_LOOKBACK_DAYS}) self.log("TRAINING", {"lookback_days": TRAIN_LOOKBACK_DAYS, "instrument": TRAIN_INSTRUMENT})
df = self.fetch_candles(TRAIN_LOOKBACK_DAYS) df = self.fetch_candles(TRAIN_LOOKBACK_DAYS, TRAIN_INSTRUMENT)
if df.empty: if df.empty:
self.log("TRAINING_FAILED", {"reason": "no data"}) self.log("TRAINING_FAILED", {"reason": "no data"})
return False return False
@@ -93,8 +95,8 @@ class PaperTrader:
equity = account["equity"] equity = account["equity"]
notional = equity * POSITION_PCT notional = equity * POSITION_PCT
amount = round(notional / price, 1) amount = round(notional / price, 3)
amount = max(amount, 1.0) amount = max(amount, 0.001)
side = "buy" if direction == "buy" else "sell" side = "buy" if direction == "buy" else "sell"
@@ -180,7 +182,7 @@ class PaperTrader:
def run_once(self) -> str: def run_once(self) -> str:
"""Esegui un singolo ciclo. Ritorna lo stato.""" """Esegui un singolo ciclo. Ritorna lo stato."""
df = self.fetch_candles(LOOKBACK_DAYS) df = self.fetch_candles(LOOKBACK_DAYS, TRAIN_INSTRUMENT)
if df.empty: if df.empty:
return "no_data" return "no_data"
@@ -204,7 +206,8 @@ class PaperTrader:
def run(self, retrain_hours: int = 24): def run(self, retrain_hours: int = 24):
"""Loop principale.""" """Loop principale."""
print("=" * 60) print("=" * 60)
print(f" PAPER TRADER — {INSTRUMENT} {RESOLUTION}m") print(f" PAPER TRADER — {INSTRUMENT} (margine {CURRENCY})")
print(f" Segnali da: {TRAIN_INSTRUMENT} {RESOLUTION}m")
print(f" Leva: {LEVERAGE}x, Position: {POSITION_PCT*100:.0f}%, Hold: {HOLD_BARS} barre") print(f" Leva: {LEVERAGE}x, Position: {POSITION_PCT*100:.0f}%, Hold: {HOLD_BARS} barre")
print(f" Poll: ogni {POLL_SECONDS}s") print(f" Poll: ogni {POLL_SECONDS}s")
print(f" Log: {self.log_path}") print(f" Log: {self.log_path}")