Data schemas

Every storage format qrt reads or writes — the bundled parquet datasets, local .parquet/.csv files, DuckDB tables — carries one of two canonical in-memory schemas, so the rest of the library (q.feature, q.stats, q.plot, and eventually q.bt) can rely on a single layout per kind of data.

OHLCV bars

One row per bar, indexed by a DatetimeIndex named datetime, with lowercase columns open, high, low, close, volume. This is what q.data.datasets.load, q.data.sources.yfinance.read, and q.data.sources.binance.read all return. (q.data.sources.duckdb stores the long variant of the same layout — a leading symbol column and datetime as a regular column — so one table can hold many symbols.)

import qrt as q

aapl = q.data.datasets.load("aapl")
aapl.dtypes
open      float64
high      float64
low       float64
close     float64
volume      int64
dtype: object

Trade logs

One row per round-trip trade on a plain RangeIndex, with the reserved columns of q.stats.TRADE_COLUMNS, in order:

Column Meaning
symbol instrument traded
entry_time / exit_time bar timestamps of entry and exit execution
direction 1 = long, -1 = short
entry_reason / exit_reason free-form labels (e.g. "golden_cross", "trail_stop", "end_of_data")
entry_price / exit_price execution prices
return direction-adjusted decimal return of the round trip (a short falling 3% is +0.03)
mae / mfe maximum adverse / favorable excursion vs. the entry price while open, direction-adjusted
size / fees position size and costs — NaN placeholders until a backtester fills them

Any additional columns are entry-time feature snapshots — the indicator values that triggered the entry (e.g. rsi2, ema_spread). Cumulative returns are deliberately not stored: derive them with (1 + trades["return"]).cumprod() - 1 so slicing a log can never leave a stale column behind. A trade still open at the end of data is closed on the last bar with exit_reason="end_of_data", so exit_time is never missing.

This is the shape the bundled q.data.datasets.TRADE_LOGS ship in, the shape q.stats.trade_stats/q.stats.trades_to_returns consume and q.plot.trades renders — and the shape a backtest or execution log should produce:

trades = q.data.datasets.load("spy_ema_cross")
trades.head(3)
symbol entry_time exit_time direction entry_reason exit_reason entry_price exit_price return mae mfe size fees ema_spread
0 SPY 2003-05-28 2008-01-16 1 golden_cross death_cross 62.694744 97.837783 0.560542 -0.008033 0.780131 NaN NaN 0.000944
1 SPY 2009-08-06 2010-09-01 1 golden_cross death_cross 74.521469 80.361416 0.078366 -0.027362 0.228019 NaN NaN 0.001799
2 SPY 2010-09-07 2011-08-22 1 golden_cross death_cross 83.102125 88.456017 0.064425 -0.007430 0.261593 NaN NaN 0.000335
Back to top