* fix(backtest): address PR #269 review comments
- Replace naive substring keyword matching with negation-aware
_matches_intent that handles phrases like "do not sell" and "不要卖出"
- Default unrecognized/empty advice to cash instead of long to prevent
contaminating backtest statistics with false long positions
- Separate wait (flat) from bearish (down) direction semantics so "wait"
advice no longer inflates directional accuracy
- Move StockDaily access (get_start_daily, get_forward_bars) from
BacktestRepository to StockRepository for proper separation of concerns
* refactor(backtest): simplify keyword matching internals
- Consolidate _NEGATION_EN/_NEGATION_ZH into single _NEGATION_PATTERNS
- Merge duplicate if-branches in infer_position_recommendation
- Simplify _is_negated to single any() expression
- Remove unused timedelta import from stock_repo
* feat(backtest): add eval_window_days and force controls to WebUI
- Add Window (eval_window_days) number input to backtest page
- Add Force toggle button that bypasses min_age_days filter
- Add min_age_days to BacktestRunRequest schema
- Pass eval_window_days filter to results and performance endpoints
so table and summary always show consistent data for the same window
- Thread eval_window_days through performance API (backend + frontend)
* fix(backtest): show latest performance summary on page refresh
When no eval_window_days filter is specified, return the most recent
summary (by computed_at) instead of defaulting to config value 10.
This ensures the performance panel is populated on fresh page load
regardless of which window was used for the last backtest run.
* fix(backtest): filter results by eval_window_days consistently
On initial load, fetch the latest performance summary and use its
eval_window_days to filter results and pre-populate the Window input.
Also pass window filter through Filter button and pagination so all
views stay consistent.
Add a complete backtest/evaluation system that measures the accuracy of
AI-generated stock analysis recommendations against actual market outcomes.
Backend:
- Backtest engine (src/core/backtest_engine.py) with direction inference,
stop-loss/take-profit simulation, and outcome classification (win/loss/neutral)
- Repository layer (src/repositories/backtest_repo.py) with SQLite persistence
for backtest_results and backtest_summaries tables
- Service layer (src/services/backtest_service.py) orchestrating evaluation runs
with configurable window days, neutral band, and min-age filters
- REST API endpoints: POST /run, GET /results, GET /performance, GET /performance/{code}
- Pydantic schemas for request/response validation
Frontend (apps/dsa-web):
- New Backtest page with performance dashboard sidebar showing direction
accuracy, win rate, simulated returns, SL/TP trigger rates, and W/L/N counts
- Paginated results table with outcome badges, direction indicators, and
color-coded return percentages
- Stock code filter and one-click "Run Backtest" trigger
- Full TypeScript types and API client matching backend schemas
Direction mapping fix:
- "Wait/observe" (观望) advice now maps to direction_expected="down" instead
of "flat", correctly reflecting that "wait" means "stay out due to downside
risk" rather than predicting a flat market
Tests:
- 21 unit tests covering engine logic, service orchestration, and summary
aggregation (all passing)
Docs:
- Updated README, full-guide, and translations with backtest feature docs