feat(metrics): Fix 3 — DB-computed metrics, stateless tracker, resolution tracking
CI/CD / build-and-push (push) Successful in 1m47s
CI/CD / build-and-push (push) Successful in 1m47s
schema.sql
trades: + close_pnl, resolution (market outcome storage)
metrics_daily: + unrealized_pnl_est, realized_pnl, open/closed/resolved_count
db.py
close_paper_position(): accepts resolution; computes close_pnl in SQL
BUY_YES: (resolution − entry_price) × shares
BUY_NO: ((1 − resolution) − entry_price) × shares
save_daily_metrics(): persists new columns
compute_metrics_from_db(): single DB query for all metrics; no in-memory state
tracker.py — complete rewrite (stateless)
Removed self._trades, self._daily_returns, compute_metrics(), _compute_sharpe(),
check_promotion_thresholds(), _empty_metrics()
update_daily_summary() now reads compute_metrics_from_db() every cycle
Safe across pod restarts: always reflects full DB history
paper.py
close_position(): passes resolution to close_paper_position()
api/main.py /api/summary
Added unrealized_pnl_est (estimated, open trades) and realized_pnl (exact,
closed+resolved) as separate fields alongside total_pnl
win_rate: null if < 5 resolved trades (was proxy on entry_price < 0.5)
calibration_score: Brier-based, null if < 10 resolved trades
resolved_count exposed as field
Each field annotated with: exact/estimated, source, null conditions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+13
-6
@@ -177,16 +177,23 @@ class PaperExecutor:
|
||||
return cost
|
||||
|
||||
async def close_position(self, market_id: str, resolution: float) -> Optional[float]:
|
||||
"""
|
||||
Close a paper position after market resolution.
|
||||
"""Close a paper position after market resolution.
|
||||
|
||||
resolution: 1.0 if YES won, 0.0 if NO won.
|
||||
Returns P&L in USDC.
|
||||
Persists resolution and close_pnl to DB (computed via SQL from stored
|
||||
entry_price and shares). Returns approximate P&L for logging.
|
||||
"""
|
||||
if market_id not in self._portfolio.positions:
|
||||
return None
|
||||
|
||||
# This would be called by a settlement watcher (future feature)
|
||||
# For now, positions auto-expire at market end date
|
||||
position_cost = self._portfolio.positions.pop(market_id)
|
||||
log.info("Closed position in %s, resolution=%.0f", market_id, resolution)
|
||||
self._portfolio.cash += position_cost * resolution # pay out winnings
|
||||
|
||||
await self._db.close_paper_position(
|
||||
market_id,
|
||||
reason=f"market_resolved resolution={resolution:.1f}",
|
||||
resolution=resolution,
|
||||
)
|
||||
log.info("Closed position in %s, resolution=%.1f", market_id, resolution)
|
||||
# Approximate PnL: settlement value minus cost. Exact value is in close_pnl.
|
||||
return position_cost * resolution - position_cost
|
||||
|
||||
Reference in New Issue
Block a user