fix(weekly-report): calculate combined metrics directly from trades

The combined summary (PF, MDD, win_rate) was indirectly reconstructed
from per-symbol averages using round(win_rate * n), which introduced
rounding errors. MDD was max() of individual symbol MDDs, ignoring
simultaneous drawdowns across the correlated crypto portfolio.

Now computes all combined metrics directly from the trade list:
- PF: sum(wins) / sum(losses) from actual trade PnLs
- MDD: portfolio equity curve from time-sorted trades
- Win rate: direct count from trade PnLs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
21in7
2026-03-17 20:12:42 +09:00
parent 5e3a207af4
commit 652990082d
2 changed files with 52 additions and 37 deletions

View File

@@ -262,16 +262,22 @@ def test_generate_report_orchestration(tmp_path):
from scripts.weekly_report import generate_report
from unittest.mock import patch
# 합산 지표는 개별 트레이드에서 직접 계산되므로 mock에 트레이드 포함
mock_trades = [
{"net_pnl": 20.0, "entry_fee": 1.0, "exit_fee": 1.0, "exit_time": "2025-06-10 12:00:00"},
{"net_pnl": 15.0, "entry_fee": 1.0, "exit_fee": 1.0, "exit_time": "2025-06-11 12:00:00"},
{"net_pnl": -10.0, "entry_fee": 1.0, "exit_fee": 1.0, "exit_time": "2025-06-12 12:00:00"},
]
mock_bt_result = {
"summary": {
"profit_factor": 1.24, "win_rate": 45.0,
"max_drawdown_pct": 12.0, "total_trades": 88,
"total_pnl": 379.0, "return_pct": 37.9,
"avg_win": 20.0, "avg_loss": -10.0,
"sharpe_ratio": 33.0, "total_fees": 5.0,
"max_drawdown_pct": 12.0, "total_trades": 3,
"total_pnl": 25.0, "return_pct": 2.5,
"avg_win": 17.5, "avg_loss": -10.0,
"sharpe_ratio": 33.0, "total_fees": 6.0,
"close_reasons": {},
},
"folds": [], "trades": [],
"folds": [], "trades": mock_trades,
}
with patch("scripts.weekly_report.run_backtest", return_value=mock_bt_result):
@@ -287,8 +293,9 @@ def test_generate_report_orchestration(tmp_path):
)
assert report["date"] == "2026-03-07"
# PF는 avg_win/avg_loss에서 재계산됨 (GP=40*20=800, GL=48*10=480 → 1.67)
assert report["backtest"]["summary"]["profit_factor"] == 1.67
# PF는 개별 트레이드에서 직접 계산: GP=35, GL=10 → 3.5
assert report["backtest"]["summary"]["profit_factor"] == 3.5
assert report["backtest"]["summary"]["total_trades"] == 3
assert report["sweep"] is None # PF >= 1.0이면 스윕 안 함