功能
多维研究
将资产、参数和策略作为带标签列运行,再对结果索引与堆叠
VBT 如何一次测试数千个策略变体,又如何浏览结果?每个资产、参数值和策略都成为带标签的列。输入相互广播,在 NumPy 数组上计算,再以 pandas 对象返回,可像普通 DataFrame 一样选择、分组和堆叠。
from itertools import product
data = vbt.YFData.pull(
["BTC-USD", "ETH-USD", "SOL-USD"],
start="2022-01-01",
end="2025-01-01",
)
fast_windows, slow_windows = zip(*product([5, 10, 15, 20], [30, 50, 70, 90]))
fast = vbt.MA.run(data.close, window=list(fast_windows), short_name="fast")
slow = vbt.MA.run(data.close, window=list(slow_windows), short_name="slow")
entries = fast.ma_crossed_above(slow)
exits = fast.ma_crossed_below(slow)
entries.shape(1096, 48)entries.columns[:4]MultiIndex([(5, 30, 'BTC-USD'),
(5, 30, 'ETH-USD'),
(5, 30, 'SOL-USD'),
(5, 50, 'BTC-USD')],
names=['fast_window', 'slow_window', 'symbol'])pf = vbt.PF.from_signals(data, entries, exits, fees=0.001)
pf.total_return.groupby("symbol").describe()[["mean", "min", "max"]].round(3) mean min max
symbol
BTC-USD 0.758 0.174 1.481
ETH-USD 0.051 -0.195 0.474
SOL-USD 1.394 0.104 4.285print(round(pf[(10, 50, "ETH-USD")].total_return, 4)) 0.1389pf.xs("ETH-USD", level="symbol", axis=1).wrapper.shape (1096, 16)每列都是完整回测,拥有自己的订单、交易和度量;列标签携带全部参数,因此可用普通 pandas 汇总结果。
这样,同一实验可回答多个问题:哪些资产表现稳定、哪些参数范围经得起检验、不同成本假设会使结果变化多少。从输入到最终交易和绩效表,标签始终保留。
研究模型
VBT 对象由 NumPy 数组和封装器组成,封装器记录索引、列、频率与分组。编译代码在原始数组上计算,再按正确标签将结果封装回 pandas,无论结果是时间序列、每列一个值还是每组一个值。Series 和 DataFrame 使用相同代码,因此为单个资产编写的函数也适用于一千个资产。
市场数据的每个特征各有数组:收盘价一张表、最高价另一张表、入场信号又一张表。列标识交易品种和实验。VBT 数据对象可像开篇示例一样直接提供价格特征,也可从自己的数据流程传入独立数组。
广播
不同形状输入通过广播组合:标量应用于全部元素,Series 应用于每列,单行 DataFrame 应用于每根 K 线,完整 DataFrame
则逐元素应用。带标签列按层级匹配,因此上例三列价格可与共享 symbol 层级的 48 列信号对齐。小输入保持小规模:模拟内部将标量手续费作为标量读取,不会扩展成完整数组。
一维 NumPy 数组被视为时间序列,因此每根 K 线必须有一个值。逐列数值应放入 np.array([[0.001, 0.002, 0.003]])
这样的单行数组,或带标签对象。行按日期匹配:日期不同的输入按全部日期连接,并以 NaN
填充,因此不同时间周期的数据应先重采样到同一索引,参见多时间周期分析。
普通 pandas 运算符严格匹配标签,因此不同参数层级的指标输出直接比较会失败。在左操作数前加 .vbt,改用 VBT
广播,详见交易信号。
匹配价格和信号列,即可每列运行一个回测;也可通过下方 DataFrame 笛卡尔积扩展实验,在比较组合时让每个品种始终对应自己的价格。
下方 DataFrame 笛卡尔积功能将一个 DataFrame 的每列与另一个的每列组合;索引字典则无需先构建数组,即可在选定日期和列设置值。
将假设作为带标签情景比较
参数不仅能描述指标,也能描述执行假设。这里用两条虚构价格序列,分别测试零手续费和 1% 手续费。各手续费成为一个额外列层级,因此无需从独立运行收集结果,就能将收益重塑为表格:
close = pd.DataFrame(
{"A": [100.0, 105.0, 110.0], "B": [100.0, 98.0, 95.0]},
index=pd.date_range("2025-01-01", periods=3),
).rename_axis(columns="symbol")
scenarios = vbt.PF.from_holding(close, fees=vbt.Param([0.0, 0.01], name="fee"))
scenarios.total_return.unstack("symbol").round(4)symbol A B
fee
0.00 0.1000 -0.0500
0.01 0.0891 -0.0594这里是小数形式的总收益率,结束时持仓仍未平仓。手续费减少各回测入场时可以买入的数量。同一方法也能比较滑点或仓位假设。参数优化介绍更大的搜索、条件组合和随机抽样。
分组
列可为报告或模拟而分组。group_by 按层级、标签列表,或用 True 将全部列组合成组;cash_sharing=True
将一个组变成一个账户。事后设置分组只影响结果报告方式,因此只要各组列相邻,同一批回测即可按资产、参数值或整体读取。
开篇示例中,pandas 汇总按品种将各自 16 个收益分组,以比较参数选择。这与对投资组合本身分组不同,后者可报告多列共同持有的表现。共享资金投资组合参见投资组合记账,独立策略之间的配置参见多策略投资组合。
堆叠运行结果
独立运行可以合并为一个对象。按列堆叠将不同实验并排,例如在策略网格旁放上买入持有:
hold_pf = vbt.PF.from_holding(data)
both = vbt.PF.column_stack(
pf,
hold_pf,
wrapper_kwargs=dict(keys=pd.Index(["crossover", "hold"], name="strategy")),
)
both.wrapper.shape(1096, 51)both.total_return["hold"].round(3)BTC-USD 0.959
ETH-USD -0.116
SOL-USD 0.060
Name: total_return, dtype: float64按行堆叠将连续时期,例如前向滚动的各测试窗口,连接为一段历史。堆叠后的投资组合保留记录,并据此重新计算现金和净值。独立模拟的各窗口都从新资金开始:首个窗口的初始现金成为堆叠组合的初始现金,后续窗口的初始现金则在各自起点作为入金加入,因此收益率保持正确,但净值会跳变。combine_init_cash=True
改为将全部资金视为初始现金,使后续窗口的资金在早期闲置,稀释收益。若要让同一账户贯穿全部窗口,应从上一窗口最终状态继续,并以 chained=True
堆叠,参见实时模拟。下方按列堆叠和按行堆叠功能展示完整投资组合的两种方式。
这也让您可分段处理长历史,或围绕多组入场和离场事件模拟数据窗口。边界处保留预期现金与持仓状态,再按行堆叠结果分析。大批独立实验如何分批,参见并行执行与缓存。
索引
每个 VBT 对象都可像 pandas 一样索引:按列标签、用 .loc 和 .iloc 按日期与位置、按参数层级,或用
xs。按日期切片投资组合会得到该时期的新投资组合,无须重跑模拟,记录和度量均按窗口调整。设置器则反向使用同一方式:借助数据封装器,按日期、日内时间、标签或掩码赋值。下方切片与索引对齐功能展示两者。
检查订单、绘制净值或比较回撤前,先选择交易品种或参数组合。标签让您能从汇总表回到产生特定结果的回测。实验维度较多时,也可重命名、重排或删除索引层级,保持报告易读。
Pandas 访问器
导入 VBT 后,每个 Series、DataFrame 和 Index 都会增加 .vbt 访问器,以及 .vbt.returns、.vbt.signals 和 .vbt.ohlcv
等专门命名空间。数据仍是普通 pandas 对象,因此同一行中可混用 VBT 和 pandas 操作。访问器在运行时注册,部分编辑器无法自动补全,但 notebook
可以。vbt.df_acc(df) 和 vbt.sr_acc(sr) 以显式对象返回同一访问器,编辑器可以内省。
✅ 多个参数化指标可能产生形状与列不同的 DataFrame,而它们通常共享不应相互组合的列层级(如 "symbol"),使笛卡尔积构建变得复杂。现在可以通过新方法,分块交叉连接多个 DataFrame。
data = vbt.YFData.pull(["BTC-USD", "ETH-USD"], missing_index="drop")
sma = data.run("sma", timeperiod=[10, 20], unpack=True)
ema = data.run("ema", timeperiod=[30, 40], unpack=True)
wma = data.run("wma", timeperiod=[50, 60], unpack=True)
sma, ema, wma = sma.vbt.x(ema, wma)
entries = sma.vbt.crossed_above(wma)
exits = ema.vbt.crossed_below(wma)
entries.columnsMultiIndex([(10, 30, 50, 'BTC-USD'),
(10, 30, 50, 'ETH-USD'),
(10, 30, 60, 'BTC-USD'),
(10, 30, 60, 'ETH-USD'),
(10, 40, 50, 'BTC-USD'),
(10, 40, 50, 'ETH-USD'),
(10, 40, 60, 'BTC-USD'),
(10, 40, 60, 'ETH-USD'),
(20, 30, 50, 'BTC-USD'),
(20, 30, 50, 'ETH-USD'),
(20, 30, 60, 'BTC-USD'),
(20, 30, 60, 'ETH-USD'),
(20, 40, 50, 'BTC-USD'),
(20, 40, 50, 'ETH-USD'),
(20, 40, 60, 'BTC-USD'),
(20, 40, 60, 'ETH-USD')],
names=['sma_timeperiod', 'ema_timeperiod', 'wma_timeperiod', 'symbol'])手动创建数组并用 Pandas 设置数据往往很繁琐。现在有新功能来帮忙!任意可广播参数都可变成索引字典,其中包含在数组何处设置值的指令,并自动填入。它知道应更新哪个轴,非必要时不创建完整数组,节省 RAM ❤️
data = vbt.YFData.pull(["BTC-USD", "ETH-USD"])
tile = pd.Index(["daily", "weekly"], name="strategy")
pf = vbt.PF.from_orders(
data.close,
size=vbt.index_dict({
vbt.idx(
vbt.pointidx(every="day"),
vbt.colidx("daily", level="strategy")): 100,
vbt.idx(
vbt.pointidx(every="sunday"),
vbt.colidx("daily", level="strategy")): -np.inf,
vbt.idx(
vbt.pointidx(every="monday"),
vbt.colidx("weekly", level="strategy")): 100,
vbt.idx(
vbt.pointidx(every="monthend"),
vbt.colidx("weekly", level="strategy")): -np.inf,
}),
size_type="value",
direction="longonly",
init_cash="auto",
broadcast_kwargs=dict(tile=tile)
)
pf.sharpe_ratiostrategy symbol
daily BTC-USD 0.702259
ETH-USD 0.782296
weekly BTC-USD 0.838895
ETH-USD 0.524215
Name: sharpe_ratio, dtype: float64✅ 与选列类似,每个 VBT 对象现在也能用与 Pandas 相同的机制切片行 🔪 无须重新模拟,即可轻松分析和绘制模拟数据的任意子集!
data = vbt.YFData.pull("BTC-USD")
pf = vbt.PF.from_holding(data, freq="d")
pf.sharpe_ratio1.116727709477293pf.loc[:"2020"].sharpe_ratio 1.2699801554196481pf.loc["2021": "2021"].sharpe_ratio 0.9825161170278687pf.loc["2022":].sharpe_ratio -1.0423271337174647✅ 同类型复杂 VBT 对象可以轻松按列堆叠。例如,将多个无关交易策略合并为一个投资组合分析。最终对象底层仍是单一的多维结构,甚至可以比多个独立合并对象处理得更快 🫁
def strategy1(data):
fast_ma = vbt.MA.run(data.close, 50, short_name="fast_ma")
slow_ma = vbt.MA.run(data.close, 200, short_name="slow_ma")
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
return vbt.PF.from_signals(
data.close,
entries,
exits,
size=100,
size_type="value",
init_cash="auto"
)
def strategy2(data):
bbands = vbt.BBANDS.run(data.close, window=14)
entries = bbands.close_crossed_below(bbands.lower)
exits = bbands.close_crossed_above(bbands.upper)
return vbt.PF.from_signals(
data.close,
entries,
exits,
init_cash=200
)
data1 = vbt.BinanceData.pull("BTCUSDT")
pf1 = strategy1(data1)
pf1.sharpe_ratio0.9100317671866922data2 = vbt.BinanceData.pull("ETHUSDT")
pf2 = strategy2(data2)
pf2.sharpe_ratio-0.11596286232734827pf_sep = vbt.PF.column_stack((pf1, pf2))
pf_sep.sharpe_ratio0 0.910032
1 -0.115963
Name: sharpe_ratio, dtype: float64pf_join = vbt.PF.column_stack((pf1, pf2), group_by=True)
pf_join.sharpe_ratio0.42820898354646514✅ 同类型复杂 VBT 对象可以轻松按行堆叠。例如,为已有投资组合追加新数据,或将样本内投资组合与对应样本外投资组合连接起来 🧬
def strategy(data, start=None, end=None):
fast_ma = vbt.MA.run(data.close, 50, short_name="fast_ma")
slow_ma = vbt.MA.run(data.close, 200, short_name="slow_ma")
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
return vbt.PF.from_signals(
data.close[start:end],
entries[start:end],
exits[start:end],
size=100,
size_type="value",
init_cash="auto"
)
data = vbt.BinanceData.pull("BTCUSDT")
pf_whole = strategy(data)
pf_whole.sharpe_ratio0.9100317671866922pf_sub1 = strategy(data, end="2019-12-31")
pf_sub1.sharpe_ratio0.7810397448678937pf_sub2 = strategy(data, start="2020-01-01")
pf_sub2.sharpe_ratio1.070339534746574pf_join = vbt.PF.row_stack((pf_sub1, pf_sub2))
pf_join.sharpe_ratio0.9100317671866922✅ 不再要求每个 Pandas 数组具有相同索引。只要数据类型一致,所有需要相互广播的数组,其索引都会自动对齐。
btc_data = vbt.YFData.pull("BTC-USD")
btc_data.wrapper.shape(2817, 7)eth_data = vbt.YFData.pull("ETH-USD")
eth_data.wrapper.shape(1668, 7)ols = vbt.OLS.run(
btc_data.close,
eth_data.close
)
ols.predDate
2014-09-17 00:00:00+00:00 NaN
2014-09-18 00:00:00+00:00 NaN
2014-09-19 00:00:00+00:00 NaN
2014-09-20 00:00:00+00:00 NaN
2014-09-21 00:00:00+00:00 NaN
... ...
2022-05-30 00:00:00+00:00 2109.769242
2022-05-31 00:00:00+00:00 2028.856767
2022-06-01 00:00:00+00:00 1911.555689
2022-06-02 00:00:00+00:00 1930.169725
2022-06-03 00:00:00+00:00 1882.573170
Freq: D, Name: Close, Length: 2817, dtype: float64版权所有 © 2021–2026 Oleg Polakow。保留所有权利。
本站内容与文档供使用和评估 VectorBT PRO 以及教育用途。任何其他用途,包括开发或支持竞争产品或服务,均须事先取得书面同意。