Skip to main content

OHLCVault

OHLCV = Open/High/Low/Close/Volume, the universal bar format. Vault = immutable, checksummed snapshots.

Reproducible daily OHLCV data for A-share, Hong Kong and US markets — served as static files from a CDN. No API keys, no rate limits, no per-request billing.

import ohlcvault as ov

ov.connect()
df = ov.daily("600519.SH").to_pandas()      # 跨月自动拼接
ov.cross_section("cn", 20260918, limit=50)  # 当日全市场截面,按成交额排序

Why this exists

Most free market-data endpoints are APIs: stateful, rate-limited, silently revised, and impossible to reproduce. A backtest that ran last month can't be rerun today with the same inputs.

OHLCVault publishes immutable monthly shards with checksums instead. Each read is anchored to a snapshot id you can write down and reproduce later, on any machine.

Install

pip install ohlcvault            # core, zero runtime dependencies
pip install "ohlcvault[pandas]"  # + DataFrame helpers

The core has no third-party dependencies — only the standard library. That's deliberate: a data client shouldn't drag a dependency tree into your project, and it matters even more for agent/tooling contexts.

Usage

Everything (the five things you actually need)

import ohlcvault as ov
ov.connect()

# 1. Trading calendar
ov.calendar("cn", start=20260101)

# 2. Stock daily bars (cross-month stitching is handled for you)
ov.daily("600519.SH", start=20260101, end=20260918)

# 3. Index daily bars — a separate namespace, never mixed with stocks
ov.index_daily("000300.SH")

# 4. Symbol list — includes delisted stocks
ov.symbols("cn", type="stock")
ov.symbols("cn", type="stock", status="delisted")

# 5. Daily cross-section — sorted by turnover, no extra data files
ov.cross_section("cn", 20260918, sort_by="amount", limit=50)

Batch backtests

Month shards hold every symbol in the market for that month. Loading 200 symbols one-by-one would decompress the same file 200 times:

bars = ov.daily_many(["600519.SH", "000001.SZ", "300750.SZ"], start=20260101)
bars["600519.SH"].to_pandas()

Adjustment is a view, not a stored field

The dataset stores unadjusted prices only, plus the official cumulative back-adjustment factor. Forward/backward adjusted prices are computed client-side:

b = ov.daily("600519.SH")
ov.adjust(b, to="hfq")   # 后复权
ov.adjust(b, to="qfq")   # 前复权

This is not a limitation — it's the reason historical files never change. If forward-adjusted prices were stored, every dividend would rewrite all of history, and immutable caching would be impossible.

Reproducibility

st = ov.connect()
sid = st.snapshot                       # e.g. "6b197df3723871c5"
ov.connect(snapshot=sid)                # later, anywhere: exact same inputs

Offline / self-hosted mirrors

A mirror can be an HTTP(S) URL or a local directory:

ov.connect(mirrors=["/path/to/data"])

Mirrors are tried in order; whichever one succeeds is promoted to first place. Every file is checked against the sha256 in the snapshot manifest, and anything that fails is discarded and the next mirror is tried — bad bytes are never handed to the caller.

Data integrity

Guarantee How
No silently-corrupted data Every file verified against the snapshot's sha256
No silently-changed history Sealed months are never rewritten
No unverifiable numbers Missing adjustment factors raise, instead of returning raw prices
No hidden survivorship bias Delisted stocks are kept in the universe and in the data
Byte-for-byte reproduction Fixed-point integers, gzip with MTIME=0, no wall-clock timestamps

Delisted stocks matter. If your backtest universe only contains companies that are still listed today, your historical returns are systematically overstated. ov.symbols("cn", status="delisted") returns them, and their daily bars are complete over ipo … out.

Coverage and known gaps

Coverage is declared explicitly in meta/symbols/{market}.json under coverage, and ov.connect() prints it on startup. Current state:

Market Status Gaps
cn Daily bars + indices, 2000→present No Beijing Stock Exchange (upstream source doesn't provide it)
hk Symbol list only No daily bars yet; list is current listings only
us Symbol list only No daily bars yet; list is current listings only

hk / us symbol lists carry survivorship bias (the upstream source only returns currently-listed securities) and have no IPO dates. Their daily bars are pending.

An honest data project states its gaps. A dataset that quietly omits them is worse than one that is merely incomplete.

Performance

The store layout (one file per market per month, all symbols inside) is optimized for cross-section reads (one file = one whole trading day for the market) and for one-download-many-symbols workflows. Three layers make repeated reads fast:

  1. HTTP keep-alive — shard fetches reuse TLS connections instead of one handshake per request (cold-cache full-history pulls drop from minutes of handshake overhead).
  2. Shard LRU — connect(shard_cache=N) keeps N parsed monthly shards in memory (~20–25 MB each for recent full-market months; default 24 ≈ 500 MB).
  3. Per-symbol materialization (default on) — the first full-history read of a symbol writes a compact local copy under {cache}/symbols/{snapshot_id}/; any later read of the same symbol under the same snapshot is served in milliseconds and never touches a shard. Files are keyed by snapshot id, so a new snapshot invalidates them naturally. Disable with connect(materialize=False).

For SQL workflows, optionally materialize a snapshot into a local DuckDB file (pip install "ohlcvault[duckdb]" — an optional extra; the core stays zero-dependency):

p = ov.to_duckdb(store, markets=["cn"])   # writes {cache}/duckdb/{snapshot_id}.duckdb
con = ov.connect_duckdb(p)                # a local file, not a server
con.sql("SELECT d, close/1000.0 AS close FROM bars WHERE symbol='600519.SH' ORDER BY d")

to_duckdb() is idempotent per snapshot: an existing file for the same snapshot id is reused unless refresh=True. The DuckDB file is a derived local artifact — it is not part of the checksum chain and can be deleted/rebuilt at any time.

API

Function Purpose
connect(mirrors=, cache_dir=, snapshot=, shard_cache=, materialize=) Build the default client
symbols(market, type=, status=, board=) Symbol list
symbol(code) Single symbol entry
calendar(market, start=, end=) Trading calendar
daily(code, start=, end=) Stock daily bars, cross-month stitching
index_daily(code, start=, end=) Index daily bars
daily_many(codes, start=, end=) Batch read (preferred for backtests)
cross_section(market, date, sort_by=, limit=) Daily cross-section
adjust(bars_or_df, to="qfq"|"hfq"|"none") Adjustment view
to_duckdb(store, markets=, periods=) Materialize snapshot into a local DuckDB file (optional extra)
connect_duckdb(path) Open a materialized DuckDB file for SQL
snapshot() Current snapshot id

Date parameters (start / end / date) accept an int YYYYMMDD (preferred) or common string forms — "2026-09-18", "20260918", "2026/09/18". Anything unparseable raises DateError instead of failing deep inside the library.

The frozen data contract lives in SPEC.md — the client and the pipeline share nothing but this document and the files it describes. schema/example-*.json are machine-generated from real data, so the examples cannot drift from the contract.

License

MIT. See LICENSE.

Data is gathered from public sources. Verify before relying on it for anything consequential.

Release files for ohlcvault 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ohlcvault 0.2.0
File Size Uploaded
ohlcvault-0.2.0.tar.gz 56.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ohlcvault 0.2.0
File Interpreter ABI Platform
ohlcvault-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 94.2 kB

Release files / ohlcvault-0.2.0.tar.gz

Download URL ohlcvault-0.2.0.tar.gz
Size 56.8 kB
Tags Source
SHA-256 checksum
How to use checksums
ae1d0c24324884227fd9d8a7278bfe5efa09e83df5dcc6ce3c7eeaf32996b439
BLAKE2b-256 checksum
How to use checksums
ae04d1983e4165c8ad37693621eb18bce17f449bc3cc40f04b07a53edcea0540
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release files / ohlcvault-0.2.0-py3-none-any.whl

Download URL ohlcvault-0.2.0-py3-none-any.whl
Size 37.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
968ed40935ac3bc45d76dbe407146c47467d982ef36b354ee53a38e7769e0c8b
BLAKE2b-256 checksum
How to use checksums
4f5c874477e30c0ae91590190cc23b2660825ce81d44958bea1cb82744ec836c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page