1
0

重构回测代码架构,新增批量回测功能

This commit is contained in:
2026-01-28 14:10:14 +08:00
parent 173a566f8b
commit 0db4155a3c
13 changed files with 1731 additions and 283 deletions

120
backtest_command.py Executable file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env python3
import argparse
import sys
import tabulate
import backtest_core
def parse_arguments():
parser = argparse.ArgumentParser(description="量化回测工具", formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--codes", type=str, nargs="+", required=True, help="股票代码列表 (如: 000001.SZ 600000.SH)", )
parser.add_argument("--start-date", type=str, required=True, help="回测开始日期 (格式: YYYY-MM-DD)", )
parser.add_argument("--end-date", type=str, required=True, help="回测结束日期 (格式: YYYY-MM-DD)", )
parser.add_argument("--strategy-file", type=str, required=True, help="策略文件路径 (如: strategy.py)", )
parser.add_argument("--cash", type=float, default=100000, help="初始资金 (默认: 100000)", )
parser.add_argument("--commission", type=float, default=0.002, help="手续费率 (默认: 0.002)", )
parser.add_argument("--warmup-days", type=int, default=365, help="预热天数 (默认: 365约一年)", )
parser.add_argument("--output-dir", type=str, default=None, help="HTML 图表输出目录 (可选,为每个股票生成 {code}.html)", )
return parser.parse_args()
def format_single_result(result: backtest_core.BacktestResult):
print("=" * 60)
print(f"股票代码: {result.code}")
print("=" * 60)
indicator_mapping = {
"最终收益": f"{result.equity_final:.2f}",
"峰值收益": f"{result.equity_peak:.2f}",
"总收益率(%": f"{result.return_pct:.2f}",
"买入并持有收益率(%": f"{result.buy_hold_return_pct:.2f}",
"年化收益率(%": f"{result.return_ann_pct:.2f}",
"年化波动率(%": f"{result.volatility_ann_pct:.2f}",
"索提诺比率": f"{result.sortino_ratio:.2f}",
"卡尔玛比率": f"{result.calmar_ratio:.2f}",
"最大回撤(%": f"{result.max_drawdown_pct:.2f}",
"平均回撤(%": f"{result.avg_drawdown_pct:.2f}",
"最大回撤持续时长": f"{result.max_drawdown_duration:.0f}",
"平均回撤持续时长": f"{result.avg_drawdown_duration:.0f}",
"总交易次数": f"{result.num_trades:.0f}",
"胜率(%": f"{result.win_rate_pct:.2f}",
"系统质量数": f"{result.sqn:.2f}",
}
for name, value in indicator_mapping.items():
print(f"{name}: {value}")
print("=" * 60)
def format_batch_results(results: list[backtest_core.BacktestResult]):
table_data = []
for result in results:
table_data.append(
[
result.code,
f"{result.return_pct:.2f}",
f"{result.buy_hold_return_pct:.2f}",
f"{result.return_ann_pct:.2f}",
f"{result.volatility_ann_pct:.2f}",
f"{result.win_rate_pct:.2f}",
f"{result.max_drawdown_pct:.2f}",
f"{result.sortino_ratio:.2f}",
f"{result.num_trades:.0f}",
f"{result.sqn:.2f}",
]
)
headers = [
"股票代码",
"收益率%",
"买入持有%",
"年化收益%",
"年化波动%",
"胜率%",
"最大回撤%",
"索提诺比率",
"交易次数",
"SQN",
]
print(tabulate.tabulate(table_data, headers=headers, tablefmt="grid"))
def main():
args = parse_arguments()
try:
results = backtest_core.run_batch_backtest(
codes=args.codes,
start_date=args.start_date,
end_date=args.end_date,
strategy_file=args.strategy_file,
cash=args.cash,
commission=args.commission,
warmup_days=args.warmup_days,
output_dir=args.output_dir,
show_progress=True,
)
if len(results) == 1:
format_single_result(results[0])
else:
format_batch_results(results)
if args.output_dir:
print(f"\n图表已保存到: {args.output_dir}/")
except Exception as e:
print(f"\n错误: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()