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:
@@ -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}")
|
||||||
|
|
||||||
|
|||||||
@@ -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,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}")
|
||||||
|
|||||||
Reference in New Issue
Block a user