121 lines
4.2 KiB
Python
Executable File
121 lines
4.2 KiB
Python
Executable File
#!/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()
|