Lumen ChartsLumen Charts
Connect data

Historical bars and countBack

Return historical bars from getBars: time ranges, countBack, ordering and when to report no data.

Download Markdown

getBars(symbolInfo, resolution, period, onHistory, onError) returns OHLCV bars in time order. period.from and period.to are in Unix seconds, but each bar's time is in epoch milliseconds.

Return the history the chart asks for

The time window is [from, to): it includes from and excludes to. countBack is the number of bars the chart actually needs, so treat it as the main target. If the window has too few bars, add earlier bars when you have them. If the window has more bars than countBack, it's fine to return them all; don't cut the response short.

Return bars with unique timestamps in ascending order and OHLC values that make sense. Daily, weekly and monthly bars are stamped at 00:00 UTC on the trading day. Don't use a local session-open time.

Match historical and realtime boundaries

History and realtime updates must use the same interval boundaries, symbol, and price basis. For UTC-aligned H1 bars, each candle starts at 11:00, 12:00, and so on. A tick at 11:06 updates the candle starting at 11:00; it doesn't start a new hour-long candle at 11:06.

The chart uses the timestamps your datafeed supplies. It doesn't automatically realign provider candles. Align the provider's aggregation request before it calculates OHLCV. Rounding a returned candle's timestamp cannot correct its contents: a candle covering 11:06–12:06 contains different trades or quotes from one covering 11:00–12:00.

Example: Polygon minute multipliers

Polygon's 60/minute aggregation can preserve a request's starting minute. A request starting at 11:06 can therefore produce H1 candles at 11:06, 12:06, and so on. For fixed UTC intervals, align the request start to the same boundary used by your live aggregator:

const intervalMs = 60 * 60_000; // H1; use 15 * 60_000 for M15.
const fromMs = period.from * 1000; // getBars uses seconds.
const alignedFromMs = Math.floor(fromMs / intervalMs) * intervalMs;
const inclusiveToMs = period.to * 1000 - 1; // Convert [from, to) for this provider.

// Use alignedFromMs and inclusiveToMs in the provider's aggregate request.
// Keep the returned OHLCV values and candle-start timestamps together.

This may request an earlier candle that overlaps period.from; account for that when selecting enough history to satisfy countBack. Providers can also expand request boundaries or limit the number of base aggregates they scan. Validate the returned timestamps against your exclusive to, page as needed, and return bars in ascending order. See Polygon/Massive's aggregation rules for its snapping and stretching behavior.

The tick-to-candle helper uses fixed UTC boundaries by default. For exchange-session or calendar-based intervals, use a shared market-calendar policy for historical aggregation and the helper's bucketStart callback instead of assuming fixed UTC blocks.

"No more data" versus "something went wrong"

onHistory([], { noData: true }); // no older data exists
onError('History is temporarily unavailable'); // request failed

A temporary error doesn't mean the history has run out. If you return only a few bars, the chart may ask for older data a limited number of times. When your provider pages its results, keep fetching pages until you have enough bars or you've truly reached the start of the data.

Handle late responses safely

Keep track of which symbol, resolution and request each response belongs to. An old request that finishes late shouldn't overwrite newer data. Cache keys should include the provider and any metadata that changes what a bar means.

Runnable example · Time and sessions

On this page