Files
sanguo_vnpy/archive/2026-04-29-cleanup/test/rpc/rpc_direct_container.py
T
2026-04-29 20:15:25 +08:00

151 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
直接通过RPC调用执行回测 - 在容器内调用
"""
import zmq
import json
import traceback
# 策略代码
strategy_code = '''
"""
单票固定比例止损策略 - vnpy CTA回测
"""
from vnpy_ctastrategy import (
CtaTemplate, StopOrder, TickData, BarData, TradeData, OrderData,
BarGenerator, ArrayManager
)
from vnpy.trader.constant import Direction, Offset
class SingleStockStopLossStrategy(CtaTemplate):
"""单票固定比例止损策略 - 均线趋势跟踪+固定比例止损"""
author = "关羽 (云长)"
parameters = ["fast_window", "slow_window", "stop_loss_pct"]
variables = ["fast_ma", "slow_ma", "cost_price", "in_position"]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
self.bg = BarGenerator(self.on_bar)
self.am = ArrayManager(max(self.slow_window + 10, 100))
self.fast_ma = 0.0
self.slow_ma = 0.0
self.cost_price = 0.0
self.in_position = False
def on_init(self):
self.write_log(f"策略初始化,fast={self.fast_window}, slow={self.slow_window}, stop_loss={self.stop_loss_pct:.1%}")
self.put_event()
def on_bar(self, bar):
self.am.update_bar(bar)
if not self.am.inited:
return
self.fast_ma = self.am.sma(self.fast_window)
self.slow_ma = self.am.sma(self.slow_window)
have_signal = True
if self.in_position and self.cost_price > 0:
current_drawdown = (bar.close_price - self.cost_price) / self.cost_price
if current_drawdown <= -self.stop_loss_pct:
if self.pos > 0:
self.sell(bar.close_price, self.pos)
self.in_position = False
have_signal = False
if have_signal:
if not self.in_position:
if self.fast_ma > self.slow_ma:
self.buy(bar.close_price, 10000)
self.cost_price = bar.close_price
self.in_position = True
else:
if self.fast_ma < self.slow_ma:
if self.pos > 0:
self.sell(bar.close_price, self.pos)
self.in_position = False
self.put_event()
'''
# RPC请求
request = {
"strategy_code": strategy_code,
"symbol": "510300.SSE",
"interval": "1d",
"start": 1609459200,
"end": 1772515200,
"capital": 1000000,
"rate": 3e-5,
"slippage": 0.002,
"size": 10000,
"pricetick": 0.001,
"data_source": "sqlite"
}
print("Connecting to RPC: tcp://127.0.0.1:8008")
context = zmq.Context()
socket = context.socket(zmq.REQ)
socket.setsockopt(zmq.LINGER, 0)
socket.connect("tcp://127.0.0.1:8008")
socket.setsockopt(zmq.RCVTIMEO, 300000) # 5分钟超时
socket.setsockopt(zmq.SNDTIMEO, 300000)
print("Sending request...")
socket.send_string(json.dumps(request))
print("Waiting for response (may take a few minutes)...")
try:
response_json = socket.recv_string()
response = json.loads(response_json)
if "error" in response:
print(f"\n❌ ERROR: {response['error']}")
if "traceback" in response:
print("\nTraceback:")
print(response["traceback"])
else:
print("\n✅ SUCCESS!")
print("\n" + "=" * 60)
print("回测结果:")
print("=" * 60)
if "statistics" in response:
stats = response["statistics"]
print(f"\n📊 绩效指标:")
print(f" 总收益率: {stats.get('total_return', 0):.2%}")
print(f" 年化收益率: {stats.get('annual_return', 0):.2%}")
print(f" 最大回撤: {stats.get('max_drawdown', 0):.2%}")
print(f" 夏普比率: {stats.get('sharpe_ratio', 0):.2f}")
print(f" 卡玛比率: {stats.get('calmar_ratio', 0):.2f}")
print(f" 总交易次数: {stats.get('total_trades', 0)}")
print(f" 胜率: {stats.get('win_rate', 0):.2%}")
print(f" 盈亏比: {stats.get('profit_loss_ratio', 0):.2f}")
if "trades" in response:
trades = response["trades"]
print(f"\n📝 交易记录: 共 {len(trades)}")
for idx, trade in enumerate(trades[:20], 1):
print(f" {idx}. {trade.get('datetime', '')[:10]} {trade.get('direction', '')} @ {trade.get('price', 0):.2f} × {trade.get('volume', 0)}")
if len(trades) > 20:
print(f" ... 还有 {len(trades) - 20}")
print("\n" + "=" * 60)
print("回测完成!")
print("=" * 60)
except zmq.error.Again:
print("\n❌ TIMEOUT: 超过5分钟仍未完成,请检查日志")
except Exception as e:
print(f"\n❌ ERROR: {e}")
traceback.print_exc()
finally:
socket.close()
context.term()