← TradeApe

QuestDB as the market-data backbone

The queries do the work. The model explains what they found. A walkthrough of the time-series patterns behind every TradeApe tool call.

The demo instance that TradeApe ships with by default holds tens of millions of trade rows across several crypto pairs. Ask the agent about BTC's range over the last week and it's scanning across hundreds of thousands of rows to produce a handful of OHLCV candles.

That data cannot go in a context window. A single trade row — timestamp, symbol, price, amount — expands to 25–30 tokens when passed as plain text. A week of 1-minute candles for one symbol is around 10,000 rows.

A model would exhaust a 200K token context on the raw data before writing a single word of analysis. Even if it fit, you'd be asking a probabilistic function to do exact arithmetic over structured data — the wrong tool for that job.

QuestDB's own framing for this is clear: a model is a function, not a repository. You don't put data in the model. You query the database, give the model the result, and let it do what it's actually good at.

SAMPLE BY: the agent's primary tool

The most-used QuestDB primitive in TradeApe is SAMPLE BY, which aggregates rows into time buckets. The candle query that feeds almost every chart and tool call looks like this:

SELECT timestamp AS ts,
       first(price) AS open, max(price) AS high,
       min(price) AS low,   last(price) AS close,
       sum(amount)  AS volume
FROM trades
WHERE timestamp > dateadd('h', -4, now())
  AND symbol = 'ETH-USDT'
SAMPLE BY 5m
LIMIT 500

SAMPLE BY 5m turns the raw trade feed into 5-minute OHLCV candles without a pre-aggregation step. The agent asks for a window and a bucket size; QuestDB computes the candles on the fly. Switching to hourly is SAMPLE BY 1h. Going back further is changing the dateadd offset.

This matters in an agent loop because the agent doesn't know in advance which timeframes a question will require. SAMPLE BY handles that dynamically without a separate query path per granularity — the tool passes the window and sample interval, and gets back a consistently shaped result.

Current state: LATEST ON

For the price snapshot injected into every agent context, TradeApe uses LATEST ON:

SELECT symbol, price FROM trades
WHERE timestamp > dateadd('d', -1, now())
LATEST ON timestamp PARTITION BY symbol

LATEST ON timestamp PARTITION BY symbol returns the most recent trade for each symbol in a single pass. No subquery, no GROUP BY, no ORDER BY + LIMIT 1 per symbol. QuestDB's time-ordered storage makes this a first-class operation.

The agent gets a price snapshot — fetched once per session refresh and age-stamped in the context — so it can reference current prices without issuing a live query on every turn.

VWAP from first principles

Reference VWAP requires window functions over the aggregated candle series. TradeApe builds candles in a CTE, then computes the running cumulative:

WITH ohlc AS (
  SELECT timestamp AS ts,
    first(price) AS open, max(price) AS high,
    min(price) AS low,   last(price) AS close,
    sum(amount) AS volume
  FROM trades
  WHERE timestamp > dateadd('d', -7, now())
    AND symbol = 'BTC-USDT'
  SAMPLE BY 5m
)
SELECT
  sum((high + low + close) / 3 * volume) OVER (ORDER BY ts CUMULATIVE)
  / sum(volume) OVER (ORDER BY ts CUMULATIVE) AS vwap
FROM ohlc
LIMIT -1

LIMIT -1 is QuestDB shorthand for the last row — the period's current VWAP rather than the full cumulative series.

This is one of the places where the prompt's provenance constraint connects directly to the query. The agent is instructed not to call the result an institutional or session VWAP unless provenance includes a named venue and an explicit session anchor.

The query computes it correctly for the feed and window. Whether that window maps to an exchange session is a metadata question the calculation alone can't answer.

Provenance injected at query time

Every tool call in TradeApe includes a provenance line built from the feed metadata and passed to the agent alongside the query results:

provider/feed: QuestDB demo
schema: trades(timestamp, symbol, price, amount)
symbol mapping: BTC-USDT -> BTC-USDT
exchange: unknown exchange
volume: supported
candle state: final candles (inferred)
  • Exchange: unknown exchange — volume figures are feed-local, not consolidated market depth.
  • Volume: supported — the feed has amount data; per-venue attribution is a separate question.
  • Candle state: final candles (inferred) — freshness was computed from the timestamp offset, not reported by the provider. Relevant when the latest candle sits close to the current time boundary.

These aren't edge cases to paper over with a static disclaimer. They surface on almost every volume and VWAP question. Building them into the provenance object means each answer reflects the actual data state at query time, not a general caveat written once into the system prompt.

Demo vs. self-hosted

The default path uses QuestDB's public demo at demo.questdb.io. It's enough to explore the full tool surface: real OHLCV data, live candles, volume nodes, multi-symbol scans. The provenance badge in the app reflects this clearly — QuestDB demo, exchange unknown.

Pointing at a self-hosted instance is one environment variable:

VITE_QUESTDB_URL=http://localhost:9000

With local QuestDB running your own trade feed, the query surface is identical. SAMPLE BY, LATEST ON, ASOF JOIN, the window functions — all the same. The agent tools don't change. The provenance object updates to reflect whatever the new feed supports: named exchange, session anchor, trade-level attribution.

That's the boundary layer in practice. SQL is the contract between the data and the agent. Everything upstream of it — the feed, the venue, the schema — is configuration. The analysis layer stays the same.

Why standard SQL matters here

The agent in TradeApe doesn't write its own SQL. The tools generate the queries, execute them against QuestDB's REST API, and return structured results. But the reason standard SQL works as the interface is the same reason it works for human analysts: it's a stable, inspectable contract. You can read the query, run it yourself, and verify the result independently of the agent that generated it.

QuestDB extends SQL with time-series primitives rather than replacing it with a proprietary query language. That means the queries TradeApe runs are readable by anyone who knows SQL — including the agent, the developer debugging a tool call, and eventually the user wondering where a number came from.

For agentic analysis, that auditability is part of the infrastructure. The database isn't just fast. It's the place where the claim becomes traceable.

Try TradeApe locally. Open source, BYOK, QuestDB-first.
Run locally →