Add trade setup outcome tracking and performance stats
Deploy / lint (push) Successful in 25s
Deploy / test (push) Successful in 1m7s
Deploy / deploy (push) Successful in 25s

Closes the feedback loop on R:R scanner signals:

- Nightly outcome_evaluator job replays unresolved setups against daily
  OHLCV bars: target_hit / stop_hit / ambiguous (same-bar, counted as
  loss) / expired after OUTCOME_EVALUATION_MAX_BARS (default 30)
- Migration 004: evaluated_at + outcome_date on trade_setups
- GET /trades/performance: hit rate, expectancy (avg R), total R with
  breakdowns by direction, recommended action, and confidence bucket
- New Performance page (stat cards, breakdown tables, Evaluate Now,
  methodology disclosure) wired into sidebar and mobile nav
- 17 new unit tests for evaluation logic and stats aggregation

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 19:23:57 +02:00
parent d69df5df27
commit 21ed83c56c
20 changed files with 859 additions and 5 deletions
+6 -2
View File
@@ -1,8 +1,8 @@
from datetime import datetime
from datetime import date, datetime
import json
from sqlalchemy import DateTime, Float, ForeignKey, String, Text
from sqlalchemy import Date, DateTime, Float, ForeignKey, String, Text
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
@@ -32,6 +32,10 @@ class TradeSetup(Base):
reasoning: Mapped[str | None] = mapped_column(Text, nullable=True)
risk_level: Mapped[str | None] = mapped_column(String(10), nullable=True)
actual_outcome: Mapped[str | None] = mapped_column(String(20), nullable=True)
evaluated_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
outcome_date: Mapped[date | None] = mapped_column(Date, nullable=True)
ticker = relationship("Ticker", back_populates="trade_setups")