功能
配置与持久化
压缩保存任意研究对象、快速重新加载,并将设置保存在文件中
如何保存研究状态,并使其可复现?VBT 可将任意自身对象,从数据到 10,000 列投资组合,保存为一个可选压缩的文件,再重新加载分析;设置则集中放在配置文件中,而非散落于代码各处。
import time
from itertools import product
data = vbt.YFData.pull("BTC-USD", start="2020-01-01", end="2025-01-01")
pairs = list(product(range(2, 102), range(105, 305, 2)))
fast = vbt.MA.run(data.close, window=[f for f, _ in pairs], short_name="fast")
slow = vbt.MA.run(data.close, window=[s for _, s in pairs], short_name="slow")
entries = fast.ma_crossed_above(slow)
exits = fast.ma_crossed_below(slow)
pf = vbt.PF.from_signals(data, entries, exits, fees=0.001)
rows = {}
for compression in [None, "blosc2", "gzip", "lzma"]:
start = time.perf_counter()
path = pf.save("sweep", compression=compression)
save_s = time.perf_counter() - start
start = time.perf_counter()
loaded = vbt.PF.load(path)
load_s = time.perf_counter() - start
rows[str(compression)] = dict(
file=str(path),
size=vbt.file_size(path),
save_s=round(save_s, 2),
load_s=round(load_s, 2),
)
pd.DataFrame(rows).T file size save_s load_s
None sweep.pickle 11.7 MB 0.01 0.0
blosc2 sweep.pickle.blosc2 2.2 MB 0.06 0.09
gzip sweep.pickle.gzip 1.9 MB 1.15 0.01
lzma sweep.pickle.lzma 1.0 MB 1.93 0.06loaded.wrapper.shape, np.allclose(loaded.total_return, pf.total_return)((1827, 10000), True)每个文件重新加载后都是完整投资组合,包含全部 10,000 个策略,可直接计算度量和绘图。在 Apple M3 上,blosc2 将文件缩小至五分之一,保存和加载仍均不到 0.1 秒。lzma 文件最小,但写入约需两秒。
保存与加载
每个 VBT 对象,包括数据、指标、投资组合和切分器,都有 save 和 load;vbt.save 和 vbt.load 可处理任意 Python
对象,例如投资组合字典。只保存对象状态,不保存类,因此文件用当前 VBT 版本加载;加载旧文件时,重命名或移动过的类可映射到新名称。VBT
还提供重建钩子,在类参数变化后加载旧对象。跨机器迁移实验时使用匹配的软件包版本,旧对象有需要时则注册状态转换。
- 压缩: 支持 zip、gzip、bz2 和 lzma,安装后还支持 lz4、blosc 和 blosc2。
- 格式: 默认为 pickle,也可用
file_format选择可读的.cfg、.yaml和.toml文件,并无损还原为同一对象。 - 容器:
vbt.pdict是可以像其他 VBT 对象一样保存和加载的字典。 - 数据更新:
vbt.CSVDataSaver、vbt.HDFDataSaver和vbt.DuckDBDataSaver持续更新磁盘数据集,详见数据存储。
将实验内容放在一起
将输入、参数和结果保存到同一字典中,重新打开回测时就能知道它如何产生。对象保留各自形状和标签,因此价格与参数化指标输出可以共存,无须强行塞进同一张表:
import os
import tempfile
close = pd.Series([100.0, 105.0, 103.0], index=pd.date_range("2025-01-01", periods=3))
parameters = dict(fees=0.001, init_cash=1000)
pf = vbt.PF.from_holding(close, **parameters)
research = vbt.pdict(close=close, parameters=parameters, portfolio=pf, version=vbt.__version__)
with tempfile.TemporaryDirectory() as directory:
path = research.save(os.path.join(directory, "research"), compression="gzip")
restored = vbt.pdict.load(path)
print(list(restored))['close', 'parameters', 'portfolio', 'version']print(restored["parameters"]){'fees': 0.001, 'init_cash': 1000}print(restored["close"].equals(close), np.isclose(restored["portfolio"].total_return, pf.total_return))True True临时目录使示例可独立运行。若要在会话间保留文件,应使用项目路径。可将指标输出、信号或自己的笔记添加到同一字典,之后加载投资组合绘制新图或计算新度量,无须重跑回测。
重新启动数据更新
保存的研究对象与持续增长的市场数据存储用途不同。重新启动数据保存任务时,加载已有数据,围绕它创建新的 saver,并以 init_save=False
恢复定时更新。这样可以继续保存周期,无须重复写入初始数据集。数据存储介绍存储选项,工作流自动化介绍调度。
设置
vbt.settings 按领域保存整个软件包的默认值,例如 vbt.settings.portfolio["fees"] 或
vbt.settings.plotting["default_theme"]。修改一次后,后续使用该默认值的调用就会采用新值。显式调用参数则可为单次实验覆盖默认值。若要永久保存,可将设置放入工作目录中的
vbt.cfg、vbt.yml 或 vbt.toml,或用 VBT_SETTINGS_PATH 环境变量指定文件。VBT
导入时应用配置,见下方配置文件。工作目录中的 .env 文件也会在导入时加载,使 API 密钥无需写入代码。
可以只保存某个设置部分,例如投资组合默认值,再用 load_update 加载。对该部分调用 reset() 可恢复初始设置。这便于在多个 notebook
中复用项目手续费、初始资金和绘图偏好。
配置化对象与模板
多数 VBT 对象不可变:投资组合、指标或数据实例创建后不再改变。replace 返回修改了部分参数的副本,例如
pf.replace(init_cash=500),原对象保持不变,因此始终可以检查早期结果。replace
根据配置重建对象,不会重新运行策略的订单决策,因此新交易规则应使用新模拟测试。不可变性是约定,并非复制:输入按传入方式保存,因此由 DataFrame
创建对象后再原地修改它,可能使对象与已缓存结果不一致。应复制输入或重新创建对象。
设置和参数通过 vbt.merge_dicts 合并,逐键组合嵌套字典,因此传入参数只覆盖默认值中的对应键。以 vbt.unsetkey 为值可移除默认键;用
vbt.atomic_dict 包装字典则可整体替换。
模板将值的求解推迟到所需信息齐备时。vbt.Rep("name") 替换为当前上下文中的值,vbt.RepEval("expression")
在上下文中计算表达式,vbt.RepFunc(func)
则以该上下文调用函数。投资组合优化页用模板为每个再平衡窗口提供相应收益切片,详见下方模板。
✅ VBT 扩展常见配置格式(INI、YAML、TOML),形成自己的配置格式,使用户可保存、内省、修改及加载复杂内部对象。其主要优点是可读性和无损往返:任何对象都可编码后再解码,不丢失信息。主要特性包括嵌套结构、引用、字面量解析,以及任意 Python 表达式求值。此外,可为 VBT 创建配置文件并放在工作目录中,每次导入软件包时都用它更新默认设置。
[plotting]
default_theme = "dark"
[portfolio]
init_cash = 5000
[data.custom.binance.client_config]
api_key = "<YOUR_API_KEY>"
api_secret = "<YOUR_API_SECRET>"
[data.custom.ccxt.exchanges.binance.exchange_config]
apiKey = &data.custom.binance.client_config.api_key
secret = &data.custom.binance.client_config.api_secretfrom vectorbtpro import *
vbt.settings.portfolio["init_cash"]5000✅ 序列化的 VBT 对象有时会占用大量磁盘空间。此次更新支持多种压缩算法,让文件尽可能轻巧!🪶
data = vbt.RandomOHLCData.pull(
"RAND",
start="2022",
end="2023",
timeframe="1 minute",
seed=42
)
file_path = data.save()
print(vbt.file_size(file_path))21.0 MBfile_path = data.save(compression="blosc")
print(vbt.file_size(file_path))13.3 MB✅ 若工作流不需要访问器、Plotly 图表或多数其他可选功能,可完全关闭自动导入,将 VBT 加载时间缩短到一秒以内 ⏳
[importing]
auto_import = Falsestart = utc_time()
from vectorbtpro import *
end = utc_time()
end - start0.580937910079956✅ 与机器学习模型一样,每个原生 VBT 对象都可序列化并保存为二进制文件,分享数据与研究见解更加方便!另一个好处是只序列化对象实际内容,不保存类定义,因此加载后的对象始终使用最新类定义。VBT 还实现了专门逻辑,在 API 出现破坏性变更时帮助“重建”对象 🏗️
data = vbt.YFData.pull("BTC-USD", start="2022-01-01", end="2022-06-01")
def backtest_month(close):
return vbt.PF.from_random_signals(close, n=10, seed=42)
month_pfs = data.close.resample(vbt.offset("M")).apply(backtest_month)
month_pfsDate
2022-01-01 00:00:00+00:00 Portfolio(\n wrapper=ArrayWrapper(\n ...
2022-02-01 00:00:00+00:00 Portfolio(\n wrapper=ArrayWrapper(\n ...
2022-03-01 00:00:00+00:00 Portfolio(\n wrapper=ArrayWrapper(\n ...
2022-04-01 00:00:00+00:00 Portfolio(\n wrapper=ArrayWrapper(\n ...
2022-05-01 00:00:00+00:00 Portfolio(\n wrapper=ArrayWrapper(\n ...
Freq: MS, Name: Close, dtype: objectvbt.save(month_pfs, "month_pfs")
month_pfs = vbt.load("month_pfs")
month_pfs.apply(lambda pf: pf.total_return)Date
2022-01-01 00:00:00+00:00 -0.083672
2022-02-01 00:00:00+00:00 0.173909
2022-03-01 00:00:00+00:00 -0.006249
2022-04-01 00:00:00+00:00 -0.057868
2022-05-01 00:00:00+00:00 0.019209
Freq: MS, Name: Close, dtype: float64✅ 扩展类很容易,但 VBT 以函数为中心,如何增强函数或改变流程?最简单的方式是引入用户提供的小函数(即回调),由主函数在某个时刻调用。然而,主函数需要知道向回调传什么参数,以及如何处理输出。更好的方式是:允许主函数的大多数参数变成回调,再执行这些回调以取得实际值。这些参数称为“模板”,过程称为“替换”。某些参数(如数组)只有在信息齐备后才适合构建,例如其他数组已完成广播,此时模板尤其有用。每个替换时机都有自己的标识符,便于控制何时替换模板。模板在 VBT 中是一等公民,集成到大多数函数中,提供出色的灵活性!🧞
def resample_apply(index, by, apply_func, *args, template_context={}, **kwargs):
grouper = index.vbt.get_grouper(by)
results = {}
with vbt.ProgressBar() as pbar:
for group, group_idxs in grouper:
group_index = index[group_idxs]
context = {"group": group, "group_index": group_index, **template_context}
final_apply_func = vbt.substitute_templates(apply_func, context, eval_id="apply_func")
final_args = vbt.substitute_templates(args, context, eval_id="args")
final_kwargs = vbt.substitute_templates(kwargs, context, eval_id="kwargs")
results[group] = final_apply_func(*final_args, **final_kwargs)
pbar.update()
return pd.Series(results)
data = vbt.YFData.pull(["BTC-USD", "ETH-USD"], missing_index="drop")
resample_apply(
data.index, "Y",
lambda x, y: x.corr(y),
vbt.RepEval("btc_close[group_index]"),
vbt.RepEval("eth_close[group_index]"),
template_context=dict(
btc_close=data.get("Close", "BTC-USD"),
eth_close=data.get("Close", "ETH-USD")
)
)2017 0.808930
2018 0.897112
2019 0.753659
2020 0.940741
2021 0.553255
2022 0.975911
2023 0.974914
Freq: A-DEC, dtype: float64版权所有 © 2021–2026 Oleg Polakow。保留所有权利。
本站内容与文档供使用和评估 VectorBT PRO 以及教育用途。任何其他用途,包括开发或支持竞争产品或服务,均须事先取得书面同意。