mirror of
https://ghfast.top/https://github.com/aeroxw/tick-stock-panel.git
synced 2026-09-12 22:34:18 +08:00
* feat(backtest): 新增稳健性指标 Sortino + 蒙特卡罗回撤分位 + per-trade 明细 回测原本只有 Sharpe/Calmar/最大回撤, 缺防过拟合视角。本 PR 补三类: 1. Sortino 比率: 用下行偏差 (MAR=0 目标半方差) 替代总标准差, 只惩罚负 收益波动。无亏损时约定返回 None (不虚报 0/inf)。各 stats 函数用与自身 Sharpe 相同的收益基准 (逐笔 or 日频)。 2. 蒙特卡罗最大回撤分位: 对逐笔收益有放回重抽样 1000 次, 估计回撤分布, 报 P50(中位) 与 P95(95% 置信最坏)。回答"仅因成交顺序运气回撤能有多坏", 单次样本内回撤看不到这个。固定种子 (42) 保证可复现/可测。 3. per-trade 明细: best/worst/median_pnl/avg_holding_days, 补 _calc_stats 与 _calc_portfolio_stats 原本缺失的逐笔视角。 三个 stats 函数 (_calc_stats / _calc_independent_candidate_result / _calc_portfolio_stats) 全部接入, 用共享静态 helper (_sortino_ratio / _mc_drawdown_percentiles / _per_trade_block) 避免重复。纯 additive, 既有 字段不变, 空交易安全。 前端 StrategyBacktest 结果区新增 索提诺 / 蒙卡回撤(中位) / 蒙卡回撤(95%最坏) 三个指标卡 (stats 为 Record<string,any>, 无需改类型)。 新增 tests/backtest/test_robustness_metrics.py 11 用例: Sortino 手算校验/ 无下行 None/优于 Sharpe/样本不足; 蒙卡确定性/P95≤P50≤0/全正零回撤/样本不足; _calc_stats 与 portfolio 字段集成 + 空交易安全。 注: upstream main 既有 test_trailing_take_profit_exits_after_activation 失败与本 PR 无关 (改动前后一致复现)。 * test+fix(backtest): 子代理审查修复 — 补 full 主路径覆盖 + finite 护栏 + 内存上限 子代理审查发现的缺口, 本次补全: 覆盖 (测试): - [关键] full 模式主路径 _calc_independent_candidate_result 原无集成测试, 漏拼 sortino/mc 字典展开会导致前端指标卡空值却测试全绿。新增该分支断言。 - MC 确定性测试从"两次相等"升级为钉死快照值 (p50=-0.0976/p95=-0.2108), 一旦有人把种子改成系统熵立即红。 - p95 断言从恒真的 p95<=p50 (percentile 单调性数学恒成立, 无信息) 改为 p50<0 且 p95<p95 严格更差, 真正验证数值。 - 补全盈利交易 → sortino 集成层为 None 的用例 (原仅 helper 层覆盖)。 健壮性 (实现): - 三个 helper 入口 finite 过滤: inf/nan 收益会让 cumprod 传播 nan → 分位 nan → asdict 直出非法 JSON (项目 _safe 只洗 rows 不洗 stats)。剔除后与纯净输入 结果一致 (加测试守护)。 - MC 内存护栏: full 模式可数千笔, n_sims×n×8B×4 数组瞬时可达数百 MB。 按 2M 单元上限压降模拟次数 (小样本 n_sims=1000 不变, 快照稳定)。 注: 逐笔年化口径 (_calc_stats 对 pnls ×√252) 落在死路径 simulate() (无 app 调用方) 且复刻既有 Sharpe, 不在本 PR 范围。两条活路径 (portfolio/independent) 均用日频基准, ×√252 正确。