initial-import: 2026-04-11 21:18:55

This commit is contained in:
cfdaily
2026-04-11 21:18:55 +08:00
commit 5e6b2d73eb
264 changed files with 117047 additions and 0 deletions
+150
View File
@@ -0,0 +1,150 @@
#!/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()
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""
通过RPC测试 - 缩短到半年验证修复
"""
import zmq
import json
import traceback
# 简化策略 - 简单买入持有
strategy_code = '''
from vnpy_ctastrategy import CtaTemplate, BarGenerator, ArrayManager
from vnpy.trader.constant import Direction
class SimpleTestStrategy(CtaTemplate):
author = "测试"
parameters = []
variables = ["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(100)
self.in_position = False
def on_init(self):
self.load_bar(150)
self.write_log("策略初始化")
def on_bar(self, bar):
self.am.update_bar(bar)
if not self.am.inited:
return
# 第一天收盘买入,一直持有
if not self.in_position:
self.buy(bar.close_price, 10000)
self.in_position = True
self.write_log(f"买入开仓 @ {bar.close_price:.2f}")
self.put_event()
'''
# RPC请求 - 缩短到半年(2025-09-01 ~ 2026-03-01)≈120条K线
request = {
"strategy_code": strategy_code,
"symbol": "510300.SSE",
"interval": "1d",
"start": 1725081600, # 2025-09-01
"end": 1772515200, # 2026-03-01
"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, 120000)
socket.setsockopt(zmq.SNDTIMEO, 120000)
print("Sending request (半年区间)...")
socket.send_string(json.dumps(request))
print("Waiting for response...")
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('total_trades', 0)}")
print(f" 胜率: {stats.get('win_rate', 0):.2%}")
if "trades" in response:
trades = response["trades"]
print(f"\n📝 交易记录: 共 {len(trades)}")
for idx, trade in enumerate(trades, 1):
print(f" {idx}. {trade.get('datetime', '')[:10]} {trade.get('direction', '')} @ {trade.get('price', 0):.2f} × {trade.get('volume', 0)}")
print("\n" + "=" * 60)
print("回测完成!")
print("=" * 60)
except zmq.error.Again:
print("\n❌ TIMEOUT: 超过2分钟仍未完成")
except Exception as e:
print(f"\n❌ ERROR: {e}")
traceback.print_exc()
finally:
socket.close()
context.term()
+111
View File
@@ -0,0 +1,111 @@
#!/usr/bin/env python3
"""
通过RPC测试 - 缩短到1个月验证修复(最少数据量)
"""
import zmq
import json
import traceback
# 简化策略 - 简单买入持有
strategy_code = '''
from vnpy_ctastrategy import CtaTemplate, BarGenerator, ArrayManager
from vnpy.trader.constant import Direction
class SimpleTestStrategy(CtaTemplate):
author = "测试"
parameters = []
variables = ["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(100)
self.in_position = False
def on_init(self):
self.load_bar(30)
self.write_log("策略初始化")
def on_bar(self, bar):
self.am.update_bar(bar)
if not self.am.inited:
return
# 第一天收盘买入,一直持有
if not self.in_position:
self.buy(bar.close_price, 10000)
self.in_position = True
self.write_log(f"买入开仓 @ {bar.close_price:.2f}")
self.put_event()
'''
# RPC请求 - 缩短到1个月(2026-02-01 ~ 2026-03-01)≈20条K线
request = {
"strategy_code": strategy_code,
"symbol": "510300.SSE",
"interval": "1d",
"start": 1738358400, # 2025-02-01 → 不对,2026年2月
"end": 1772515200, # 2026-03-01
"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, 60000)
socket.setsockopt(zmq.SNDTIMEO, 60000)
print("Sending request (1个月区间)...")
socket.send_string(json.dumps(request))
print("Waiting for response...")
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('total_trades', 0)}")
print(f" 胜率: {stats.get('win_rate', 0):.2%}")
if "trades" in response:
trades = response["trades"]
print(f"\n📝 交易记录: 共 {len(trades)}")
for idx, trade in enumerate(trades, 1):
print(f" {idx}. {trade.get('datetime', '')[:10]} {trade.get('direction', '')} @ {trade.get('price', 0):.2f} × {trade.get('volume', 0)}")
print("\n" + "=" * 60)
print("回测完成!")
print("=" * 60)
except zmq.error.Again:
print("\n❌ TIMEOUT: 超过1分钟仍未完成")
except Exception as e:
print(f"\n❌ ERROR: {e}")
traceback.print_exc()
finally:
socket.close()
context.term()
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""
通过RPC测试 - 缩短时间区间(1年)验证修复
"""
import zmq
import json
import traceback
# 简化策略 - 简单买入持有
strategy_code = '''
from vnpy_ctastrategy import CtaTemplate, BarGenerator, ArrayManager
from vnpy.trader.constant import Direction
class SimpleTestStrategy(CtaTemplate):
author = "测试"
parameters = []
variables = ["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(100)
self.in_position = False
def on_init(self):
self.load_bar(300)
self.write_log("策略初始化")
def on_bar(self, bar):
self.am.update_bar(bar)
if not self.am.inited:
return
# 第一天收盘买入,一直持有
if not self.in_position:
self.buy(bar.close_price, 10000)
self.in_position = True
self.write_log(f"买入开仓 @ {bar.close_price:.2f}")
self.put_event()
'''
# RPC请求 - 缩短到1年
request = {
"strategy_code": strategy_code,
"symbol": "510300.SSE",
"interval": "1d",
"start": 1735689600, # 2025-01-01
"end": 1772515200, # 2026-03-01
"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, 180000)
socket.setsockopt(zmq.SNDTIMEO, 180000)
print("Sending request (1年区间)...")
socket.send_string(json.dumps(request))
print("Waiting for response...")
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('total_trades', 0)}")
print(f" 胜率: {stats.get('win_rate', 0):.2%}")
if "trades" in response:
trades = response["trades"]
print(f"\n📝 交易记录: 共 {len(trades)}")
for idx, trade in enumerate(trades, 1):
print(f" {idx}. {trade.get('datetime', '')[:10]} {trade.get('direction', '')} @ {trade.get('price', 0):.2f} × {trade.get('volume', 0)}")
print("\n" + "=" * 60)
print("回测完成!")
print("=" * 60)
except zmq.error.Again:
print("\n❌ TIMEOUT: 超过3分钟仍未完成")
except Exception as e:
print(f"\n❌ ERROR: {e}")
traceback.print_exc()
finally:
socket.close()
context.term()
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env python3
"""
简化测试策略 - 只测试数据加载和回测框架是否能产生交易
"""
import zmq
import json
import traceback
# 简化策略 - 简单买入持有,一定会产生交易
strategy_code = '''
from vnpy_ctastrategy import CtaTemplate, BarGenerator, ArrayManager
from vnpy.trader.constant import Direction
class SimpleTestStrategy(CtaTemplate):
author = "测试"
parameters = []
variables = ["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(100)
self.in_position = False
def on_init(self):
self.load_bar(1000)
self.write_log("策略初始化")
def on_bar(self, bar):
self.am.update_bar(bar)
if not self.am.inited:
return
# 第一天收盘买入,一直持有 - 一定会产生交易
if not self.in_position:
self.buy(bar.close_price, 10000)
self.in_position = True
self.write_log(f"买入开仓 @ {bar.close_price:.2f}")
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, 180000)
socket.setsockopt(zmq.SNDTIMEO, 180000)
print("Sending request...")
socket.send_string(json.dumps(request))
print("Waiting for response...")
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('total_trades', 0)}")
print(f" 胜率: {stats.get('win_rate', 0):.2%}")
if "trades" in response:
trades = response["trades"]
print(f"\n📝 交易记录: 共 {len(trades)}")
for idx, trade in enumerate(trades, 1):
print(f" {idx}. {trade.get('datetime', '')[:10]} {trade.get('direction', '')} @ {trade.get('price', 0):.2f} × {trade.get('volume', 0)}")
print("\n" + "=" * 60)
print("回测完成!")
print("=" * 60)
except zmq.error.Again:
print("\n❌ TIMEOUT: 超过3分钟仍未完成")
except Exception as e:
print(f"\n❌ ERROR: {e}")
traceback.print_exc()
finally:
socket.close()
context.term()
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""测试容器内部连接"""
import requests
import time
# 后台启动服务
import subprocess
import os
print("🔧 后台启动API服务...")
proc = subprocess.Popen(['python', 'api_for_fixed_rpc.py'],
stdout=open('api_test.log', 'w'),
stderr=subprocess.STDOUT)
print(f"✅ 进程已启动,PID: {proc.pid}")
# 等待启动
time.sleep(5)
print("\n🔍 在容器内部测试连接 http://0.0.0.0:8088/ ...")
try:
response = requests.get('http://0.0.0.0:8088/', timeout=5)
print(f"✅ 请求成功,状态码: {response.status_code}")
print(f"✅ 响应内容: {response.text}")
except Exception as e:
print(f"❌ 请求失败: {e}")
# 查看日志
print("\n📝 服务日志:")
with open('api_test.log', 'r') as f:
print(f.read())
# 不杀进程,让它继续运行
print(f"\n✅ 服务继续运行,PID: {proc.pid}")
+70
View File
@@ -0,0 +1,70 @@
from vnpy_ctastrategy import (
CtaTemplate,
StopOrder,
TickData,
BarData,
TradeData,
OrderData,
BarGenerator,
ArrayManager,
)
class SimpleTestStrategy(CtaTemplate):
"""最简单的测试策略,用于排查回测API问题"""
author = "姜维"
# 策略参数
fast_window = 5
slow_window = 20
# 策略变量
fast_ma = 0
slow_ma = 0
parameters = ["fast_window", "slow_window"]
variables = ["fast_ma", "slow_ma"]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
"""初始化策略"""
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
# 创建K线合成器
self.bg = BarGenerator(self.on_bar)
# 创建数组管理器
self.am = ArrayManager()
def on_init(self):
"""策略初始化"""
self.write_log("策略初始化")
# 预加载10根K线
self.load_bar(10)
def on_start(self):
"""策略启动"""
self.write_log("策略启动")
def on_stop(self):
"""策略停止"""
self.write_log("策略停止")
def on_tick(self, tick: TickData):
"""Tick推送"""
self.bg.update_tick(tick)
def on_bar(self, bar: BarData):
"""K线推送"""
# 更新数组
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)
# 简单交易逻辑
if self.fast_ma > self.slow_ma and not self.pos:
self.buy(bar.close_price, 1)
elif self.fast_ma < self.slow_ma and self.pos > 0:
self.sell(bar.close_price, 1)