Database Design for Market Data
How to design database tables for bars, ticks, symbols, orders and fills. Learn keys, indexes, partitioning, data types and how to avoid common design mistakes.
Market data grows fast. One liquid crypto pair can produce millions of trades a day; a few years of minute bars across thousands of stocks reaches billions of rows. A well designed database keeps queries fast, prevents duplicate or contradictory records and preserves the history needed for honest backtests. A poorly designed one becomes slow, hard to trust and painful to fix later. The good news is that a few design principles cover most trading use cases.
Core tables#
| Table | Purpose | Key columns |
|---|---|---|
| instruments | One row per tradable instrument | instrument_id, symbol, exchange, asset class, tick size, currency, listed and delisted dates |
| bars | OHLCV candles | instrument_id, interval, ts, open, high, low, close, volume |
| trades | Raw executions from the feed | instrument_id, ts, price, size, trade_id |
| corporate_actions | Splits and dividends | instrument_id, ex_date, type, ratio or amount |
| orders | Your orders | order_id, client_order_id, instrument_id, side, qty, type, price, status, timestamps |
| fills | Your executions | fill_id, order_id, qty, price, fee, ts |
Use an instrument ID, not just the symbol#
Ticker symbols change and get reused. A company can change its ticker, and a delisted ticker can later be assigned to a different company. Storing a stable internal instrument_id, with a separate history of which symbol applied when, prevents mixing two different companies' prices. See Survivorship and Selection Bias and Point-in-Time and Survivorship-Free Data.
Primary keys and uniqueness#
| Table | Natural unique key |
|---|---|
| bars | (instrument_id, interval, ts) |
| trades | (instrument_id, trade_id) or (instrument_id, ts, sequence) |
| fills | fill_id from the broker |
Unique constraints stop duplicates when a feed resends data or a loader runs twice. Combined with "insert, or ignore if it already exists" logic, loads become safe to repeat. See Data Pipelines and ETL.
Data types#
| Field | Recommended type | Why |
|---|---|---|
| Timestamps | Timestamp with time zone, stored in UTC | Avoids daylight saving confusion. See Timestamps, Time Zones and Daylight Saving |
| Prices in research tables | Double precision | Fast and adequate for analysis |
| Prices and money in accounting tables | Fixed precision decimal | Exact sums with no floating point drift |
| Volume | Big integer or decimal | Crypto quantities can be fractional |
Indexes and partitioning#
- Index (instrument_id, ts) on bars and trades, since nearly every query filters by both.
- Partition by time, for example one partition per month, so queries for recent data scan less and old data can be archived easily.
- Avoid too many indexes on write heavy tables; each one slows inserts.
Raw versus adjusted prices#
Store raw, unadjusted prices plus a corporate actions table, then compute adjusted prices when needed. If you only store adjusted prices, every new split or dividend silently rewrites history, and you can no longer see what prices actually traded. See Splits and Dividends in Price Data and Corporate Actions, Delistings and Rolls in Backtests.
Orders and fills: keep state history#
Rather than overwriting an order's status, record each status change with a timestamp: new, acknowledged, partially filled, filled, cancelled. This history forms part of your audit trail and makes debugging possible. See Logging, Audit Trails and Incident Response.
Common mistakes#
- Symbol as the only key, mixing different companies over time.
- Local time timestamps that break around daylight saving changes.
- No unique constraints, allowing duplicate bars.
- Storing only adjusted prices.
- One giant unpartitioned table that becomes slow to query and maintain.
Frequently asked questions#
How should I store stock price data in a database?#
Use an instruments table with stable IDs, a bars table keyed by instrument, interval and UTC timestamp, raw prices plus a corporate actions table, and indexes on instrument and time.
Should I store prices as floats or decimals?#
Floats are fine for research; use exact decimals for accounting values such as fills, fees and balances.
When should I partition a table?#
When it grows into hundreds of millions of rows or when you regularly query recent periods and archive old ones.
Next, learn TradingView's scripting language in Pine Script Basics.
3 quick questions on this lesson. Get them all right to finish it.
Turn on JavaScript to take the quiz.
Mentioned in
- Building Trading BotsProgramming and Data
- Data Pipelines and ETLProgramming and Data
- Splits and Dividends in Price DataProgramming and Data