78 lines
2.8 KiB
Python
78 lines
2.8 KiB
Python
"""
|
|
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)
|