157 lines
5.3 KiB
Python
157 lines
5.3 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
本地执行回测 - 通过RPC连接vnpy回测引擎
|
||
SingleStockStopLossStrategy with 15% stop loss
|
||
510300.SSE 2021-01-01 ~ 2026-03-01
|
||
|
||
Author: Guan Yu YunChang
|
||
Date: 2026-03-31
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
import pickle
|
||
import zmq
|
||
from typing import Dict, Any
|
||
|
||
# RPC配置 - 容器外连接(当前运行在容器外 NAS Mac mini 192.168.2.153)
|
||
RPC_ENDPOINT = "tcp://192.168.2.154:8008"
|
||
|
||
# 回测参数
|
||
BACKTEST_CONFIG = {
|
||
"strategy_name": "SingleStockStopLossStrategy",
|
||
"description": "进阶多因子动态加权 + 关羽15%单票止损风控 - 510300.SSE",
|
||
"symbol": "510300.SSE",
|
||
"interval": "1d",
|
||
"start": 1609459200,
|
||
"end": 1772515200,
|
||
"start_date": "2021-01-01",
|
||
"end_date": "2026-03-01",
|
||
"initial_capital": 1000000,
|
||
"commission_fee": 3e-5,
|
||
"slippage": 0.002,
|
||
"contract_size": 10000,
|
||
"price_tick": 0.001,
|
||
"data_source": "sqlite",
|
||
"strategy_module_path": os.path.abspath("main_strategy_single_file.py"),
|
||
"strategy_class": "MultiFactorDynamicStrategy",
|
||
"stop_loss_pct": 0.15,
|
||
"parameters": {
|
||
"stop_loss_pct": 0.15,
|
||
"enabled": True
|
||
}
|
||
}
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
print("=" * 60)
|
||
print("SingleStockStopLossStrategy 回测 - 本地RPC执行")
|
||
print("标的: 510300.SSE 沪深300ETF")
|
||
print("区间: 2021-01-01 ~ 2026-03-01")
|
||
print("止损: 15%")
|
||
print("初始资金: 1,000,000")
|
||
print("=" * 60)
|
||
|
||
# 读取策略代码
|
||
code_path = os.path.join(os.path.dirname(__file__), "main_strategy_single_file.py")
|
||
with open(code_path, 'r', encoding='utf-8') as f:
|
||
strategy_code = f.read()
|
||
|
||
BACKTEST_CONFIG["strategy_code"] = strategy_code
|
||
|
||
# 连接RPC
|
||
context = zmq.Context()
|
||
socket = context.socket(zmq.REQ)
|
||
socket.connect(RPC_ENDPOINT)
|
||
|
||
print(f"\n连接RPC服务器: {RPC_ENDPOINT}")
|
||
|
||
try:
|
||
# 发送回测请求 - 使用pickle序列化匹配服务器端
|
||
print("发送回测请求... (pickle)")
|
||
socket.send_pyobj(BACKTEST_CONFIG)
|
||
|
||
# 接收响应
|
||
print("等待回测结果... (全区间回测需要几分钟,请耐心等待)")
|
||
result = socket.recv_pyobj()
|
||
|
||
# 检查结果
|
||
if isinstance(result, dict):
|
||
if result.get("code") == 200 or result.get("status") == "ok" or "metrics" in result:
|
||
print("\n✅ 回测成功完成!")
|
||
else:
|
||
print(f"\n❌ 回测失败: {result.get('msg') or result.get('error')}")
|
||
print(f"详细信息: {result}")
|
||
sys.exit(1)
|
||
else:
|
||
print(f"\n❌ 返回结果不是字典: {type(result)}")
|
||
print(f"结果: {result}")
|
||
sys.exit(1)
|
||
|
||
# 保存结果
|
||
output_file = os.path.join(
|
||
os.path.dirname(__file__),
|
||
f"backtest_result_510300_stoploss_{int(BACKTEST_CONFIG['stop_loss_pct'] * 100)}.json"
|
||
)
|
||
# 保存为json方便查看
|
||
with open(output_file, 'w', encoding='utf-8') as f:
|
||
import json
|
||
json.dump(result, f, indent=2, ensure_ascii=False)
|
||
print(f"\n💾 完整结果已保存到: {output_file}")
|
||
|
||
# 打印关键指标
|
||
print("\n" + "=" * 60)
|
||
print("📊 回测结果摘要")
|
||
print("=" * 60)
|
||
|
||
metrics = result.get('metrics') or result.get('data', {}).get('metrics', result)
|
||
|
||
print(f"\n📋 基本信息:")
|
||
print(f" 策略名称: {BACKTEST_CONFIG['strategy_name']}")
|
||
print(f" 标的: {BACKTEST_CONFIG['symbol']} 沪深300ETF")
|
||
print(f" 回测区间: {BACKTEST_CONFIG['start_date']} ~ {BACKTEST_CONFIG['end_date']}")
|
||
print(f" 初始资金: {BACKTEST_CONFIG['initial_capital']:,}")
|
||
print(f" 单票止损: {BACKTEST_CONFIG['stop_loss_pct'] * 100:.0f}%")
|
||
|
||
print(f"\n📈 绩效指标:")
|
||
|
||
def print_metric(key, name, fmt="{:.2%}"):
|
||
if key in metrics:
|
||
print(f" {name}: {fmt.format(metrics[key])}")
|
||
elif key.lower() in metrics:
|
||
print(f" {name}: {fmt.format(metrics[key.lower()])}")
|
||
|
||
print_metric('total_return', '总收益率')
|
||
print_metric('annual_return', '年化收益率')
|
||
print_metric('max_drawdown', '最大回撤')
|
||
print_metric('sharpe_ratio', '夏普比率', '{:.2f}')
|
||
print_metric('calmar_ratio', '卡玛比率', '{:.2f}')
|
||
print_metric('sortino_ratio', '索提诺比率', '{:.2f}')
|
||
print_metric('win_rate', '胜率')
|
||
print_metric('profit_loss_ratio', '盈亏比', '{:.2f}')
|
||
|
||
print(f"\n⚠️ 交易统计:")
|
||
if 'total_trades' in metrics:
|
||
print(f" 总交易次数: {metrics['total_trades']} 次")
|
||
if 'stop_loss_count' in metrics or 'stop_loss_triggered' in metrics:
|
||
print(f" 触发止损次数: {metrics.get('stop_loss_count') or metrics.get('stop_loss_triggered', 0)} 次")
|
||
if 'final_capital' in metrics:
|
||
print(f" 最终资金: {metrics['final_capital']:,.2f}")
|
||
|
||
print("\n" + "=" * 60)
|
||
print("回测完成!")
|
||
|
||
return result
|
||
|
||
except Exception as e:
|
||
print(f"\n❌ 执行异常: {type(e).__name__}: {e}")
|
||
import traceback
|
||
traceback.print_exc()
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|