Historical queries
HistoricalService returns archived trades and candles for any pair within the last 30 days. Authenticated; pulls from the deployment's historical archive (when configured).
What's available
| RPC | Returns | Cap |
|---|---|---|
GetTrades | Trades within [from_sec, to_sec] for a pair. | 1,000 rows per call |
GetCandles | OHLCV 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
from_secandto_secare both inclusive.- Units are unix seconds. Values ≥ 10¹¹ auto-convert from milliseconds with a
Warningheader. - Historical trades come back oldest-first. (Live trades from
MarketDataServicecome back newest-first — different paths, different conventions.) - Maximum window is 30 days per call. Larger spans need to be paginated.
Backfill recipe
To paginate a long span (e.g. a year of 1-minute candles), chunk by the row cap:
use superis::proto::{Interval, GetCandlesRequest, Pair, SpotId};
let interval_sec = 60u64;
let max_rows = 10_000u64;
let window_sec = max_rows * interval_sec;
let mut from = start_sec;
let mut all = Vec::new();
while from < end_sec {
let to = (from + window_sec).min(end_sec);
let res = historical
.get_candles(GetCandlesRequest {
pair: Some(Pair { base: Some(SpotId { id: 1 }), quote: Some(SpotId { id: 0 }) }),
interval: Interval::Interval5m.into(),
from_sec: Some(from),
to_sec: Some(to),
})
.await?
.into_inner();
all.extend(res.candles);
from = to + 1;
}const intervalSec = uint64(60)
const maxRows = uint64(10_000)
windowSec := maxRows * intervalSec
from := startSec
var all []*pb.Candle
for from < endSec {
to := from + windowSec
if to > endSec { to = endSec }
res, err := historical.GetCandles(ctx, &pb.GetCandlesRequest{
Pair: &pb.Pair{Base: &pb.SpotId{Id: 1}, Quote: &pb.SpotId{Id: 0}},
Interval: pb.Interval_INTERVAL_5M,
FromSec: &from,
ToSec: &to,
})
if err != nil { log.Fatal(err) }
all = append(all, res.Candles...)
from = to + 1
}const intervalSec = 60n;
const maxRows = 10_000n;
const windowSec = maxRows * intervalSec;
let from = startSec;
const all: Candle[] = [];
while (from < endSec) {
const to = from + windowSec > endSec ? endSec : from + windowSec;
const { candles } = await historical.getCandles({
pair: { base: { id: 1n }, quote: { id: 0n } },
interval: "INTERVAL_5M",
fromSec: from,
toSec: to,
});
all.push(...candles);
from = to + 1n;
}Stitching with live
For a chart that shows the last 24h:
GetCandleswithfrom_sec = now - 86_400,to_sec = nowto pull the historical body.- Subscribe to
MarketDataService.Subscribefor the pair to drive live updates fromnowforward, computing the last candle yourself from the fill stream — or justGetCandlesagain 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.