本文档提供 quantPlus 项目的开发规范和技术指南。
本仓库是 quantPlus 主工程,面向 A 股期货量化交易(主要品种:棕榈油,大商所 DCE)。
- vendor/vnpy 只做 submodule + editable install:原则上不直接修改 vendor/vnpy 源码;如需定制,通过继承/包装在 src/qp 中实现。
- 业务代码只允许放在 src/qp 目录:禁止在 vendor/vnpy 下开发任何业务逻辑。
- 双网关支持:CTP (SimNow) 和 TTS (OpenCTP) 灵活切换,策略代码完全兼容。
- 研究与执行解耦:
- OpenBB 用于研究层(因子计算、数据抓取、探索性分析)
- 实盘/回测数据以 vn.py 数据库/CSV 导入为准
- 两者通过 ingest 脚本桥接,不在运行时混用
quantPlus/
├── vendor/
│ └── vnpy/ # vn.py submodule,独立维护与更新
├── src/
│ └── qp/
│ ├── runtime/
│ │ └── trader_app.py # 实盘启动 + GUI 显示入口(支持 profile 和 gateway 参数)
│ ├── ui/
│ │ ├── launcher.py # GUI 启动器
│ │ └── profiles.py # Profile 配置(trade/research/all)
│ ├── strategies/
│ │ ├── cta_palm_oil.py # 双均线策略(学习用)
│ │ └── cta_turtle_enhanced.py # 增强海龟策略(推荐)
│ ├── research/
│ │ ├── openbb_fetch.py # OpenBB 数据拉取 / 因子计算
│ │ └── ingest_vnpy.py # OpenBB 数据 -> vn.py 数据库/CSV 转换入库
│ └── backtest/
│ └── run_cta_backtest.py # 脚本化回测入口(批处理/CI 场景)
├── tests/
│ └── test_openctp_connection.py # OpenCTP 连接测试脚本
├── docs/
│ ├── development.md # 本文件
│ ├── openctp_quickstart.md # OpenCTP 快速上手指南
│ ├── openctp_integration_research.md # OpenCTP 技术调研报告
│ ├── trade_all_features.md # Trade 模式功能说明
│ ├── quickstart_chart_recorder.md # K线图和数据录制快速上手
│ └── data_directory_guide.md # Data 目录说明
├── pyproject.toml # uv 依赖管理
└── uv.lock # 锁定依赖版本
| 路径 | 职责 |
|---|---|
vendor/vnpy/ |
vn.py 框架 submodule,只读引用,更新通过 git submodule update |
src/qp/runtime/trader_app.py |
GUI 主入口,支持 --profile (trade/research/all) 和 --gateway (ctp/tts) 参数 |
src/qp/strategies/cta_palm_oil.py |
双均线 CTA 策略,继承 CtaTemplate(学习用) |
src/qp/strategies/cta_turtle_enhanced.py |
增强海龟策略(趋势过滤+中轨止盈)推荐 |
src/qp/research/openbb_fetch.py |
研究层数据获取,与交易运行时隔离 |
src/qp/research/ingest_vnpy.py |
数据桥接:将 OpenBB 数据转为 vn.py 可用格式 |
src/qp/backtest/run_cta_backtest.py |
脚本化回测,用于批量参数优化或 CI 流水线;图形化回测推荐走 GUI 的 research profile |
- Python 版本:优先 3.13,但 3.11+ 可用
- 操作系统:不限定;GUI 功能需要桌面环境(Qt/PySide6)
- 依赖管理:uv 是唯一入口,不要混用 pip/conda 直接安装依赖
- 如使用 conda,仅作为底层 Python 解释器来源,依赖仍由 uv 锁定管理
# 基础同步(不含 GUI)
uv sync
# 完整同步(含 GUI 与交易组件)
uv sync --all-extras# 全功能模式(实盘 + 回测 + 数据管理),默认 CTP 网关
uv run python -m qp.runtime.trader_app --profile all
# 仅投研/回测(加载 CtaBacktesterApp, DataManagerApp)
uv run python -m qp.runtime.trader_app --profile research
# 仅实盘交易(加载 CtaStrategyApp, RiskManagerApp)
uv run python -m qp.runtime.trader_app --profile trade
# 使用 OpenCTP TTS 网关(7x24 模拟环境)
uv run python -m qp.runtime.trader_app --gateway tts
# 组合使用:TTS 网关 + 投研模式
uv run python -m qp.runtime.trader_app --gateway tts --profile research# 批量回测入口(将来采用该入口)
uv run python -m qp.backtest.run_cta_backtestGUI 的功能页签由加载的 App 模块决定:
| App 类 | 页签功能 |
|---|---|
CtaStrategyApp |
CTA 策略实盘运行 |
CtaBacktesterApp |
CTA 策略回测 |
DataManagerApp |
历史数据管理(查看/导入/导出) |
RiskManagerApp |
风控规则管理 |
ChartWizardApp |
K线图表 |
| Profile | 用途 | 加载的 App |
|---|---|---|
trade |
实盘交易 | CtaStrategy, RiskManager, ChartWizard |
research |
投研回测 | CtaBacktester, DataManager, ChartWizard |
all |
全功能调试 | 以上全部 |
格式:合约代码.交易所
示例:p2405.DCE(棕榈油 2405 合约,大商所)
DCE= 大连商品交易所(大商所)p= 棕榈油品种代码
重要:策略、回测、数据入库、GUI 各处必须使用完全一致的 vt_symbol。
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
| 策略找不到合约 | vt_symbol 格式错误或合约未订阅 | 登录后在"合约查询"中确认 pXXXX.DCE 存在 |
| 回测加载不到数据 | 数据库中 vt_symbol 与策略不一致 | 检查 DataManager 中的数据 symbol 格式 |
| K线图表空白 | 未订阅行情或历史数据缺失 | 先通过 DataManager 确认数据已入库 |
- 类型标注:所有函数签名必须有完整的 typing 标注
- 数据结构:优先使用
dataclass或pydantic.BaseModel - 模块边界:严格区分 runtime / strategies / research / backtest,禁止跨层直接耦合
- 统一使用
logging模块,禁止print作为主日志 - 关键路径必须有
info/warning/error级别日志 - 日志格式应包含时间戳、模块名、级别
- 外部依赖(OpenBB、网络请求、数据源):必须显式捕获异常,提供降级或明确报错
- 交易逻辑:严禁 silent failure,任何异常必须记录并通知
- 禁止依赖未锁定的隐式系统包
- 所有依赖必须通过 pyproject.toml 声明,由 uv 管理
原则:禁止提交任何真实的账号密码、Token、私钥等敏感信息到 Git 仓库
自动检测:
- 项目已集成
scripts/check_sensitive_info.py敏感信息检测脚本 - 在
git commit时通过 pre-commit hook 自动运行 - 检测内容:
- 真实密码(排除
your_password、123456等占位符) - Token/API Key(长度 ≥ 32 字符的随机字符串)
- 私钥(PEM 格式)
- 数据库连接字符串(包含真实密码)
- 不应提交的文件类型(
.vntrader/*.json、*.pem、*.key等)
- 真实密码(排除
配置文件保护:
.vntrader/connect_ctp.json(SimNow 配置).vntrader/connect_tts.json(OpenCTP 配置)- 所有
.vntrader/*.json文件已在.gitignore中
文档中的示例:
- 必须使用占位符:
your_username、your_password、your_token - 禁止在文档中写入任何真实账号密码(包括测试账号)
- 用户需自行申请:SimNow 官网 / OpenCTP GitHub
手动测试:
# 测试敏感信息检测
python scripts/check_sensitive_info.py
# 强制提交(不推荐,仅用于紧急情况)
git commit --no-verify如何修复检测失败:
- 移除暂存区中包含敏感信息的文件:
git restore --staged <file> - 使用占位符替换敏感信息
- 确保敏感配置文件已在
.gitignore中
# 验证 vnpy 导入
uv run python -c "import vnpy; print(vnpy.__version__ if hasattr(vnpy, '__version__') else 'vnpy-ok')"
# 验证 OpenBB 导入
uv run python -c "from openbb import obb; print('openbb-ok')"
# 验证 PySide6/Qt 可用
uv run python -c "from PySide6.QtWidgets import QApplication; print('pyside6-ok')"
# 测试 OpenCTP 连接(需要配置 .vntrader/connect_tts.json)
uv run python tests/test_openctp_connection.py# Trader GUI 帮助信息
uv run python -m qp.runtime.trader_app --help
# 策略模块导入验证
uv run python -c "from qp.strategies.cta_palm_oil import CtaPalmOilStrategy; print('strategy-ok')"
uv run python -c "from qp.strategies.cta_turtle_enhanced import CtaTurtleEnhancedStrategy; print('turtle-ok')"
# 回测脚本帮助
uv run python -m qp.backtest.run_cta_backtest --help
# 60分钟数据回测验证
uv run python -m qp.backtest.run_cta_backtest --strategy CtaTurtleEnhancedStrategy --interval HOUR --days 240所有 CTA 策略放置于 src/qp/strategies/ 目录,命名规范:cta_{品种}_{策略类型}.py
from vnpy.trader.utility import ArrayManager
from vnpy_ctastrategy import CtaTemplate
class MyStrategy(CtaTemplate):
author = "QuantPlus"
# 参数(GUI 可见)
param1: int = 10
parameters = ["param1"]
# 变量(GUI 可见)
var1: float = 0.0
variables = ["var1"]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
super().__init__(cta_engine, strategy_name, vt_symbol, setting)
# ArrayManager size 必须覆盖所有窗口 + buffer
self.am = ArrayManager(size=self.param1 + 20)
def on_init(self):
self.load_bar(10) # 预加载历史数据
def on_bar(self, bar):
self.am.update_bar(bar)
if not self.am.inited:
return # warm-up 未完成,不交易
# 交易逻辑...| 要点 | 说明 |
|---|---|
| Warm-up 防护 | ArrayManager size = max(所有窗口) + 20,防止 0 成交 |
| Look-ahead 防护 | 计算指标时使用 [-window-1:-1] 切片,避免使用当前 bar |
| 止损单管理 | 每根 bar 调用 cancel_all() 后重新挂止损单 |
| 日志记录 | 入场/出场/加仓必须 write_log(),便于调试 |
| 参数校验 | on_init() 中校验参数合法性 |
策略需要放置桥接文件才能在 GUI 中显示:
# .vntrader/strategies/{策略文件名}.py
from qp.strategies.{策略模块} import {策略类名}新策略必须通过脚本化回测验证:
uv run python -m qp.backtest.run_cta_backtest --strategy {策略类名} --days 365 -v| 策略 | Sharpe Ratio | 总收益率 | 年化收益 | 最大回撤 | 推荐度 |
|---|---|---|---|---|---|
| CtaTurtleEnhancedStrategy (激进) | 1.28 | +24.41% | +34.07% | -8.23% | 推荐 |
| CtaPalmOilStrategy | -1.50 | -1.64% | -2.28% | -1.85% | 学习用 |
改进版海龟策略,经激进优化后收益率达24%+,年化34%:
- 趋势过滤:MA10 > MA100 时只做多,MA10 < MA100 时只做空(超长周期过滤噪音)
- 中轨止盈:触及唐奇安通道中线时止盈,锁定利润
- 极速出场:exit_window=1,最快响应反转信号
- 激进仓位:risk_per_trade=6%,max_units=50
- 快速加仓:pyramid_atr=0.15,利润快速累积
默认参数(2026-01 激进优化: Sharpe 1.28, 收益 +24.41%):
| 参数 | 值 | 说明 |
|---|---|---|
| entry_window | 15 | 入场通道窗口 |
| exit_window | 1 | 出场通道窗口(极速出场) |
| trend_ma_fast | 10 | 快速趋势均线 |
| trend_ma_slow | 100 | 慢速趋势均线(超长周期过滤) |
| atr_stop | 1.5 | ATR 止损倍数 |
| risk_per_trade | 0.06 | 单笔风险预算 6% |
| max_units | 50 | 最大持仓手数 |
| pyramid_atr | 0.15 | 加仓间隔 ATR 倍数 |
| use_trend_filter | True | 趋势过滤开关 |
| use_mid_line_exit | True | 中轨止盈开关 |
| enable_pyramid | True | 金字塔加仓开关 |
保守版参数(Sharpe 1.14, 收益 +6.85%, 回撤 -2.92%):
- exit_window: 2
- trend_ma_slow: 80
- risk_per_trade: 0.03
- max_units: 15
- pyramid_atr: 0.5
回测命令:
uv run python -m qp.backtest.run_cta_backtest --strategy CtaTurtleEnhancedStrategy --interval HOUR --days 240基础双均线策略,用于学习和验证框架功能。
| 数据源 | 用途 | 说明 |
|---|---|---|
| 迅投研 (XTQuant) | 主数据源 | Tick/分钟数据,支持 Token 连接 |
| akshare (新浪) | 备用 | 有限历史深度,无 Tick |
使用 vnpy_xt 包进行数据获取,支持 Token 连接(无需 QMT 客户端):
# 配置 settings.json
{
"datafeed.name": "xt",
"datafeed.username": "client", # Token 模式
"datafeed.password": "<your_token>"
}| 周期 | 数据源 | 时间跨度 | 说明 |
|---|---|---|---|
| Tick | 迅投研 | 无限制 | 最精细粒度 |
| 1分钟 | 迅投研 | 无限制 | 原始分钟数据 |
| 日线 | akshare | ~1年 | futures_main_sina |
| 60分钟 | akshare | ~8个月 | futures_zh_minute_sina(period='60') |
中国期货交易所(如 DCE 大商所)的主力连续合约数据(如 p0 棕榈油)采用交易时段聚合方式,每日固定 6 根 K 线。而迅投研等数据源提供的 p00 数据采用自然小时聚合(每日 7-8 根)。
两种聚合方式的回测结果差异显著,必须统一聚合规范以保证策略可复现性。
QuantPlus 标准 K 线聚合规范:采用交易时段聚合,时间戳为 K 线结束时间。
| 时段名 | 交易时间 | K 线时间戳 |
|---|---|---|
| 早盘1 | 09:00 - 10:00 | 10:00 |
| 早盘2 | 10:00 - 11:30 | 11:15 |
| 午盘1 | 13:30 - 14:15 | 14:15 |
| 午盘2 | 14:15 - 15:00 | 15:00 |
| 夜盘1 | 21:00 - 22:00 | 22:00 |
| 夜盘2 | 22:00 - 23:00 | 23:00 |
注意:早盘2 包含 10:15-10:30 休息时间,K 线时间戳为 11:15 而非 11:30。
- K 线时间戳 = K 线结束时间(与 p0 主力连续一致)
- 夜盘数据归属下一个交易日(如周五夜盘归属周一)
| 数据类型 | K 线数/天 | 聚合方式 | 时间戳 | 示例 |
|---|---|---|---|---|
| p0 (主力连续) | 6 | 交易时段 | 结束时间 | 10:00, 11:15, 14:15, 15:00, 22:00, 23:00 |
| p00 (自然小时) | 7-8 | 自然小时 | 开始时间 | 09:00, 10:00, 11:00, 13:00, 14:00, 21:00, 22:00 |
| qp_session | 6 | 交易时段 | 结束时间 | 与 p0 一致 |
from qp.datafeed import SessionBarSynthesizer, DCE_PALM_OIL_SESSIONS
# 创建合成器
synthesizer = SessionBarSynthesizer(sessions=DCE_PALM_OIL_SESSIONS)
# 从分钟数据合成时段 K 线
session_bars = synthesizer.synthesize_from_minutes(minute_bars)
# 从 Tick 数据合成时段 K 线
session_bars = synthesizer.synthesize_from_ticks(tick_data)
# 从时段 K 线合成日 K 线
daily_bars = synthesizer.synthesize_daily(session_bars)from datetime import time
from qp.datafeed.session_synthesizer import TradingSession, SessionBarSynthesizer
# 定义自定义时段
custom_sessions = [
TradingSession("时段1", time(9, 0), time(10, 30), time(10, 30)),
TradingSession("时段2", time(10, 30), time(11, 30), time(11, 30)),
# ... 更多时段
]
synthesizer = SessionBarSynthesizer(sessions=custom_sessions)使用 SessionBarSynthesizer 合成的数据与 p0 原始数据回测结果对比:
| 数据源 | 收益率 | Sharpe Ratio | 最大回撤 |
|---|---|---|---|
| p0 原始 | +24.41% | 1.28 | -8.23% |
| qp_session 合成 | +27.41% | 1.41 | -7.89% |
差异来源:数据获取时间点不同、主力合约换月处理差异等,结果在合理范围内。
from vnpy_xt import Datafeed
from vnpy.trader.object import HistoryRequest
from vnpy.trader.constant import Exchange, Interval
from datetime import datetime
# 初始化
datafeed = Datafeed()
datafeed.init()
# 查询分钟数据
req = HistoryRequest(
symbol="p00",
exchange=Exchange.DCE,
start=datetime(2024, 1, 1),
end=datetime(2025, 1, 1),
interval=Interval.MINUTE
)
bars = datafeed.query_bar_history(req)import akshare as ak
# 拉取60分钟数据
df = ak.futures_zh_minute_sina(symbol='P0', period='60')
print(df.head())