Question it answers
“Give me 1-hour OHLCV candles for pool/pair 0x…, open, high, low, close, volume, and trade count per hour.” Mirrors Moralis GET /pairs/{address}/ohlcv.
Candles are built by aggregating swaps, not reported by an exchange. This recipe is a downstream aggregation on top of the same trade ingest as Swaps by Pair: the sync lands every trade once (USD-enriched in-block), then a candle surface buckets those trades into fixed 1-hour candles per (pool_address, hour). “Price” per fill is the bought-leg (token1) in-block USD price already on the trade (price_usd); volume is sum(abs(notional_usd)) and trades is the fill count.
What you get
One candle row per(pool_address, bucket_start), where bucket_start is the start of a 1-hour window. Only priced trades (a same-block USD price update for the bought leg) contribute:
The candles sit on top of a trade fact table (
fact_swaps / swaps) carrying every fill, tx_hash, trader_address, token0/1_address, amount0/1, side, source_kind, protocol, price_usd, notional_usd, so you can drop to the underlying trades whenever you need detail behind a candle.
Source
The trade ingest is identical to Swaps by Pair: two projection branches expand the per-block swap arrays into one row per fill:tokenSwaps (pool fills) · aggregateTokenSwaps (aggregator routes)
Each fill is USD-enriched inline from the same block’s tokenPriceUpdates (reversed so the chronologically-last update wins on duplicate keys), no separate price join at read time. The candle surface then aggregates these priced fills by hour.
Destination
ClickHouse uses the collapsing log-table pattern (see the recipes overview) so chain reorganizations self-correct. Two candle surfaces ship on ClickHouse:
candles is a low-latency accelerator backed by an AggregatingMergeTree that is sign-aware for additive measures, and candles_exact reads fact_swaps FINAL for always-exact OHLC. On Postgres/MySQL the candles VIEW aggregates swaps at read time and is correct the moment a backfill lands.
Full schema
Below are the trade fact table and the candle surfaces this recipe produces. The candle columns are a fixed shape (open/high/low/close/volume/trades); the underlying trade table is the wider starting point, keep the columns you need (see Schema & flexibility). Raw uint256 amounts are stored as text on ClickHouse and NUMERIC(76,0) on Postgres (they exceed standard numeric precision); USD columns are wide decimals so a low-decimals token × price never overflows.
ClickHouse, fact_swaps + candles
ClickHouse, fact_swaps + candles
sign column on fact_swaps drives reorg collapsing. candles is the low-latency accelerator; candles_exact is always reorg-exact (see fidelity gaps). A single-node setup can use the non-replicated engines (CollapsingMergeTree(sign), AggregatingMergeTree) without the replication path.Postgres, swaps + candles
Postgres, swaps + candles
DECIMAL(38,18) for the USD columns and DECIMAL(76,0) for raw amounts, and ships the candles VIEW only. position is the block-level cursor used during backfill. To change the bucket interval, swap date_trunc('hour', …) (and the ClickHouse toStartOfHour / partition expression) for your target window.Example reads
Newest 24 hourly candles for one pool (ClickHouse, fast accelerator):FINAL before aggregating):
candles VIEW reads the same way, keyed on pool_address:
Modes
Shipped defaults: ClickHousehybrid (backfill → realtime), Postgres / MySQL historical (one-shot backfill). For live/reorg-safe ingestion, use ClickHouse, see the overview.
Realtime on Postgres / MySQL is not supported for this shape. The realtime reorg path needs a single-column unique on the block-level position, but trades are array-expanded (many per block), so these tables can only carry a composite unique. Run realtime/hybrid on ClickHouse, where the collapsing log table corrects reorgs per-block and the candle accelerator is sign-aware. The Postgres/MySQL configs are intended for
historical backfill, re-run the backfill (and REFRESH the candle matview) to pick up corrections.Multichain
The recipe is chain-parametrized, point it at any supported EVM chain or Solana, and the candle math is chain-agnostic. On Solana, the samelogIndex can repeat across events in one instruction, so the pool-fill event identity is widened (with token0Address, token1Address, token0Amount) to keep trade rows distinct; the candle surface it produces is identical in shape.
Fidelity gaps
- Fixed 1-hour interval (v1). Moralis’
/pairs/{address}/ohlcvtakes atimeframeparam (1m/5m/1h/1d/…); this recipe ships a single fixed 1-hour bucket. For other intervals, change the bucket expression (toStartOfHour/date_trunc('hour', …)/ the MySQL modulus) to your target window. - Trade-reconstructed, not exchange-reported. Candles are built from on-chain swap fills, so they may differ slightly from a DEX’s own reported OHLC (which can apply off-chain smoothing or a different price reference). No off-chain smoothing is applied.
- Price = bought-leg in-block USD; no carry-forward. A candle only includes trades whose bought-leg token had a same-block
tokenPriceUpdate. Thin/illiquid pools with no in-block price update in an hour produce no candle for that hour (a gap), rather than a flat carry-forward candle. Dense pools (the majority of volume) are unaffected. - ClickHouse
candleshigh/low under reorg. The fast accelerator’shigh/loware monotonic state functions; a reorg−1row cannot retract an extreme, so if a reorg removes an hour’s all-time high/low,candlesmay stay slightly wide until the next canonical trade re-establishes the range. Readcandles_exactwhen post-reorg precision matters.volumeandtradesare sign-weighted and always exact on both surfaces. volumeis absolute traded USD.notional_usdcarries the signed token1 delta (V3/V4 report signed swap amounts, negative when the token leaves the pool), sovolumeusesabs(notional_usd)(×reorg-sign on ClickHouse), otherwise buy/sell legs cancel and volume goes negative. This is the canonical OHLCV “traded value” definition.- Missing-decimals volume outliers. A handful of exotic tokens arrive with
token1decimals absent/0 (coalesced to 0 in the shared swap transform); their raw amount isn’t decimal-scaled andnotional_usdis wildly inflated, producing an implausibly large candlevolume. OHLC prices are unaffected (they use per-unitprice_usd). Filtervolume < 1e12for a clean volume distribution; a production deployment should source token decimals from a metadata table rather than the in-event field.
Migrating from the REST API
This recipe replaces the pair-OHLCV REST endpoint, and it’s the closest to 1:1 in the swaps family: the shipped candle surface mirrors the endpoint at a 1-hour timeframe, and because the underlyingswaps table is trade-level, any other timeframe is a GROUP BY you own rather than a fixed parameter. Fidelity labels: exact = straight from the chain, calculated = aggregated from real DEX trades (very close, compare with a tolerance).
GET /pairs/:address/ohlcv
Candlestick data for a pool/pair. Replaced by this recipe’scandles surface for 1-hour candles, or a bucket query over swaps for any other timeframe.
The REST
timeframe parameter becomes the bucket expression. A 5-minute example over swaps (Postgres, use 60 / 3600 / 86400 for 1m / 1h / 1d):
- OHLC is trade-reconstructed, not exchange-reported, it tracks the old endpoint closely, but compare with a tolerance rather than expecting identical figures.
- The REST
currencyparameter has no direct analog: candles here are priced in the bought leg’s in-block USD price, so be explicit about which token the OHLC is quoted in. - Thinly-traded pairs produce sparse candles, gaps where no priced trade occurred, not flat carry-forward candles (see Fidelity gaps).
volumemust besum(abs(notional_usd)), the notional is signed on V3/V4 pools, so a plainsumlets buy and sell legs cancel.- For live charts, run the recipe
realtimeon ClickHouse and readcandles(orcandles_exactafter a reorg); for historical candles back to a date, backfillhistorical/hybridfrom that date, no need to index from genesis. See History & backfill.
Related
Swaps by Pair
The per-trade ingest these candles aggregate, every fill on a pair.
Trading & Charting
The use case OHLCV candles power.

