Angolin Docs
Download / Universe

Wide schema

Layout

One row per time slot, then one boolean column per asset, named by its ticker: true when the pair was listed during the slot. Wide covers a single quote currency, so the ticker alone names the pair.

close_tsBTCLUNC
2022-05-13T00:00:00Ztruetrue
2022-05-14T00:00:00Ztruetrue
2022-05-15T00:00:00Ztruefalse

Use it with the wide OHLCV schema of the same quote and interval: the column BTC masks BTC_close, BTC_open, and so on, row for row.

One boolean column per asset. The examples null out every close of a wide OHLCV file whose pair wasn't tradable at that time.

pandas

Align the universe on the OHLCV timestamps and rename its columns to match, then where() keeps the closes where the mask is true.

import pandas as pd

closes = pd.read_parquet("ohlcv_usdt_1h.parquet").set_index("close_ts").filter(regex="_close$")
listed = pd.read_parquet("universe_binance_usdt_1h.parquet").set_index("close_ts")

# Null out every close of a pair that wasn't tradable at that time
mask = listed.reindex(closes.index).rename(columns=lambda base: f"{base}_close")
masked = closes.where(mask[closes.columns].fillna(False).astype(bool))

Polars

Join on close_ts, then keep each close only when its asset's column is true.

import polars as pl

closes = pl.read_parquet("ohlcv_usdt_1h.parquet").select("close_ts", pl.col("^.*_close$"))
listed = pl.read_parquet("universe_binance_usdt_1h.parquet")

# Null out every close of a pair that wasn't tradable at that time
bases = [c.removesuffix("_close") for c in closes.columns[1:]]
masked = closes.join(listed, on="close_ts", how="left", maintain_order="left").select(
    "close_ts",
    *[pl.when(pl.col(b)).then(pl.col(f"{b}_close")).alias(f"{b}_close") for b in bases],
)

Polars (lazy)

The same query on scanned files. collect_schema() gives the column names without reading any data.

import polars as pl

closes = pl.scan_parquet("ohlcv_usdt_1h.parquet").select("close_ts", pl.col("^.*_close$"))
listed = pl.scan_parquet("universe_binance_usdt_1h.parquet")

# Nothing is read yet: build the mask, then run it all on collect()
bases = [c.removesuffix("_close") for c in closes.collect_schema().names()[1:]]
masked = (
    closes.join(listed, on="close_ts", how="left", maintain_order="left")
    .select("close_ts", *[pl.when(pl.col(b)).then(pl.col(f"{b}_close")).alias(f"{b}_close") for b in bases])
    .collect()
)