feat: capitale virtuale $1000 USDC, PnL tracking realistico con fee

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 10:04:54 +02:00
parent 8c4ddebe85
commit 591f045cde
+31 -9
View File
@@ -13,7 +13,7 @@ from src.live.signal_engine import SignalEngine
LOG_DIR = Path(__file__).resolve().parents[2] / "data" / "paper_trades"
INSTRUMENT = "ETH_USDC-PERPETUAL"
TRAIN_INSTRUMENT = "ETH-PERPETUAL" # storico più lungo per training
TRAIN_INSTRUMENT = "ETH-PERPETUAL"
CURRENCY = "USDC"
RESOLUTION = "15"
LEVERAGE = 3
@@ -22,6 +22,7 @@ HOLD_BARS = 3
POLL_SECONDS = 60
LOOKBACK_DAYS = 60
TRAIN_LOOKBACK_DAYS = 365
VIRTUAL_CAPITAL = 1000.0 # simula capitale reale, ignora balance testnet
class PaperTrader:
@@ -29,10 +30,12 @@ class PaperTrader:
self.client = CerberoClient()
self.engine = SignalEngine(bb_w=14, sq_thr=0.8, ml_thr=0.70)
self.virtual_capital = VIRTUAL_CAPITAL
self.in_position = False
self.position_entry_time: datetime | None = None
self.position_direction: str | None = None
self.position_entry_price: float = 0
self.position_size: float = 0
self.bars_held = 0
self.last_bar_ts: int = 0
@@ -52,9 +55,11 @@ class PaperTrader:
def save_status(self):
status = {
"virtual_capital": round(self.virtual_capital, 2),
"in_position": self.in_position,
"direction": self.position_direction,
"entry_price": self.position_entry_price,
"position_size": self.position_size,
"entry_time": self.position_entry_time.isoformat() if self.position_entry_time else None,
"bars_held": self.bars_held,
"last_update": datetime.now(timezone.utc).isoformat(),
@@ -91,10 +96,8 @@ class PaperTrader:
def open_position(self, direction: str, signal: dict):
ticker = self.client.get_ticker(INSTRUMENT)
price = ticker["last_price"]
account = self.client.get_account_summary()
equity = account["equity"]
notional = equity * POSITION_PCT
notional = self.virtual_capital * POSITION_PCT * LEVERAGE
amount = round(notional / price, 3)
amount = max(amount, 0.001)
@@ -104,7 +107,8 @@ class PaperTrader:
"side": side,
"amount": amount,
"price": price,
"equity": equity,
"virtual_capital": round(self.virtual_capital, 2),
"notional": round(notional, 2),
"signal": signal,
})
@@ -120,6 +124,7 @@ class PaperTrader:
self.in_position = True
self.position_direction = side
self.position_entry_price = price
self.position_size = amount
self.position_entry_time = datetime.now(timezone.utc)
self.bars_held = 0
self.log("OPENED", {"order_result": result})
@@ -134,27 +139,43 @@ class PaperTrader:
exit_price = ticker["last_price"]
if self.position_direction == "buy":
pnl_pct = (exit_price - self.position_entry_price) / self.position_entry_price * 100
trade_pnl = (exit_price - self.position_entry_price) * self.position_size
else:
pnl_pct = (self.position_entry_price - exit_price) / self.position_entry_price * 100
trade_pnl = (self.position_entry_price - exit_price) * self.position_size
fee = self.position_size * (self.position_entry_price + exit_price) * 0.001
net_pnl = trade_pnl - fee
pnl_pct = net_pnl / self.virtual_capital * 100
self.log("CLOSING", {
"reason": reason,
"entry_price": self.position_entry_price,
"exit_price": exit_price,
"size": self.position_size,
"trade_pnl": round(trade_pnl, 2),
"fee": round(fee, 2),
"net_pnl": round(net_pnl, 2),
"pnl_pct": round(pnl_pct, 3),
"bars_held": self.bars_held,
"capital_before": round(self.virtual_capital, 2),
})
try:
result = self.client.close_position(INSTRUMENT)
self.log("CLOSED", {"result": result, "pnl_pct": round(pnl_pct, 3)})
self.virtual_capital += net_pnl
self.log("CLOSED", {
"result": result,
"net_pnl": round(net_pnl, 2),
"pnl_pct": round(pnl_pct, 3),
"virtual_capital": round(self.virtual_capital, 2),
})
except Exception as e:
self.log("CLOSE_FAILED", {"error": str(e)})
self.in_position = False
self.position_direction = None
self.position_entry_price = 0
self.position_size = 0
self.position_entry_time = None
self.bars_held = 0
@@ -215,7 +236,8 @@ class PaperTrader:
account = self.client.get_account_summary()
self.log("STARTUP", {
"equity": account["equity"],
"virtual_capital": self.virtual_capital,
"testnet_equity": account["equity"],
"testnet": account.get("testnet", True),
})