Angolin Docs
Download / OHLCV

Long schema

Layout

One row per asset and timestamp. The assets are stacked in a base column, and each field (open, close, base_volume…) is a single column shared by all assets. There is no index: close_ts is a regular column.

close_tsbaseopenhighlowclosebase_volumequote_volume
2026-10-01T01:00:00ZBTC63412.563610.063380.263530.1412.8326202856.78
2026-10-01T02:00:00ZBTC63530.163575.463340.863388.7389.1224693321.73
2026-10-01T03:00:00ZBTC63388.763655.963371.063601.4455.6728932789.43
2026-10-01T01:00:00ZETH2471.22480.42466.92476.856105.415104912.23
2026-10-01T02:00:00ZETH2476.852479.32461.52468.15872.914520598.43
2026-10-01T03:00:00ZETH2468.12486.02465.22481.956390.215815904.75

Use it when you filter or group by asset, or when you work with many assets. Adding an asset adds rows, not columns. This is the long, tidy format that most analysis tools expect.

A download by universe has the same columns, sorted by close_ts then base: a pair simply has no rows in the slots where it wasn't listed.

Plain columns, no index: close_ts and base come back as regular columns in pandas and in Polars.

pandas

Filter on base like any other column. The filters argument of read_parquet is applied while reading, so only the matching rows are loaded.

import pandas as pd

df = pd.read_parquet("ohlcv_usdt_1h.parquet")
btc = df[df["base"] == "BTC"]
closes = df.pivot(index="close_ts", columns="base", values="close")

# Read only the rows and columns you need
btc_close = pd.read_parquet(
    "ohlcv_usdt_1h.parquet",
    columns=["close_ts", "close"],
    filters=[("base", "==", "BTC")],
)

Polars

Filter on base, or pivot to get one column per asset.

import polars as pl

df = pl.read_parquet("ohlcv_usdt_1h.parquet")
btc = df.filter(pl.col("base") == "BTC")
closes = df.pivot(on="base", index="close_ts", values="close")

Polars (lazy)

scan_parquet reads nothing yet: it only builds a query. Chain your operations on it, then call collect() last to run the whole query and load the result. The filter and the column selection are pushed down to the file, so only the matching rows and columns are read.

import polars as pl

lf = pl.scan_parquet("ohlcv_usdt_1h.parquet")

# Nothing is read yet: do whatever you want on lf
# (filter, select, group_by, with_columns, ...)

df = lf.collect()