功能

数据存储与数据库

在文件、DuckDB、SQL 数据库和 ArcticDB 中保存和查询市场数据

下载后的市场数据应放在哪里,又如何只读取需要的部分?VBT 可将任意数据对象保存到 CSV、HDF5、Feather、Parquet、DuckDB、SQL 数据库或 ArcticDB,再读回可用于研究的数据对象。在 Python 中存储市场数据,并为下一次回测选择所需日期和标的。

Store four weeks of minute bars in DuckDB and query one week
data = vbt.GBMOHLCData.pull(  
    ["AAA", "BBB", "CCC"],
    start="2024-01-01",
    end="2024-01-29",
    timeframe="1min",
    std=0.0005,
    seed=42,
)
data.loc[:"2024-01-21 23:59"].to_duckdb(connection="market.duckdb")
data.loc["2024-01-22":].to_duckdb(  
    connection="market.duckdb",
    if_exists="append",
)
vbt.DuckDBData.list_tables(connection="market.duckdb")
['AAA', 'BBB', 'CCC']
week = vbt.DuckDBData.pull(
    ["AAA", "BBB", "CCC"],
    start="2024-01-15",
    end="2024-01-22",
    tz="UTC",  
    connection="market.duckdb",
)
week.close.shape
(10080, 3)
week.close.head(3).round(2)
symbol                       AAA     BBB    CCC
2024-01-15 00:00:00+00:00  67.01  176.99  60.39
2024-01-15 00:01:00+00:00  66.99  176.51  60.20
2024-01-15 00:02:00+00:00  67.18  175.62  60.15

每个标的成为一张表,追加操作延长历史,查询则仅返回请求的一周数据,作为普通数据对象,可直接用于指标和回测。

选择存储格式

根据采集和复用数据的方式选择:

您的需求从哪里开始
可检查并与其他工具交换的文件CSV
在一个文件中存储多个标的数据集,并在之后追加HDF5
保存 DataFrame,用 Python 和 Arrow 工具重新加载Feather
一次写入、反复查询的压缩归档Parquet,适合时按日期分区
对本地表或已有 Parquet 文件执行 SQL 分析DuckDB
数据已存于 PostgreSQL、MySQL 或 SQLite通过 vbt.SQLData 使用 SQLAlchemy
在本地或对象存储中保存有版本的 DataFrameArcticDB

可以同时使用多种格式。保留 Parquet 归档,用 DuckDB 查询子集,再将结果传入回测。下面示例展示存储和读取。

文件

每个数据对象都有 to_csv、to_hdf、to_feather 和 to_parquet,对应的 vbt.CSVData、vbt.HDFData、vbt.FeatherData 和 vbt.ParquetData 类负责读回文件。同样四周的分钟 K 线,根据格式不同,占用空间差别很大:

Compare file sizes for 40,320 bars of three symbols
import os

data.to_csv("csv")
data.to_hdf("data.h5")
data.to_parquet("parquet")
data.to_feather("feather")

def size_mb(path):
    if os.path.isfile(path):
        return os.path.getsize(path) / 1e6
    return sum(
        os.path.getsize(os.path.join(d, f))
        for d, _, files in os.walk(path)
        for f in files
    ) / 1e6

paths = ["csv", "data.h5", "parquet", "feather", "market.duckdb"]
pd.Series({path: round(size_mb(path), 1) for path in paths})
csv              12.0
data.h5           5.2
parquet           5.9
feather           4.8
market.duckdb     5.8
dtype: float64

CSV 几乎处处可读,但体积最大、解析最慢。HDF5 将多个标的保存在同一文件。Parquet 采用压缩列式存储,多数数据工具可读取,并能按标的或日期分区。读取文件夹会发现其中的文件,按文件名匹配标的。CSV 和 HDF 读取器可分块处理,仅保留需要的行。Parquet 筛选器可选择日期或其他值,如下方分区数据集示例所示。

使用独立文件时,每个文件被视为一个标的。分区 Parquet 目录则可为同一标的保留多个日期分区。对于已有的每日文件集合,可用 Pandas 合并并使用 vbt.Data.from_data,也可通过 DuckDB 查询。

数据库

vbt.DuckDBData 在 DuckDB 中存储和查询数据。DuckDB 是运行于进程内的嵌入式分析数据库,也能直接查询 Parquet 和 CSV 文件。vbt.SQLData 支持 SQLAlchemy 能连接的任意数据库,例如 PostgreSQL、MySQL、SQLite;vbt.ArcticDBData 则利用 ArcticDB 存储大型版本化时间序列。DuckDB 和 SQLData 接受自定义 SQL 查询,并能列出模式、表和视图。ArcticDBData 列出库与标的,并通过 ArcticDB 自身 API 读取。vbt.DuckDBData.pull 默认以只读方式打开数据库,因此多个研究进程可同时读取同一文件。连接可作为对象传入,也可根据 URL 创建。反复执行 SQL 读取和更新时,可复用 SQLAlchemy 引擎及其连接池。

ArcticDBData 可选择已存的 version、日期区间和列。因此即使写入新数据后,在比较研究运行时,仍能重新访问保存的数据集版本。

使用现有数据存储

无需为每个标的单独建表。自定义查询可从共享表选择行、连接其他输入,并为 VBT 重命名列。结果会成为与提供商下载相同类型的数据对象。

这个小示例创建一个包含两个标的虚构价格的 Parquet 文件,再仅读取最后两天。在您自己的归档中,文件已经存在:

Query an existing multi-symbol Parquet file
archive_rows = pd.DataFrame(
    {
        "timestamp": pd.to_datetime(["2024-01-01", "2024-01-02", "2024-01-03"] * 2, utc=True),
        "ticker": ["AAA"] * 3 + ["BBB"] * 3,
        "price": [100.0, 102.0, 104.0, 50.0, 49.0, 51.0],
    }
)
archive_rows.to_parquet("archive.parquet", index=False)
archive_data = vbt.DuckDBData.pull(
    ["AAA", "BBB"],
    query=vbt.key_dict(
        {
            symbol: f"""
                SELECT timestamp, price AS Close
                FROM read_parquet('archive.parquet')
                WHERE ticker = '{symbol}' AND timestamp >= '2024-01-02'
                ORDER BY timestamp
            """
            for symbol in ["AAA", "BBB"]
        }
    ),
    index_col="timestamp",
)
archive_data.close
symbol                       AAA   BBB
timestamp
2024-01-02 00:00:00+00:00  102.0  49.0
2024-01-03 00:00:00+00:00  104.0  51.0

SQL 查询在数据成为 Pandas 对象之前选择行和列。DuckDB 可以在读取时筛选 Parquet 数据,让研究运行仅使用大型归档中的相关切片。SQLData 对 SQL 数据库提供同样的自定义查询入口。如果已有返回 DataFrame 的加载器,市场数据页面也展示了如何将其引入 VBT。

持续更新存储

让研究数据自动保持最新。数据保存器结合 update 和保存:按计划拉取新增数据并追加到存储,可停止并恢复而不丢失进度,如下方数据保存器功能介绍所示。研究过程中,数据缓存将每次下载保存在本地,使重新运行 notebook 时读取缓存而非提供商,如数据缓存功能介绍所示。

CSV、HDF5、SQL、DuckDB 和 ArcticDB 都有保存器,并支持多个标的。使用与存储匹配的保存器,例如 vbt.DuckDBDataSaver 或 vbt.SQLDataSaver,并传入知道如何获取更新的提供商数据对象。数据流水线页面介绍增量更新与调度。

保存研究会话

如需将数据与 VBT 设置和获取参数一同保留,可用 data.save 保存整个对象,再通过数据类的 load 方法恢复。这适合在另一会话中继续使用同一提供商对象。Parquet 等表格导出则适合与其他工具共享底层数据。配置与持久化介绍 VBT 对象的保存和恢复。

数据缓存

✅ 所有数据类现在都支持数据缓存,让您从(远程)数据源拉取时将数据缓存在磁盘上。缓存使用高效键值存储 LMDB。缓存键根据类、标的和其他获取参数的哈希生成,确保不同拉取分别缓存。

Pull AAPL minute data from TradingView or cache if available
data = vbt.TVData.pull(
    "AAPL",
    exchange="NASDAQ",
    timeframe="1 minute",
    tz="America/New_York",
    cache=True  
)

ArcticDB

✅ ArcticDB 是为 Python 数据科学生态设计的无服务器 DataFrame 数据库引擎。它依托通用对象存储(兼容 S3 的存储及 Azure Blob Storage),大规模存储、读取和处理 Series 与 DataFrame。除运行中的 Python 环境和对象存储访问权限外,ArcticDB 无需额外基础设施,数秒即可安装。

Save minute data to a ArcticDB database, and read one day
data = vbt.TVData.pull(
    "AAPL",
    exchange="NASDAQ",
    timeframe="1 minute",
    tz="America/New_York"
)

data.to_arcticdb()

day_data = vbt.ArcticDBData.pull(
    "AAPL",
    start="2026-02-27 09:30:00",
    end="2026-02-27 16:00:00",
    tz="America/New_York",
)
day_data.get()
                             Open    High     Low    Close    Volume
datetime
2026-02-27 09:30:00-05:00  272.90  272.90  269.80  269.960   49256.0
2026-02-27 09:31:00-05:00  269.97  270.12  269.19  269.230   43715.0
2026-02-27 09:32:00-05:00  269.18  270.05  269.18  269.890   33913.0
2026-02-27 09:33:00-05:00  269.85  270.01  269.60  269.770   27569.0
2026-02-27 09:34:00-05:00  269.74  269.98  269.40  269.500   22963.0
...                           ...     ...     ...      ...       ...
2026-02-27 15:55:00-05:00  264.41  264.51  263.15  263.490  162153.0
2026-02-27 15:56:00-05:00  263.51  263.78  263.23  263.670  114741.0
2026-02-27 15:57:00-05:00  263.67  263.78  262.89  263.655  113641.0
2026-02-27 15:58:00-05:00  263.61  263.92  263.25  263.620  158462.0
2026-02-27 15:59:00-05:00  263.68  264.28  263.65  264.200  107619.0

[390 rows x 5 columns]

SQL 查询

✅ 借助 DuckDB,现在可以直接对数据实例运行 SQL 查询。

Run a rolling average of 14 days on minute data using SQL
data = vbt.TVData.pull(
    "AAPL",
    exchange="NASDAQ",
    timeframe="1 minute",
    tz="America/New_York"
)

data.sql("""
    SELECT datetime, AVG(Close) OVER(
        ORDER BY "datetime" ASC
        RANGE BETWEEN INTERVAL 14 DAYS PRECEDING AND CURRENT ROW
    ) AS "Moving Average"
    FROM "AAPL";
""")
datetime
2023-09-11 09:30:00-04:00    180.080000
2023-09-11 09:31:00-04:00    179.965000
2023-09-11 09:32:00-04:00    180.000000
2023-09-11 09:33:00-04:00    180.022500
2023-09-11 09:34:00-04:00    179.984000
                                    ...
2023-10-20 15:55:00-04:00    177.786669
2023-10-20 15:56:00-04:00    177.785492
2023-10-20 15:57:00-04:00    177.784322
2023-10-20 15:58:00-04:00    177.783166
2023-10-20 15:59:00-04:00    177.781986
Name: Moving Average, Length: 11700, dtype: float64

DuckDB

✅ DuckDB 是高性能分析数据库系统,提供功能完善的 SQL 方言来操作各种数据存储。即使数据无法装入内存,也能对本地数据运行分析查询,无需分布式框架;还可以直接查询 CSV、Parquet 和 JSON 文件。

Save minute data to a DuckDB database, and read one day
data = vbt.TVData.pull(
    "AAPL",
    exchange="NASDAQ",
    timeframe="1 minute",
    tz="America/New_York"
)

URL = "database.duckdb"
data.to_duckdb(connection=URL)

day_data = vbt.DuckDBData.pull(
    "AAPL",
    start="2023-10-02 09:30:00",
    end="2023-10-02 16:00:00",
    tz="America/New_York",
    connection=URL
)
day_data.get()
                              Open    High     Low   Close    Volume
datetime
2023-10-02 09:30:00-04:00  171.260  171.34  170.93  171.10   61654.0
2023-10-02 09:31:00-04:00  171.130  172.37  171.13  172.30   53481.0
2023-10-02 09:32:00-04:00  172.310  172.64  172.16  172.64   44750.0
2023-10-02 09:33:00-04:00  172.640  172.97  172.54  172.78   53195.0
2023-10-02 09:34:00-04:00  172.780  173.07  172.75  173.00   47416.0
...                            ...     ...     ...     ...       ...
2023-10-02 15:55:00-04:00  173.300  173.51  173.26  173.51   61619.0
2023-10-02 15:56:00-04:00  173.525  173.59  173.42  173.43   45066.0
2023-10-02 15:57:00-04:00  173.430  173.55  173.42  173.50   45220.0
2023-10-02 15:58:00-04:00  173.510  173.60  173.46  173.56   47371.0
2023-10-02 15:59:00-04:00  173.560  173.78  173.56  173.75  161253.0

[390 rows x 5 columns]

SQLAlchemy

✅ SQLAlchemy 提供标准接口,让开发者编写不依赖具体数据库的代码,与众多 SQL 数据库引擎通信。借助它,您现在可以轻松将数据存入 SQL 数据库,也能从中读取。

Save minute data to a PostgreSQL database, and read one day
data = vbt.TVData.pull(
    "AAPL",
    exchange="NASDAQ",
    timeframe="1 minute",
    tz="America/New_York"
)

URL = "postgresql://postgres:postgres@localhost:5432"
data.to_sql(engine=URL)

day_data = vbt.SQLData.pull(
    "AAPL",
    start="2023-10-02 09:30:00",
    end="2023-10-02 16:00:00",
    tz="America/New_York",
    engine=URL
)
day_data.get()
                              Open    High     Low   Close    Volume
datetime
2023-10-02 09:30:00-04:00  171.260  171.34  170.93  171.10   61654.0
2023-10-02 09:31:00-04:00  171.130  172.37  171.13  172.30   53481.0
2023-10-02 09:32:00-04:00  172.310  172.64  172.16  172.64   44750.0
2023-10-02 09:33:00-04:00  172.640  172.97  172.54  172.78   53195.0
2023-10-02 09:34:00-04:00  172.780  173.07  172.75  173.00   47416.0
...                            ...     ...     ...     ...       ...
2023-10-02 15:55:00-04:00  173.300  173.51  173.26  173.51   61619.0
2023-10-02 15:56:00-04:00  173.525  173.59  173.42  173.43   45066.0
2023-10-02 15:57:00-04:00  173.430  173.55  173.42  173.50   45220.0
2023-10-02 15:58:00-04:00  173.510  173.60  173.46  173.56   47371.0
2023-10-02 15:59:00-04:00  173.560  173.78  173.56  173.75  161253.0

[390 rows x 5 columns]

PyArrow 与 FastParquet

✅ 数据可以通过 PyArrow 写入 Feather,也可通过 PyArrow 或 FastParquet 写入 Parquet。Parquet 在一次写入、多次读取的情景下表现尤其出色,具有高效压缩和解压能力,非常适合时间序列存储。

Save minute data to a Parquet dataset partitioned by day, and read one day
data = vbt.TVData.pull(
    "AAPL",
    exchange="NASDAQ",
    timeframe="1 minute",
    tz="America/New_York"
)

data.to_parquet(partition_by="day")  

day_data = vbt.ParquetData.pull("AAPL", filters=[("group", "==", "2023-10-02")])
day_data.get()
                              Open    High     Low   Close    Volume
datetime
2023-10-02 09:30:00-04:00  171.260  171.34  170.93  171.10   61654.0
2023-10-02 09:31:00-04:00  171.130  172.37  171.13  172.30   53481.0
2023-10-02 09:32:00-04:00  172.310  172.64  172.16  172.64   44750.0
2023-10-02 09:33:00-04:00  172.640  172.97  172.54  172.78   53195.0
2023-10-02 09:34:00-04:00  172.780  173.07  172.75  173.00   47416.0
...                            ...     ...     ...     ...       ...
2023-10-02 15:55:00-04:00  173.300  173.51  173.26  173.51   61619.0
2023-10-02 15:56:00-04:00  173.525  173.59  173.42  173.43   45066.0
2023-10-02 15:57:00-04:00  173.430  173.55  173.42  173.50   45220.0
2023-10-02 15:58:00-04:00  173.510  173.60  173.46  173.56   47371.0
2023-10-02 15:59:00-04:00  173.560  173.78  173.56  173.75  161253.0

[390 rows x 5 columns]

数据保存器

✅ 想象一个脚本,可以定期从交易所拉取最新数据并保存到磁盘,全程无需干预。VBT 实现了两个这样的类:一个保存为 CSV,另一个保存为 HDF。

BTCUSDT_1m_saver.py
from vectorbtpro import *

import logging
logging.basicConfig(level=logging.INFO)

if __name__ == "__main__":
    if vbt.CSVDataSaver.file_exists():
        csv_saver = vbt.CSVDataSaver.load()
        csv_saver.update()
        init_save = False
    else:
        data = vbt.BinanceData.pull(
            "BTCUSDT",
            start="10 minutes ago UTC",
            timeframe="1 minute"
        )
        csv_saver = vbt.CSVDataSaver(data)
        init_save = True
    csv_saver.update_every(1, "minute", init_save=init_save)
    csv_saver.save()  
Run in console and then interrupt
$ python BTCUSDT_1m_saver.py
2023-02-01 12:26:36.744000+00:00 - 2023-02-01 12:36:00+00:00: : 1it [00:01,  1.22s/it]
INFO:vectorbtpro.data.saver:Saved initial 10 rows from 2023-02-01 12:27:00+00:00 to 2023-02-01 12:36:00+00:00
INFO:vectorbtpro.utils.schedule_:Starting schedule manager with jobs [Every 1 minute do update(save_kwargs=None) (last run: [never], next run: 2023-02-01 13:37:38)]
INFO:vectorbtpro.data.saver:Saved 2 rows from 2023-02-01 12:36:00+00:00 to 2023-02-01 12:37:00+00:00
INFO:vectorbtpro.data.saver:Saved 2 rows from 2023-02-01 12:37:00+00:00 to 2023-02-01 12:38:00+00:00
INFO:vectorbtpro.utils.schedule_:Stopping schedule manager
Run in console again to continue
$ python BTCUSDT_1m_saver.py
INFO:vectorbtpro.utils.schedule_:Starting schedule manager with jobs [Every 1 minute do update(save_kwargs=None) (last run: [never], next run: 2023-02-01 13:42:08)]
INFO:vectorbtpro.data.saver:Saved 5 rows from 2023-02-01 12:38:00+00:00 to 2023-02-01 12:42:00+00:00
INFO:vectorbtpro.utils.schedule_:Stopping schedule manager

本地数据

✅ 获取远程数据后,您很可能希望将其保存到磁盘。现在有两种新选择:序列化整个数据类,或将实际数据保存为 CSV 或 HDF5。每个数据集可保存在一个扁平文件中,比数据库更易处理。保存后,可以通过反序列化,或使用专门加载 CSV、HDF5 文件的数据类,轻松重新读取。这些类支持按行和日期时间区间筛选、更新、分块,甚至智能数据集搜索:递归遍历子目录,返回匹配指定 glob 模式或正则表达式的数据集 🧲

Fetch and save symbols separately, then load them back jointly
btc_data = vbt.BinanceData.pull("BTCUSDT")
eth_data = vbt.BinanceData.pull("ETHUSDT")

btc_data.to_hdf()
eth_data.to_hdf()

data = vbt.BinanceData.from_hdf(start="2020", end="2021")
键 2/2
data.close
symbol                      BTCUSDT  ETHUSDT
Open time
2020-01-01 00:00:00+00:00   7200.85   130.77
2020-01-02 00:00:00+00:00   6965.71   127.19
2020-01-03 00:00:00+00:00   7344.96   134.35
2020-01-04 00:00:00+00:00   7354.11   134.20
2020-01-05 00:00:00+00:00   7358.75   135.37
...                             ...      ...
2020-12-27 00:00:00+00:00  26281.66   685.11
2020-12-28 00:00:00+00:00  27079.41   730.41
2020-12-29 00:00:00+00:00  27385.00   732.00
2020-12-30 00:00:00+00:00  28875.54   752.17
2020-12-31 00:00:00+00:00  28923.63   736.42

[366 rows x 2 columns]

版权所有 © 2021–2026 Oleg Polakow。保留所有权利。

本站内容与文档供使用和评估 VectorBT PRO 以及教育用途。任何其他用途,包括开发或支持竞争产品或服务,均须事先取得书面同意。