Skip to content

Historical queries

HistoricalService returns archived trades and candles for any pair within the deployment's retention window. It is public and runs on the public listener. Pulls from the deployment's historical archive when configured.

What's available

RPCReturnsCap
GetTradesTrades within [start, end) for a pair.1,000 rows per call
GetCandlesOHLCV candles at one of seven intervals.10,000 rows per call

If the deployment doesn't have the historical archive enabled, every RPC returns FAILED_PRECONDITION. Read it once at boot to feature-gate the UI.

Range semantics

  • start and end are Timestamp { micros } values, i.e. Unix microseconds.
  • start is inclusive.
  • end is exclusive. Unset or micros = 0 means "now".
  • GetTrades returns rows newest-first.
  • GetCandles returns rows oldest-first.
  • Maximum window is 30 days per call. Larger spans need to be paginated.
  • Historical price, size, and OHLCV values are Decimal strings — the same wire format as the live book and fill streams. Parse with a fixed-precision type (rust_decimal, bignumber.js).

Backfill recipe

To paginate a long span (e.g. a year of 1-minute candles), chunk by the row cap:

rust
use sweetspot_api_client::api::proto::{CandleInterval, GetCandlesRequest, Pair, SpotId, Timestamp};

let mut historical = client.historical();

let interval_us = 5 * 60 * 1_000_000u64;
let max_rows = 10_000u64;
let window_us = max_rows * interval_us;

let mut start = start_us;
let mut all = Vec::new();
while start < end_us {
    let end = (start + window_us).min(end_us);
    let res = historical
        .get_candles(GetCandlesRequest {
            pair: Some(Pair { base: Some(SpotId { id: 1 }), quote: Some(SpotId { id: 0 }) }),
            interval: CandleInterval::CandleInterval5m.into(),
            start: Some(Timestamp { micros: start }),
            end: Some(Timestamp { micros: end }),
        })
        .await?
        .into_inner();
    all.extend(res.candles);
    start = end;
}
ts
import {
  GrpcWebTransport,
  HistoricalService,
  createServiceClient,
} from "@superis-labs/sweetspot-client";
import {
  CandleInterval,
  type Candle,
} from "@superis-labs/sweetspot-client/gen/sweetspot/api/v1/historical/messages_pb.js";

const historical = createServiceClient(
  HistoricalService,
  new GrpcWebTransport({
    baseUrl: "https://api.superis.exchange",
  }),
);

const intervalUs = 5n * 60n * 1_000_000n;
const maxRows = 10_000n;
const windowUs = maxRows * intervalUs;

let start = startUs;
const all: Candle[] = [];
while (start < endUs) {
  const end = start + windowUs > endUs ? endUs : start + windowUs;
  const { candles } = await historical.getCandles({
    pair: { base: { id: 1n }, quote: { id: 0n } },
    interval: CandleInterval.CANDLE_INTERVAL_5M,
    start: { micros: start },
    end: { micros: end },
  });
  all.push(...candles);
  start = end;
}
python
from flint import Client, Endpoints
from flint.gen.sweetspot.api.v1 import common_pb2
from flint.gen.sweetspot.api.v1.historical import messages_pb2 as hist

client = Client(
    Endpoints(
        public_url="https://api.superis.exchange",
    )
)

request = hist.GetCandlesRequest(
    pair=common_pb2.Pair(
        base=common_pb2.SpotId(id=1),
        quote=common_pb2.SpotId(id=0),
    ),
    interval=hist.CANDLE_INTERVAL_5M,
    start=common_pb2.Timestamp(micros=start_us),
    end=common_pb2.Timestamp(micros=end_us),
)
response = await client.historical_candles(request)

Stitching with live

For a chart that shows the last 24h:

  1. GetCandles with start.micros = now_us - 86_400_000_000 and end.micros = now_us to pull the historical body.
  2. Subscribe to MarketDataService.Subscribe for the pair to drive live updates from now forward, computing the last candle yourself from the fill stream — or just GetCandles again every interval for less aggressive UIs.

The historical and live paths align on the interval boundary.

When to prefer live

HistoricalService.GetTrades over a recent window is more expensive than just streaming MarketDataService.SubscribeFills and ringing your own buffer. Use the historical path for backfill or for ranges older than your in-memory retention; use the live path for anything inside the active session.

Build on Solana