""" BacktestReport - 标准化回测报告 将 BacktestResult 格式化输出为文本/JSON。 """ import json from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: from data_platform.backtest_runner import BacktestResult class BacktestReport: """回测报告生成器""" def __init__(self, result: "BacktestResult"): self.result = result def to_text(self) -> str: """生成文本格式报告""" r = self.result lines = [ "=" * 60, f" 回测报告 | {r.strategy_name} | {r.code}", "=" * 60, f" 回测区间: {r.start_date.date()} ~ {r.end_date.date()}", f" 初始资金: {r.initial_capital:,.0f}", f" 最终权益: {r.final_capital:,.0f}", "-" * 60, f" 总收益率: {r.total_return:>8.2%}", f" 年化收益率: {r.annual_return:>8.2%}", f" 最大回撤: {r.max_drawdown:>8.2%}", f" 夏普比率: {r.sharpe_ratio:>8.2f}", f" 胜率: {r.win_rate:>8.2%}", f" 交易次数: {r.total_trades:>8d}", "-" * 60, ] if r.trades: lines.append(f" {'买入日':>12s} {'卖出日':>12s} {'买入价':>8s} " f"{'卖出价':>8s} {'收益率':>8s} {'股数':>6s}") lines.append(" " + "-" * 68) for t in r.trades[:20]: profit_str = f"{t.profit_pct:7.2%}" if t.profit_pct is not None else " N/A" lines.append( f" {t.entry_date.date()!s:>12s} {t.exit_date.date()!s:>12s} " f"{t.entry_price:>8.2f} {t.exit_price:>8.2f} " f"{profit_str} {t.shares:>6d}" ) if len(r.trades) > 20: lines.append(f" ... 共 {len(r.trades)} 条,仅显示前20条") lines.append("=" * 60) return "\n".join(lines) def to_dict(self) -> dict: """导出为字典""" r = self.result return { "strategy": r.strategy_name, "code": r.code, "start_date": str(r.start_date.date()), "end_date": str(r.end_date.date()), "initial_capital": r.initial_capital, "final_capital": round(r.final_capital, 2), "total_return": round(r.total_return, 4), "annual_return": round(r.annual_return, 4), "max_drawdown": round(r.max_drawdown, 4), "sharpe_ratio": round(r.sharpe_ratio, 2), "win_rate": round(r.win_rate, 4), "total_trades": r.total_trades, } def to_json(self, indent: int = 2) -> str: """导出为JSON""" return json.dumps(self.to_dict(), indent=indent, ensure_ascii=False)