- Add html_report.py module for Playwright-based screenshot generation
- Add generate_html_report() method to SimpleRotationStrategy
- Modify backtest_viewer.html to use window-scoped variables for external injection
- Inject monthly/yearly returns table into screenshot
- Auto-generate HTML report in __main__ after export_results()
Output: simple_rotation_html_report.png with ranking table + monthly returns
Previously, position weights were recalculated every day in _generate_signals,
causing weights to change even when holdings didn't change (only ranking order
shifted). This was incorrect - weights should be locked at rebalance and remain
stable until the next rebalance.
Changes:
- _generate_signals now computes _pending_weights (for signal generation only)
- run() maintains active_weights, updated only on is_rebalance or first day
- _calculate_daily_return uses the locked active_weights
- daily_records stores active_weights in position_weights field
Result: 391 → 318 rebalances, 25.63% → 26.38% CAGR
- Record position_weights in daily_records during backtest run
- Export weight field per held asset in detail JSON
- Display weight percentage in backtest_viewer holdings cards
- Force-add backtest_viewer.html (previously ignored by *.html rule)
- Extract compute_position_weights() as pluggable pure function
- Add WeightType enum (equal/rank) and RotationConfig.weight field
- Fix bond threshold dimension mismatch: use configured factor function
for all assets instead of hardcoded weighted_momentum_score
- Default weight: equal in config, active: rank in config_simple.yaml
Remove slope_snr, slope_snr_r2, james_stein score functions and r2_alpha parameter.
slope_r2_score reverts to simple slope*R² with no alpha parameter.
Minor docstring fix: R^2 → R².
- Add data integrity check: if any currently held asset is missing
from factors, raise RuntimeError immediately to prevent false rebalance
- Previously missing data would silently cause incorrect sell signals
- Now fails fast with clear error message identifying the missing assets
and the date of failure
- Move scripts/daily_scheduler.py -> rotation/daily_scheduler.py
- Add run_simple_rotation() to execute simple_rotation.py via subprocess
- Add --strategy flag (simple/legacy/all) for flexible strategy selection
- Add --simple-config flag for custom simple rotation config path
- Update Dockerfile and docker-compose.yml path references
- Add configurable title to send_report_to_dingtalk()