feat: DailyScorecard model for per-market performance review (issue #90)
Some checks failed
CI / test (pull_request) Has been cancelled
Some checks failed
CI / test (pull_request) Has been cancelled
- Add DailyScorecard dataclass with market-scoped fields - Fields: date, market, decisions, pnl, win_rate, scenario_match_rate, lessons, cross_market_note - Export from src/evolution/__init__.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ from src.evolution.performance_tracker import (
|
|||||||
PerformanceTracker,
|
PerformanceTracker,
|
||||||
StrategyMetrics,
|
StrategyMetrics,
|
||||||
)
|
)
|
||||||
|
from src.evolution.scorecard import DailyScorecard
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"EvolutionOptimizer",
|
"EvolutionOptimizer",
|
||||||
@@ -16,4 +17,5 @@ __all__ = [
|
|||||||
"PerformanceTracker",
|
"PerformanceTracker",
|
||||||
"PerformanceDashboard",
|
"PerformanceDashboard",
|
||||||
"StrategyMetrics",
|
"StrategyMetrics",
|
||||||
|
"DailyScorecard",
|
||||||
]
|
]
|
||||||
|
|||||||
25
src/evolution/scorecard.py
Normal file
25
src/evolution/scorecard.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"""Daily scorecard model for end-of-day performance review."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class DailyScorecard:
|
||||||
|
"""Structured daily performance snapshot for a single market."""
|
||||||
|
|
||||||
|
date: str
|
||||||
|
market: str
|
||||||
|
total_decisions: int
|
||||||
|
buys: int
|
||||||
|
sells: int
|
||||||
|
holds: int
|
||||||
|
total_pnl: float
|
||||||
|
win_rate: float
|
||||||
|
avg_confidence: float
|
||||||
|
scenario_match_rate: float
|
||||||
|
top_winners: list[str] = field(default_factory=list)
|
||||||
|
top_losers: list[str] = field(default_factory=list)
|
||||||
|
lessons: list[str] = field(default_factory=list)
|
||||||
|
cross_market_note: str = ""
|
||||||
81
tests/test_scorecard.py
Normal file
81
tests/test_scorecard.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
"""Tests for DailyScorecard model."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from src.evolution.scorecard import DailyScorecard
|
||||||
|
|
||||||
|
|
||||||
|
def test_scorecard_initialization() -> None:
|
||||||
|
scorecard = DailyScorecard(
|
||||||
|
date="2026-02-08",
|
||||||
|
market="KR",
|
||||||
|
total_decisions=10,
|
||||||
|
buys=3,
|
||||||
|
sells=2,
|
||||||
|
holds=5,
|
||||||
|
total_pnl=1234.5,
|
||||||
|
win_rate=60.0,
|
||||||
|
avg_confidence=78.5,
|
||||||
|
scenario_match_rate=70.0,
|
||||||
|
top_winners=["005930", "000660"],
|
||||||
|
top_losers=["035420"],
|
||||||
|
lessons=["Avoid chasing breakouts"],
|
||||||
|
cross_market_note="US volatility spillover",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert scorecard.market == "KR"
|
||||||
|
assert scorecard.total_decisions == 10
|
||||||
|
assert scorecard.total_pnl == 1234.5
|
||||||
|
assert scorecard.top_winners == ["005930", "000660"]
|
||||||
|
assert scorecard.lessons == ["Avoid chasing breakouts"]
|
||||||
|
assert scorecard.cross_market_note == "US volatility spillover"
|
||||||
|
|
||||||
|
|
||||||
|
def test_scorecard_defaults() -> None:
|
||||||
|
scorecard = DailyScorecard(
|
||||||
|
date="2026-02-08",
|
||||||
|
market="US",
|
||||||
|
total_decisions=0,
|
||||||
|
buys=0,
|
||||||
|
sells=0,
|
||||||
|
holds=0,
|
||||||
|
total_pnl=0.0,
|
||||||
|
win_rate=0.0,
|
||||||
|
avg_confidence=0.0,
|
||||||
|
scenario_match_rate=0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert scorecard.top_winners == []
|
||||||
|
assert scorecard.top_losers == []
|
||||||
|
assert scorecard.lessons == []
|
||||||
|
assert scorecard.cross_market_note == ""
|
||||||
|
|
||||||
|
|
||||||
|
def test_scorecard_list_isolation() -> None:
|
||||||
|
a = DailyScorecard(
|
||||||
|
date="2026-02-08",
|
||||||
|
market="KR",
|
||||||
|
total_decisions=1,
|
||||||
|
buys=1,
|
||||||
|
sells=0,
|
||||||
|
holds=0,
|
||||||
|
total_pnl=10.0,
|
||||||
|
win_rate=100.0,
|
||||||
|
avg_confidence=90.0,
|
||||||
|
scenario_match_rate=100.0,
|
||||||
|
)
|
||||||
|
b = DailyScorecard(
|
||||||
|
date="2026-02-08",
|
||||||
|
market="US",
|
||||||
|
total_decisions=1,
|
||||||
|
buys=0,
|
||||||
|
sells=1,
|
||||||
|
holds=0,
|
||||||
|
total_pnl=-5.0,
|
||||||
|
win_rate=0.0,
|
||||||
|
avg_confidence=60.0,
|
||||||
|
scenario_match_rate=50.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
a.top_winners.append("005930")
|
||||||
|
assert b.top_winners == []
|
||||||
Reference in New Issue
Block a user