Chart windows
How to load price history (OHLCV candles) for a chart and keep it updated without downloading the full history on every poll.
OHLCV reads use the same window options on:
easy.read.getChart— you pass a pair label (e.g.42-USDT)telemetry.getCandles— you pass a venue ID when you already know the pool
Both return a CandleSeries.
What is a candle?
Each candle is one time bucket on your chart (e.g. one hour if interval: '1h'). It has:
time— when that bucket starts (Unix timestamp in seconds)open,high,low,close— prices for that bucket
Closed candles are finished buckets — they will not change. The current open bucket (the bar still forming) may be returned separately as partialCandle — see Response fields below.
Four ways to fetch data
You will use different calls depending on what the screen needs:
| Pattern | When you need it | What to call |
|---|---|---|
| Bootstrap | User opens the chart, or changes pair / interval | getChart({ pair, interval, limit: 300 }) |
| Tail refresh | A closed candle may have appeared since last load | getChart({ pair, interval, after: … }) |
| Partial bar | Chart is open; update the live rightmost bar often | getChart({ pair, interval, scope: 'partialOnly' }) |
| Tick | Header / ticker — latest price only, not OHLC | getTokenPrice({ instrument }) |
Bootstrap = first load of history. Tail refresh = append newly closed candles. Partial bar = cheap poll for the open bucket only. Tick = spot price without candle shape.
Do not repeat bootstrap on a timer. Use partial bar for frequent live updates and tail refresh when you need closed candles (often less frequent than partial bar, or right after a bucket closes).
Window parameters (CandleQuery)
Optional fields on getChart / getCandles: limit, after, before, scope.
If an interval is not available for the request or config, the call fails with CAPABILITY_NOT_AVAILABLE — not an empty series.
If you omit all of them, Nutrimatic returns a default slice of recent history — the same as omitting only limit (default 300 closed candles). That is scope: 'default'.
You can combine before + limit when loading older history. Do not pass after and before together unless you need a narrow slice between two times.
Do not combine scope: 'partialOnly' with limit, before, or bootstrap — it is only for updating the open bar when history is already loaded.
limit
What it means: “Return at most this many closed candles.” Nutrimatic picks the most recent candles up to that count.
Optional — default 300 if omitted. Responses cap at 1000; if you ask for more, you receive at most the cap.
When to use it
| Situation | Should you pass limit? | Example |
|---|---|---|
| First chart load (bootstrap) | Yes | limit: 300 — you decide how much history to load initially |
Tail refresh (you already passed after) | Usually no | after already limits the response to new candles only |
| User scrolls left for older history | Yes, together with before | before: 1719705600, limit: 100 |
| Latest price in a header (tick) | Not used | Call getTokenPrice instead |
after
What it means: “I already have candles up to this time — only send me newer ones.”
after is a Unix timestamp in seconds (same unit as each candle’s time). Nutrimatic returns closed candles whose time is strictly greater than after.
When to use it: after bootstrap, on each chart refresh (every N seconds, or when the user returns to the tab).
What value to pass: the time of the last closed candle you already have in memory.
import { lastClosedCandleTime } from '@nutrimatic/sdk'
const after = lastClosedCandleTime(series.candles)
// Pass `after` on the next getChart call
What you do with the response: merge it into what you already have — do not throw away your previous candles. Use mergeCandleSeries (see Recommended refresh loop).
Plain-language example: your last closed hourly candle starts at 1719792000. You call after: 1719792000. You get only buckets that started after that moment — not the whole chart again.
before
What it means: “I already have recent candles — send me older ones.”
before is a Unix timestamp in seconds. Nutrimatic returns closed candles whose time is strictly less than before.
When to use it: the user scrolls or zooms left on the chart and you need more history than bootstrap loaded.
What value to pass: the time of the oldest candle you already display — that becomes before, so the API returns buckets before that point.
import { firstClosedCandleTime } from '@nutrimatic/sdk'
const before = firstClosedCandleTime(series.candles)
// Pass `before` together with `limit` on the next getChart call
Always pair with limit: e.g. before: oldestTime, limit: 100 loads up to 100 older candles.
What you do with the response: prepend merged candles to your existing series (oldest new data first).
Plain-language example: your oldest displayed candle starts at 1719705600. You call before: 1719705600, limit: 100 to fetch up to 100 candles that ended before that time.
ledgers (getChart only)
What it means: “Resolve this pair on these blockchains only.”
You only need this when your SDK config lists more than one ledger and a pair label could exist on multiple chains.
Pass ledger IDs or aliases from your config, e.g. ledgers: ['infinite-mainnet'].
When to omit it: single-ledger apps — Nutrimatic uses the only configured ledger.
This parameter does not change how many candles you get; it only affects which chain the pair is resolved on.
scope
What it means: whether Nutrimatic should work out closed candles for this request, or only return the open bucket.
type CandleReadScope = 'default' | 'partialOnly'| Value | candles in response | partialCandle | When to use |
|---|---|---|---|
'default' or omit | New or matching closed candles (per limit / after / before) | Included when available | Bootstrap, tail refresh, page backward |
'partialOnly' | Always [] (empty) | Current open bar only | Frequent poll to animate the rightmost bar |
When to use 'partialOnly': you already ran bootstrap, the chart is on screen, and you want to refresh the live bar every few seconds without reloading the full candle history.
const live = await sdk.easy.read.getChart({
pair: '42-USDT',
interval: '1h',
scope: 'partialOnly',
})
series = {
...series,
partialCandle: live.partialCandle,
asOf: live.asOf,
}
// Or: series = mergeCandleSeries(series, live)
// partialOnly returns no closed candles — the helper keeps your history and only updates partialCandle and asOf
Both forms do the same thing for partialOnly: keep your existing candles, replace partialCandle and asOf from the new response. Use whichever reads clearer in your app — mergeCandleSeries is the same helper as tail refresh (Step 2b).
Optional after with 'partialOnly': not required (pair + interval identify the open bucket). You may pass after: lastClosedCandleTime(series.candles) to anchor the open bucket to your current chart window.
Do not use 'partialOnly' with: limit, before, or the first chart load. For header price without OHLC, use getTokenPrice instead.
Response fields
Every successful chart call returns a CandleSeries. Important fields:
| Field | What it is | What you do with it |
|---|---|---|
candles | Array of closed candles, oldest → newest | Draw the main chart lines / bars |
partialCandle | The current open bucket (still updating) | Draw the rightmost “live” bar; update on tail refresh |
asOf | When Nutrimatic built this snapshot (Unix seconds) | Optional “data as of …” label in UI |
priceAxisLabel | Human-readable axis label (e.g. 42/USDT) | Chart axis / legend |
interval | Bucket size you requested | Must stay the same when merging tail responses |
partialCandle is not duplicated inside candles. On tail refresh, update your live bar from the latest partialCandle in the response.
Recommended refresh loop
Copy this shape for a minimal live chart:
import {
createNutrimaticSdk,
lastClosedCandleTime,
mergeCandleSeries,
} from '@nutrimatic/sdk'
const sdk = createNutrimaticSdk({ /* … */ })
// Step 1 — Bootstrap (once when the chart opens, or pair/interval changes)
let series = await sdk.easy.read.getChart({
pair: '42-USDT',
interval: '1h',
limit: 300,
})
// Draw series.candles and series.partialCandle
// Step 2a — Partial bar (often — e.g. every 5s while the chart is visible)
async function refreshLiveBar() {
const live = await sdk.easy.read.getChart({
pair: '42-USDT',
interval: '1h',
scope: 'partialOnly',
})
series = { ...series, partialCandle: live.partialCandle, asOf: live.asOf }
// Or: series = mergeCandleSeries(series, live)
// partialOnly returns no closed candles — the helper keeps your history and only updates partialCandle and asOf
}
// Step 2b — Tail refresh (when a bucket may have closed — e.g. every 30s or on interval boundary)
async function refreshClosedCandles() {
const after = lastClosedCandleTime(series.candles)
if (after === undefined) return
const tail = await sdk.easy.read.getChart({
pair: '42-USDT',
interval: '1h',
after,
})
series = mergeCandleSeries(series, tail)
}
// Step 3 — Tick (optional — price in header, no OHLC bar)
const tick = await sdk.easy.read.getTokenPrice({ instrument: '42' })For Step 2a, the spread assignment and the commented mergeCandleSeries line are equivalent when scope: 'partialOnly' — the response has no closed candles, so the helper only updates partialCandle and asOf. Step 2b always uses mergeCandleSeries because new closed candles may arrive.
For a live chart in product UI, prefer sdk.live.watchPair with chart: { interval } — Nutrimatic owns freshness. The window helpers on this page are for bootstrap, load-older, and advanced composition. You still choose errors, loading states, and chart library.
Kernel helpers
Small pure functions (no network, no cache) from @nutrimatic/kernel, also exported by @nutrimatic/sdk:
import {
mergeCandles,
mergeCandleSeries,
lastClosedCandleTime,
firstClosedCandleTime,
} from '@nutrimatic/sdk'Candle[] vs CandleSeries — which merge to use?
Every getChart / getCandles response is a CandleSeries object — the full API payload:
CandleSeries ← what getChart / getCandles return
├── venueId
├── interval
├── priceAxisLabel
├── asOf?
├── partialCandle? ← current open bar (not inside candles)
└── candles: Candle[] ← only the closed bars
├── { time, open, high, low, close }
├── { time, open, high, low, close }
└── …A Candle[] is just the inner list (series.candles). Nothing else from the response is included.
| You have… | Use |
|---|---|
Two full API responses (let series + const tail = await getChart(…)) | mergeCandleSeries(series, tail) — usual case for tail refresh |
Only two candle arrays (e.g. you store candles separately in app state) | mergeCandles(base, incoming) — lower level; you update metadata yourself |
Default rule: if both sides came from getChart or getCandles, keep the whole CandleSeries in a variable and call mergeCandleSeries. It merges candles and carries forward asOf and partialCandle from the newer response.
When mergeCandles makes sense: you scrolled backward and only merged arrays before building your own object, or your UI state holds candles apart from partialCandle. Example — prepend older history:
const before = firstClosedCandleTime(series.candles)
if (before === undefined) return
const older = await sdk.easy.read.getChart({
pair: '42-USDT',
interval: '1h',
before,
limit: 100,
})
series = {
...series,
candles: mergeCandles(older.candles, series.candles),
}Here mergeCandles prepends: older response first, your existing candles second (result is sorted by time).
Helper reference
| Function | Input | Output | Typical use |
|---|---|---|---|
lastClosedCandleTime(candles) | Candle[] | number | undefined | Value for after (tail refresh) — newest closed candle |
firstClosedCandleTime(candles) | Candle[] | number | undefined | Value for before (page backward) — oldest closed candle |
mergeCandles(base, incoming) | two Candle[] | Candle[] | Merge arrays only; you keep partialCandle / asOf in your own code |
mergeCandleSeries(base, tail) | two CandleSeries | CandleSeries | Tail refresh after getChart — start here |
mergeCandleSeries throws if venueId or interval differ — that means pair or bucket size changed; run bootstrap again instead of merging.
Nutrimatic returns a closed candle only when that bucket had market activity. Plot bars by each candle’s time so quiet gaps stay honest — the series is already correct as returned.
Related
- Activity feeds — discrete event feeds (separate domain)
- Read operations — getChart ·
getCandles - Types — Candle ·
CandleQuery·CandleSeries - Integration guides — Scope