Every DEX trade settles as a Swap event, and the executed price is in its amounts, fee and slippage included. SQD serves those events decoded, from genesis to chain head, across 225+ networks, with the same query shape for a backtest and a live signal. Confirmed blocks only: order signing and submission stay on your bot's own RPC path.
The price a backtest should fill at is the price real trades actually got, and that price is in each fill's amounts. Rebuilt off an RPC node, replaying a pool's history means paginating eth_getLogs against per-call block-range and result-count caps, standing up archive access, and reading prices back off resampled candles that average the real fills away. The fills are onchain already; the cost is in how you read them back.
Every trade a Uniswap V3 pool executes settles as a Swap event, and the price the trade actually got is in its amounts, fee and slippage included. Take the USDC/WETH 0.05% pool (0x88e6…5640): its PoolCreated log at block 12,376,729 fixes token0 as USDC and token1 as WETH, so every fill since decodes the same way.
The first fill in block 21,000,006 put 65,003.274773 USDC into the pool and took 24.641506187 WETH out. Divide the two and the executed price of that fill is 2,637.96 USDC per WETH:
The query is one filter on the pool address and the Swap topic0, pinned here to a single block so you can run it and get exactly this:
The block's line carries a second fill after this one (0xde87…e964 is the first). Two decode gotchas: amount0 and amount1 are signed int256 in two's complement, so the 0xff... word above is minus 24.64 WETH, and an unsigned parse reads it as a 78-digit number. And sqrtPriceX96 is the pool's marginal price after the fill (2,636.96 here), not the price the trade got; a backtest that fills orders at it misprices every trade.
The second fill in the same block went the other way: 1.6335 WETH sold into the pool for 4,305.286906 USDC, an executed price of 2,635.62. Two fills, one block, a 2.34 USDC gap between the buy and the sell that a single candle collapses to one number.
Widen the same query to 7,200 blocks (about 24 hours from 2024-10-19 13:45 UTC) and it returns every fill the pool executed in that window. Only blocks holding matching fills come back, plus a few chunk-boundary headers, not one line per scanned block:
Against an RPC node the same backfill is a pagination problem: eth_getLogs is capped per call, by block range or by result count, and historical ranges need archive access. At the 50-block cap some public endpoints enforce, this one day is 144 calls; the pool's full history since 2021 is the same problem repeated at scale. The caps are documented, verbatim, in the eth_getLogs limits guide.
One gotcha: a range request can end before toBlock, at HTTP 200, when the stream reaches an internal chunk boundary. The first response for this window stopped at block 21,001,566; the client reads the last line's header number and re-issues from the next block. Five sequential requests covered the full 7,200-block range, so treat the resume loop as part of the client.
The same filter pointed at chain head streams new fills as blocks confirm, so a backtest and its live signal read identical data. There is no mempool or pending-transaction view; order signing and submission stay on the bot's own RPC path.
Live queries against real blockchain data. Pick a network, choose a query, hit run.
The public endpoint is free for development. See plans for dedicated portals and higher limits.
A Swap event carries the signed amounts of a real fill, so price comes from amount0 and amount1, fee and slippage included, not from a candle or an oracle quote. The same decode reads Uniswap V3 on Ethereum and any fork of it on any EVM chain; Solana DEX fills come from the instruction stream instead.
Every historical swap of a pool streams from one filter on its address and topic0, from the pool's creation block to head. No per-call block-range caps or result-count limits to code around, and no archive node to run; wide ranges stream in resumable chunks.
The same filter pointed at chain head streams new fills as blocks confirm, over plain HTTP. A backtest and its production signal read identical data, which removes one class of train-live skew. There is no mempool or pending-transaction data; the Portal serves confirmed blocks only.
Decoded ERC-20 Transfer events filtered by your trading addresses are the position ledger: entries and exits as they confirm. Balances are aggregations you fold from that history downstream; the Portal does not serve reconstructed balance snapshots.
Every fill is a public Swap event. What a provider saves a bot is the reading: backfill depth, an honest executed price, and one data path shared by the backtest and the live signal.
See this on your own contracts
Tell us the chains and contracts you care about, and we will show you the query that returns them.
A worked Pipes SDK example: stream the full Hyperliquid fills tape into your own ClickHouse, rank traders by realized PnL, and split volume into crypto versus TradFi markets.
The Hyperliquid fills dataset: what a fill record holds, maker and taker fees, realized PnL from closing trades, and how to separate crypto markets from the TradFi and HIP-3 listings on the same venue.
Swaps, liquidations, and the internal calls a log-only feed misses.
Decoded events and traces streamed into the warehouse you already run.
Pre-decoded onchain data for agents, over MCP or plain HTTP.
Address-keyed history: transfers, native and internal value, NFT moves.
Traces and state diffs, the execution footprint logs alone never show.
Private Portal. Dedicated. Validated. Managed. Tell us what you're building, we'll show you what it looks like on SQD.