ZVT 项目研究站 · 策略与回测
从信号到可信回测:ZVT 给你执行接口,成交假设要你自己补
策略能跑完、曲线好看,只说明代码通了。要变成可信的回测,还差五件事:成交时点、交易成本、可成交性、仓位规模、样本外验证。ZVT 在接口层提供了 due_timestamp / happen_timestamp 这对时间戳,其余四项要在你的策略与数据层实现。
信号时点与成交时点必须分开
trade_the_targets 参数自绘(非官方图);ZVT 把两个时间戳做成显式参数,成本项需自行加入。成交时点:ZVT 已经替你做了什么,还差什么
这是 ZVT 在回测链条里最实在的一处设计:把「信号时点」与「成交时点」做成两个显式参数。
| 环节 | ZVT 提供的 | 你还需要做的 | 不改会怎样 |
|---|---|---|---|
| 信号产生 | StockTrader.on_time(timestamp) 每期回调一次 | 在回调里明确「本时点可见的数据截止哪一天」 | 把当期尚未公布的数据算进信号 |
| 下单 | self.trade_the_targets(due_timestamp=, happen_timestamp=, long_selected=, short_selected=) | 把 happen_timestamp 设为实际可成交时点(如 T+1 开盘) | 同一天信号与成交,收益虚高 |
| 成交价 | 由 ZVT 按交易日与价格口径撮合 | 确认用的是不是与因子一致的复权口径 | 因子用后复权、成交用真实价,量级错配 |
| 成交可能性 | 不提供判断 | 涨跌停、停牌、一字板、流动性不足时禁止成交 | 在买不到的价格上买入 |
| 止盈等条件 | profit_threshold 参数(官方示例设为 None) | 按策略需要决定是否启用,并记录取值 | 无法复现别人的结果 |
happen_timestamp 设为 T+1 开盘;③T+1 若涨停或停牌则当日不成交;④计入佣金与印花税;⑤滑点按成交额的固定比例或最小变动单位估算。把这五条写进研究记录,回测才有讨论价值。官方示例里没有的三项成本
README 的策略示例专注于「怎么选、怎么下单」,没有对交易成本建模。以下三项要你自己加,否则等于假设了零成本交易。
| 成本项 | 怎么计 | 量级感受 | 对结果的影响 | 注意点 |
|---|---|---|---|---|
| 佣金 | 按成交金额的约定比例,双边收取(券商费率因人而异,注意常见的最小收费门槛) | 单边万分之一到万分之三不等 | 高频调仓策略对费率极其敏感 | 费率要以你实际券商为准,不要拍脑袋 |
| 印花税 | 卖出时按成交金额征收(现行税率为千分之一) | 单边千分之一,比佣金高一个量级 | 换手越高侵蚀越大 | 税率会调整,回测区间跨年时要分段 |
| 滑点 | 按买贵卖便宜的差额估算,可设为固定比例或最小变动单位 | 与标的流动性、下单方式强相关 | 小市值、低流动性标的滑点可能是主要成本 | 不要对全市场用同一个滑点假设 |
四种不可成交的情形,必须显式排除
这些情形在你的 ZVT 本地库里都有痕迹,问题只在于回测代码有没有去查。
| 情形 | 数据特征 | 处理 | 不做会怎样 |
|---|---|---|---|
| 停牌 | 交易日历有、行情表缺行,或成交量为 0 | 停牌期间不允许买也不允许卖 | 持仓价格凭空延续,回撤被隐藏 |
| 一字涨停 | 开盘=收盘=最高=最低,且为涨停价 | 当日不可买入,信号顺延或作废 | 在最强势的起点买入,收益虚高 |
| 一字跌停 | 四个价格同为跌停价 | 当日不可卖出 | 止损失效,回撤被低估 |
| 流动性不足 | 成交额低于你的下单规模(如成交额小于资金量的若干倍) | 设置最小成交额门槛,或按参与率上限拆分下单 | 小资金假设在大资金下不可复现 |
| 新股上市初期 | 上市不久、涨跌幅规则不同、价格发现不充分 | 设置上市天数门槛(如 60 个交易日) | 把规则不同的样本混进常态统计 |
仓位与资金规模:让回测结果可复现
只报「选股命中率」不足以说明策略,仓位规则必须写死。
| 仓位方式 | 规则 | 适合 | 注意点 |
|---|---|---|---|
| 等权 | 每期选中的标的分摊相同资金 | 因子验证、横截面研究 | 选 3 只和选 30 只的波动差异极大,必须固定数量 |
| 固定金额 | 每只标的投入固定资金 | 资金量小、标的数不固定 | 可选标的变多时总仓位会膨胀 |
| 按分数加权 | 用 score_result 之类评分加权 | 评分型因子 | 权重过度集中会放大单只风险 |
| 上限约束 | 单票上限、行业上限 | 接近实盘的资金管理 | 约束越复杂,越要写清实现细节 |
| 资金规模假设 | 回测资金量与实际计划投入一致 | 所有严肃回测 | 不同资金量下的滑点与流动性约束完全不同 |
结果怎么报:样本内外与四项必备指标
一份能被别人复核的回测记录,至少包含下面这些内容。
| 要素 | 为什么必须有 | 合格做法 | 不合格表现 |
|---|---|---|---|
| 样本内外划分 | 区分「拟合」与「泛化」 | 事先定好切分点(如 2018–2022 内、2023 起外),规则写进记录 | 反复调参直到样本外也好看 |
| 年化收益 | 最基本的收益口径 | 与同口径基准对照 | 只给累计收益,区间不同无法比较 |
| 夏普比率 | 单位风险的收益 | 写明无风险利率取值 | 不写无风险利率,无法复算 |
| 最大回撤 | 决定策略能不能被拿住 | 同时给回撤区间与恢复时长 | 只报收益不报回撤 |
| 换手率 | 成本的来源 | 给出年化换手与成本敏感性 | 高换手但零成本假设 |
on_time 回调与 trade_the_targets(due_timestamp, happen_timestamp) 这组接口。本站未在本机运行 ZVT,因此不提供任何回测结果或绩效数字。策略与回测常见问题
涉及框架接口的部分给源码/README 依据;涉及交易规范的部分标明为通用做法。
ZVT 的回测计入手续费吗?
从 README 的策略示例看,没有对佣金、印花税、滑点建模,示例的重点是交易逻辑本身。所以默认应视为零成本假设,需要你自己把成本加进成交价或收益计算。
due_timestamp 和 happen_timestamp 有什么区别?
前者是期望成交时点,后者是信号发生时点。框架要求分开传,作用就是让「用哪天的信息、在哪天成交」这件事显式化——这也是避免未来函数最直接的接口设计。
怎么实现 T+1 成交?
把 happen_timestamp 设为信号日的下一个交易日(开盘成交假设),并在成交前检查该日是否涨停/停牌。ZVT 不会替你跳过不可成交日。
回测结果要多久才算可信?
不做时间承诺。可操作的判断标准是:样本外表现没有明显劣化、成本敏感性测试通过(成本翻倍后仍不失效)、回撤在可承受范围,并且这份记录别人拿同样的数据与规则能复现。
止盈止损怎么加?
框架的 trader 实例化参数里有 profit_threshold(官方示例设为 None,即不启用)。更复杂的止盈止损逻辑需要在 on_time 里自己实现,并注意跌停时卖不出去这一点。
单只标的的回测和全市场横截面回测差别大吗?
差别很大:全市场回测要处理动态股票池、可成交性、资金约束,单标的回测则相对简单。README 的示例大多是小样本演示,目的是说明写法,不足以作为研究规模的参照。