版本: v1.0 · 日期: 2026-08-18 · 状态: 待审阅
一个代码驱动的量化策略调优与回测平台。策略以 Python 代码编写(非配置、非 DSL),支持多维度绩效评估、贝叶斯/网格/随机参数调优、walk-forward 滚动样本外验证、参数稳健性分析,并生成基于 ECharts 的独立 HTML 在线报告。
项目位置: ~/.hermes/workspace/quantlab
运行时: Python 3.12 (系统)
| 决策项 | 结论 |
|---|---|
| 数据源 | 东方财富 API 为主源;中证红利全收益(H00922)东财无收录 → 中证指数官网补源 |
| 缓存 | 必须缓存,回测只读本地;东财有请求频率限制 |
| 策略编写 | Python 代码,非配置非 DSL |
| 信号频率 | 可配置,默认日频,不支持小时/分钟级 |
| 成交时点 | 次日开盘成交(无未来函数) |
| T+1 规则 | 不建模(中长期低频策略,影响可忽略) |
| 交易载体 | 指数做信号与净值(方案 C);成本按 ETF 真实费率估算 |
| 分红处理 | 全部真实数据:创业板侧用 399606 创业板R(东财),红利侧用 H00922 中证红利全收益(中证官网) |
| 初始持仓 | 100% 现金 |
| 基准 | 多基准支持;本策略: 100%创业板 / 50%+50% / 100%红利 |
| 评估指标 | 全套(见 §7)+ 任意时点买入持有 1/3/5 年盈利概率 |
| 无风险利率 | 当年活期存款利率,按年度计算(内置历史利率表,可编辑) |
| 目标函数 | 默认: 夏普 − λ_mdd·max(0, MDD−0.20) − λ_turnover·年换手率;用户可写自定义 Python 目标函数;λ 可配 + 内置 λ 敏感性分析 |
| 调优方法 | optuna (TPE 贝叶斯),网格/随机可切换;SQLite 持久化 → 断点续跑 + 进度展示 |
| 过拟合控制 | walk-forward 滚动优化:默认 3 年优化 → 1 年实盘 → 逐年滚动 → 拼接 OOS 绩效(窗口可配) |
| 稳健性 | 一维敏感性曲线 / 二维热力图 / ±10% 邻域统计+稳健性得分 / 参数漂移轨迹 |
| 报告 | 独立 HTML + ECharts(本地嵌入,jinja2 模板);preview-url 预览,不配 nginx |
| ETF 重叠期实证 | 2019-12 至今用真实 ETF 价格(515080/159915)复跑策略,与指数回测并排对比 |
| 项目位置 | ~/.hermes/workspace/quantlab |
quantlab/
├── SPEC.md
├── pyproject.toml # 项目配置与依赖
├── README.md
├── quantlab/
│ ├── __init__.py
│ ├── data/ # 数据层
│ │ ├── providers.py # 数据源适配器(东财/中证官网)
│ │ ├── cache.py # parquet 缓存管理 + 增量更新
│ │ ├── loader.py # 统一加载接口 + 数据体检
│ │ └── instruments.py # 资产注册表(代码/名称/来源/用途)
│ ├── engine/ # 回测引擎
│ │ ├── core.py # bar 循环(事件驱动)
│ │ ├── portfolio.py # 账户、订单执行、成本模型
│ │ └── models.py # Trade / EquityCurve / PositionSnapshot
│ ├── strategies/ # ★ 策略代码区
│ │ ├── base.py # Strategy 基类 + tunable_params 协议
│ │ └── ratio_rotation.py # 创业板/红利比值轮动策略(第一个策略)
│ ├── metrics/ # 评估指标
│ │ ├── returns.py # 收益/年化/波动率/夏普
│ │ ├── risk.py # MDD/回撤持续/Calmar/Sortino
│ │ ├── stats.py # 胜率/盈亏比/月度胜率/滚动收益/换手
│ │ └── probability.py # 任意时点买入持有盈利概率
│ ├── optimization/ # 调优层
│ │ ├── search.py # optuna 封装(grid/random/tpe)
│ │ ├── objective.py # 目标函数工厂(默认+自定义)
│ │ ├── walkforward.py # walk-forward 滚动框架
│ │ └── robustness.py # 稳健性分析
│ ├── report/ # 报告层
│ │ ├── generate.py # 报告编排(数据 → HTML)
│ │ ├── charts.py # ECharts 图表数据构建
│ │ └── templates/ # jinja2 模板 + 静态资源
│ ├── cli.py # 命令行入口
│ └── config.py # 全局配置(费率/窗口/λ 默认值)
├── tests/ # pytest 测试
└── data/ # 运行期数据(缓存 parquet、optuna db)【不入库】
扩展路径 (B 型应用): engine/metrics/optimization 全部是纯 Python 函数(无 IO 副作用),report 与核心解耦 → 未来 FastAPI 暴露 POST /backtest、POST /optimize + 任务队列即可,核心零改动。
| code | 名称 | 类型 | 来源 | 用途 |
|---|---|---|---|---|
| 399006 | 创业板指 | 价格指数 | 东财 (secid=0.399006) | 信号 X 分子 |
| 000922 | 中证红利 | 价格指数 | 东财 (secid=1.000922) | 信号 X 分母 |
| 399606 | 创业板R | 全收益指数 | 东财 (secid=0.399606) | 创业板侧净值 |
| H00922 | 中证红利全收益 | 全收益指数 | 中证官网 (perf/index-perf) | 红利侧净值 |
| 159915 | 创业板ETF | ETF | 东财 (secid=0.159915) | ETF 重叠期实证 |
| 515080 | 中证红利ETF | ETF | 东财 (secid=1.515080) | ETF 重叠期实证 |
统一接口 fetch_daily(code, start, end) -> DataFrame[date, open, high, low, close, volume, amount]:
push2his.eastmoney.com/api/qt/stock/kline/get,参数 secid/klt=101/fqt=1/beg/end/lmt,限速 ≥0.3s/请求,失败自动重试(3 次退避)www.csindex.com.cn/csindex-home/perf/index-perf,参数 indexCode/startDate/endDate(仅 H00922 使用;该接口 open/high/low 为 null,只使用 close;只含收盘价,无成交量)data/cache/{code}.parquet(含 total_return 列 = 全收益口径净值,价格指数资产该列与 close 相同;信号统一用 close,净值统一用 total_return)data/rates/demand_deposit.csv(央行活期存款基准利率历史,按年度生效),可编辑覆盖class Strategy:
name: str = "strategy" # 唯一标识
tunable_params: dict[str, ParamSpec] = {} # 声明可调优参数(default/min/max/step/choices)
# 生命周期钩子(引擎调用):
def on_start(self, ctx): ... # 回测开始(读取初始现金、注册资产)
def on_bar(self, ctx): ... # 每个交易日收盘后调用
def on_end(self, ctx): ... # 回测结束
ctx 提供: 当日各资产 close/total_return、当前持仓与现金、X 值(若策略自定义指标)、ctx.order(asset, pct)(按当前总资产百分比下单,次日开盘成交)、ctx.log(msg)(记录事件供报告展示){"default": ..., "min": ..., "max": ..., "step": ..., "choices": [...]},调优器据此构建搜索空间信号: X = close(399006) / close(000922)
| 参数 | 默认 | 范围 | 语义 |
|---|---|---|---|
| L | 0.40 | [0.20, 0.80] | X < L → 分批卖出红利、买入创业板 |
| U | 0.60 | [0.40, 1.00] | X > U → 分批卖出创业板、买入红利 |
| n_batches | 3 | [1, 10] int | 每轮换仓批次数 |
| batch_interval | 5 | [1, 60] int | 节拍器: 交易日间隔(天) |
| x_step | 0.05 | [0.01, 0.30] | 节拍器: X 变动幅度触发 |
| abort_mode | "pause" | ["pause","abandon"] | 中途反悔: pause=条件保持暂停 / abandon=放弃本轮 |
| batch_weight | "equal" | ["equal","front","back"] | 批次权重: 等分/前重后轻/前轻后重(front=先买多, back=先买少) |
节拍器语义(混合): 触发后,下一批在 距离上一批 ≥ batch_interval 个交易日 且 X 较触发时再变动 ≥ x_step 时才执行;两条件满足其一即允许(可配 AND/OR,默认 OR)。 中途反悔: pause = 若 X 回到中性区 (L ≤ X ≤ U),暂停剩余批次,等 X 再次穿越阈值才恢复;abandon = 直接取消剩余批次。 初始持仓: 100% 现金(引擎启动时不自动建仓,策略 on_start 决定首仓;本策略在首次 X<L 时开始分批建仓,若从未触发则全程持币——报告需标注该情形)。
逐交易日 bar 循环,每日顺序: 1. 执行: 用今日开盘价成交昨日 on_bar 产生的挂单(订单在生成后下一 bar 开盘执行;若开盘价缺失 → 顺延至下一有数据的交易日) 2. 记账: 以今日收盘价 mark-to-market,记录净值与持仓快照 3. 信号: 调用策略 on_bar(基于今日收盘数据,产生明日订单)
trades: 每笔成交 (日期/资产/方向/价格/数量/金额/成本)equity_curve: 日频 (日期/总资产/现金/各资产持仓市值/净值)events: 策略日志事件(触发/批次/暂停/放弃,供报告买卖点标注)对 equity_curve 与基准曲线计算:
| 类别 | 指标 |
|---|---|
| 收益 | 总收益率、年化收益(CAGR) |
| 风险 | 年化波动率、最大回撤(MDD)、MDD 发生区间、最长回撤持续时间、水下时间占比 |
| 风险调整 | 夏普(当年活期利率)、Sortino、Calmar |
| 交易 | 年换手率、年交易次数、总交易次数、单边换仓次数 |
| 分布 | 月度胜率、盈亏比(平均盈/平均亏)、滚动 1 年收益分布(分位数) |
| 概率 | 盈利概率: 净值曲线上任意交易日买入并持有 1/3/5 年的盈利比例(滚动窗口滑窗统计,需覆盖区间≥持有期) |
多基准: 基准同为资产组合(100%创业板 / 50+50 静态 / 100%红利),以 total_return 计算基准净值曲线,同套指标并列输出,并计算超额收益(策略 − 各基准)。
data/optuna/{strategy}_{run_id}.db(SQLite)grid(ProductGridSampler) / random(RandomSampler) / tpe(TPESampler,默认)score = Sharpe − λ_mdd·max(0, MDD − 0.20) − λ_turnover·年换手率fn(result) -> float,result 为 §6.3 输出 + 全部指标)见 §9。
以 walk-forward 最终选定的参数(或用户指定参数)为中心:
report/assets/echarts.min.js,页面离线可开;CDN 为 fallback)~/.hermes/workspace/quantlab/output/{run_id}.html,经 preview-url 发布为可分享链接| 模块 | 关键测试 |
|---|---|
| data | 东财/中证适配器字段解析;缓存幂等;增量合并去重;数据体检检出缺口/空值 |
| engine | 次日开盘成交语义(含开盘缺失顺延);成本计算(佣金最低5元/滑点);现金约束(不能超买);分批订单部分成交 |
| metrics | 指标数值正确性(与手工计算对照);盈利概率滑窗;基准净值计算 |
| optimization | 目标函数默认/自定义;λ 惩罚生效;walk-forward 窗口边界;断点续跑恢复 |
| report | 报告生成成功;图表数据 JSON 结构;无 JS 错误(浏览器冒烟) |
| strategy | ratio_rotation 状态机: 触发/节拍器/中途反悔(pause/abandon)/批次权重 各分支 |
| # | 内容 | 验收标准 |
|---|---|---|
| M1 | 数据层 | 6 个资产全部可缓存,体检通过,报告数据体检小节 |
| M2 | 引擎+成本模型 | 单资产买入持有回测正确(与手工账对照) |
| M3 | 指标层 | 全套指标 + 盈利概率 + 多基准计算正确 |
| M4 | 报告管道 | 第一份完整回测报告(净值/回撤/买卖点/指标表)可预览 |
| M5 | 调优层 | optuna 搜索 + walk-forward + 断点续跑 + 进度 |
| M6 | 稳健性 + λ 敏感性 | 全部图组可生成 |
| M7 | ETF 重叠期实证 + 打磨 | 报告全功能,测试通过,README |