看基准收益
只想算「一直持有」的对照曲线,用 from_holding,输入持仓数量即可,不需要写信号逻辑。它常被用来回答「择时到底有没有跑赢不择时」,注意基准与策略要用同一段价格、同一套成本假设。
Portfolio
Portfolio 是这套框架回测的核心对象,提供三种工厂方法:from_holding(买入并持有)、from_signals(按信号进出场)、from_orders(按订单精细控制)。选对入口后,还要核对信号可知时间、freq 年化口径、费用和成交假设。单看统计摘要无法证明结果可交易。
vectorbt 三个工厂方法的差别不在「快慢」,而在你手上已经有的是持仓、信号还是订单。
| 入口 | 输入 | 适用场景 | 注意点 |
|---|---|---|---|
from_holding | 持仓数量(如固定股数或固定比例) | 买入并持有,或作为与策略对比的基准曲线 | 它不主动交易,只反映持有收益;当作基准时必须与策略同区间、同成本口径 |
from_signals | 布尔买卖信号(entries / exits) | 双均线、MACD 金叉死叉、RSI 等信号驱动策略 | 信号与价格同索引时等于当日收盘信号当日成交,必须先 shift(1) |
from_signals + accumulate | 连续出现的买入信号 | 分批建仓、定投式加仓、金字塔加仓 | 不加该参数时,连续信号可能只在首次买入生效,仓位与预期不符 |
from_signals 多列信号 | DataFrame 多列(多标的或多组参数) | 多标的等权、参数组横向对比 | 列数会被广播成多组独立回测,标的数与参数数相乘会显著吃内存 |
from_orders | 订单规模(数量或金额)+ 成交价格 | 组合再平衡、指定成交价、含做空、按目标仓位调仓 | 目标仓位与当前仓位的差额要自己算;算错会出现越调越偏 |
| 先算信号、再转订单 | 先由信号推出订单,再交给订单入口 | 既要信号驱动的逻辑,又要精细控制成交价与数量 | 两步计算要对齐索引与日期,否则订单会落在错误的 bar 上 |
| 三入口组合使用 | 同一份价格数据分别建多个 Portfolio | 策略与基准对比、不同仓位口径的敏感性测试 | 它们各自独立,不共享资金与持仓;想放在一起比较须统一初始资金与费率 |
用 vectorbt 之前先问自己手上有什么:持仓、信号,还是明确的订单清单。
只想算「一直持有」的对照曲线,用 from_holding,输入持仓数量即可,不需要写信号逻辑。它常被用来回答「择时到底有没有跑赢不择时」,注意基准与策略要用同一段价格、同一套成本假设。
策略靠买卖信号驱动,用 from_signals:把指标算出的交叉、突破等布尔序列传进 entries / exits。连续加仓时确认 accumulate 是否符合预期,并且记得把信号 shift(1) 再交给回测。
需要指定成交价格、数量、或做组合再平衡、分批建仓时用 from_orders,由你自己给出每一笔订单。它比信号入口更贴近实盘下单口径,代价是所有仓位计算都要自己写清楚。
回测里常见的时间错误,是把「事后才知道的信息」当成「当时就能用的信息」。信号是否后移取决于信号计算时点与成交价假设。
| 错误写法 | 现象 | 正确写法 | 为什么 |
|---|---|---|---|
| 信号不 shift,直接传入 | 收益明显高于实盘,回撤还很小 | entries / exits 先 shift(1) 再传入 | 当日收盘价既用于生成信号又用于成交,等于用了未来信息 |
| 用当日收盘价算交叉、当日成交 | 换手越高,虚高越明显 | 信号延后一根 bar 生效 | 盘中无法提前确认收盘价,收盘后下单最快也要下一根 bar 成交 |
| 多标的信号不统一索引 | 某几只标的的收益对不上账 | 先对齐到同一交易日索引,再 shift | 各标的停牌与上市日期不同,索引错位会让信号落到错误日期 |
| 缺失值与停牌不处理 | 净值曲线出现异常跳变 | 缺失值补齐或对齐时剔除,再回测 | 缺口位置被当成零收益或异常收益,扭曲了区间收益 |
| 不设手续费与滑点 | 策略看起来稳赚成本几乎为零 | 回测时显式传入费率与滑点假设 | 真实交易有佣金、税费与冲击成本,换手越频繁偏差越大 |
| 指标用整段样本计算(如全区间标准化、排序) | 样本内极漂亮,样本外失效 | 只用当根及之前的窗口计算 | 整列一次性运算是向量化的便利,也最容易无意识地引用未来数据 |
| 在全部历史上挑较优参数 | 回测里较优的参数上实盘就钝化 | 做样本内外切分或滚动验证 | 择优这一步本身就使用了未来的信息,属于过拟合 |
import vectorbt as vbt
price = vbt.YFData.download("AAPL").get("Close")
fast = vbt.MA.run(price, 10)
slow = vbt.MA.run(price, 30)
entries = fast.ma_crossed_above(slow)
exits = fast.ma_crossed_below(slow)
# 反例:信号与价格同索引,等于用当日收盘价成交
pf = vbt.Portfolio.from_signals(price, entries, exits)
pf = vbt.Portfolio.from_signals(
price,
entries.shift(1).astype(bool), # 信号延后一根 bar 生效
exits.shift(1).astype(bool),
fees=0.001, slippage=0.001, # 成本假设要写进回测(此处数值仅示意)
)
pf.stats()
年化指标将观测期映射到年度尺度,实际频率与标注不匹配会造成错误缩放。失真幅度由索引、样本区间和实现口径共同决定;本页不编造固定倍数。
| 检查项 | 记录值 | 不匹配风险 | 建议复核 |
|---|---|---|---|
| 数据粒度与freq | 实际间隔与显式频率 | 年观测数、年化收益及比率缩放不一致 | 抽查相邻时间戳并对照交易日历 |
| 小时/分钟数据 | 1h/15m 等源数据间隔 | 误标日线可能使年化波动和风险比率失真 | 确认盘中交易时段、时区与缺失 bar |
| 不规则序列 | 停牌、休市和缺失值处理 | 固定频率可能不代表实际观测间距 | 记录重采样或剔除规则 |
| 多个市场混合 | 各资产的交易日历与时区 | 一个 freq 可能无法覆盖所有市场 | 先分市场统计,再明确组合换算口径 |
| 默认/自动推断 | 所用版本对索引的实际解释 | 版本或索引变化可能影响统计结果 | 读取输出并显式记录验证过的设置 |
| 对照实验 | 同一收益序列分别用正确/错误 freq | Sharpe、Sortino 变化依赖真实序列 | 保留原始收益与 API 版本,不外推固定倍数 |
同一套 vectorbt 代码换一个区间,这些数字会全变;先看口径,再看大小。
| 指标 | 含义 | 怎么判断 | 常见误读 |
|---|---|---|---|
| Start / End | 回测起止区间 | 区间是否覆盖了牛熊与震荡 | 不同区间的结果放在一起比大小 |
| Total Return | 区间总收益 | 与同区间基准、与同类策略比 | 只看收益不看承担的风险与回撤 |
| Annualized Return | 折算到年的收益 | 区间越长越有参考意义 | 短区间年化被放大,看着很惊人 |
| Max Drawdown | 区间内回撤幅度 | 先问自己能否承受这个回撤 | 把它当成「未来的回撤幅度」,换区间就会变 |
| Sharpe Ratio | 风险调整后收益 | 同区间、同口径下横向比较 | 忽略无风险利率与频率假设的差异 |
| Sortino Ratio | 只惩罚下行波动的风险调整收益 | 与 Sharpe 一起看是否一致 | 样本少或极端值少时数值极不稳定 |
| Win Rate | 盈利交易占比 | 与盈亏比、期望值一起看 | 以为高胜率就等于赚钱 |
| Profit Factor | 总盈利与总亏损之比 | 大于 1 才有正期望 | 不看交易次数与资金曲线形态 |
| Total Trades / Total Fees | 交易次数与累计费用 | 换手是否合理、成本吃掉多少收益 | 只看收益不看被费用消耗掉的部分 |
| 手续费与税费 | 费率、最低收费和买卖侧差异 | 净收益是否被交易成本吞噬 | 按账户/市场规则设值并做敏感性测试 |
| 滑点与点差 | 比例、固定价差或冲击成本近似 | 触发价是否接近可成交价 | 对高换手、低流动性区间加严假设 |
| 交易单位与资金 | 整股/手数、现金共享、杠杆与保证金 | 订单是否符合资金和最小单位约束 | 检查取整后的数量、余额与仓位峰值 |
| 涨跌停、停牌与跳空 | 拒单/延迟规则及隔夜价格缺口 | 模型是否把不可成交订单当成已成交 | OHLCV 不足以模拟排队成交,需额外状态数据 |
先确认代码来自哪个发行形态,再核对安装包、API 文档与许可。社区仓库可证明的功能不能与商业版页面混写;下表列出核验步骤,不宣称已经穷尽 PRO 当前功能清单。
| 核验项 | 社区版(开源仓库/PyPI) | VectorBT PRO | 使用前动作 |
|---|---|---|---|
| 发行和获取 | 公开仓库、PyPI 社区包 | 商业产品以官方页面为准 | 核对当前版本与访问条件 |
| 组合 API | 按社区 API reference 核验 | 商业版按其产品文档核验 | 对照安装包接口签名与示例 |
| 扩展与并行 | 不从第三方文章推断特定 API | 以 PRO 文档当前说明为准 | 核对版本、授权与可导入接口 |
| 许可 | 仓库 LICENSE 含 Commons Clause 条款 | 以 PRO 商业许可/合同为准 | 分别阅读原文,不互相推断 |
| 示例兼容性 | 使用与社区版本相符的官方示例 | 商业示例可能依赖不同包或凭证 | 未核实的 run_parallel 片段不当作标准 API |
| 性能声明 | 按本机版本与输入实测 | 以商业版文档和限定条件为准 | 不引用没有测试环境的倍率 |
| 官方来源 | 社区仓库和 API 文档 | PRO 页面和产品文档 | 逐版本核验功能和许可,不互相推断 |
它控制连续出现的信号是否累计加仓:开启后连续的买入信号会叠加仓位,不开启时可能只在首次买入生效。是否开启取决于你的策略意图(定投式加仓通常需要它),参数的准确语义与默认值以官方 API 文档为准。
三个工厂方法各自独立创建 Portfolio,一次回测通常只用一个;复杂逻辑建议先把信号或订单准备好再统一交给一个入口,避免口径混乱。若同一份数据要分别回测策略与基准,就建两个 Portfolio,但要把区间、初始资金与费率统一;入口语义以官方 API 文档为准
最常见的三个原因:信号没有 shift(1) 造成前视、没有计入手续费与滑点、多标的索引未对齐。按这三条依次排查通常能解释大部分差距;剩余差异则来自成交价口径与实盘流动性,这部分只能用实盘口径去校准;排查口径以官方实现为准
需要指定成交价、成交数量,或做组合再平衡、分批建仓等精细控制时用它;简单信号策略用信号入口更简洁。订单入口把所有仓位计算交给你,因此更适合对成交口径要求高的场景,具体字段以官方文档为准。
能作为同一口径下的横向参考,但不能直接当成未来预期:年化与比率类指标依赖数据频率、区间长度与无风险利率假设,换一个区间数值就会变。指标的定义与计算方式以官方实现为准,且任何回测数字都不构成投资建议。
每期观测数与年化换算不匹配会改变年化收益以及 Sharpe、Sortino 等指标的缩放。先检查索引的真实时间间隔、交易日历和时区,再按当前版本官方 API 指定并记录 freq;本页没有提供未经实测的固定误差倍数。
可以走另一条独立路线:本机已核验的量化类技能覆盖了回测与风险指标建模(quant-analyst)、A 股行情数据(akshare-finance / tushare-finance)等方向,一句话即可发起分析任务,但需要先安装对应客户端,且部分数据源要令牌。需要说明的是,vectorbt 与本机技能路线无已证实集成,两者是并列的独立路线,分析结果同样仅供研究;技能能力以官方技能文档为准