Timeframes & tick charts
OpenAlgo Charts handles the full range of timeframes - from seconds and ticks up to weeks - because the time axis is a logical index, not a fixed grid.
Time labels default to Asia/Kolkata, the Indian-market default, and nothing more:
pass an IANA name to relabel for another market, at construction or at runtime.
createChart(el, { timezone: 'America/New_York' }) and chart.setTimezone('Europe/London')
both drive the time-axis ticks and the crosshair pill. See Timezones.
A timeFormatter (UTC seconds -> string) is still there for a host that wants to format
labels itself, and it outranks the zone.
There are two sources of bars:
- Historical bars come from your data feed at a chosen interval (
5s,1m,1h,D,W, …). The chart just draws them. - Tick, volume, and sub-minute bars are aggregated live from a trade/quote stream
using
TickBarAggregatororCandleBuilder- OHLCV history alone cannot produce them.
History intervals
For custom intervals over native source candles, use the opt-in
withTimeframes adapter. It works with any DataFeed
when the host supplies source capabilities and candle/session boundaries.
The OpenAlgo REST adapter passes the interval through verbatim, so any interval your broker supports works. Fetch the exact list at runtime instead of hard-coding it:
// OpenAlgo exposes the supported intervals per broker:
// seconds: 5s, 10s, 15s, 30s, 45s, 60s ...
// minutes: 1m, 3m, 5m, 10m, 15m, 30m ...
// hours: 1h, 2h, 4h ...
// days+: D, W, M
const bars = await feed.getBars({
symbol: 'RELIANCE', exchange: 'NSE', interval: '5s',
from: dayStartUtcSeconds, to: nowUtcSeconds,
});
series.setData(bars);The time axis automatically switches to HH:MM:SS labels when the visible bars are less than a minute apart, and the crosshair tag shows seconds on sub-minute bars - so a 5-second chart reads correctly without any configuration.
An interval code resolves through a registry, and an unrecognised code throws rather
than quietly meaning one minute. The built-in grammar covers s, m, h, d and w and
is case-insensitive, so a bare M is a minute: register a calendar interval if your
broker’s M means a month, and register 1Q, T500 or V10K before you use them. See
Custom Intervals.
To branch on the timeframe (does this need a session reset, does the axis need seconds), ask
the registry rather than matching on the string. intervalParts('120m') and
intervalParts('2h') both answer 2 hours, and isIntradayInterval, isDailyInterval,
isSecondsInterval and isTickInterval answer the yes-or-no questions directly, for any
registered code and not only for the built-in tokens. See
Reading a code back.
Second timeframes (live)
CandleBuilder buckets a tick stream into interval OHLC. intervalSec can be any number of
seconds - here, 5-second candles. Pass a sessionAnchorSec so buckets align to the session
open (09:15) rather than an arbitrary epoch floor.
View example code
const chart = lib.createChart(el);
const series = chart.addSeries('candlestick');
// Build 5-second candles from a simulated tick stream.
const builder = new lib.CandleBuilder({ intervalSec: 5, volumeMode: 'ltq-sum' });
const bars = [];
let t = 1700000000, p = 100, seed = 99;
for (let i = 0; i < 3000; i++) {
seed = (seed * 1103515245 + 12345) & 2147483647;
p = Math.max(1, p + (seed / 2147483647 - 0.5) * 0.4);
t += 1; // one tick per second
const u = builder.onTick({ time: t, price: p, ltq: 10 });
if (u) { if (u.isNew) bars.push(u.bar); else bars[bars.length - 1] = u.bar; }
}
series.setData(bars);
chart.timeScale.fitContent(bars.length);
return chart;Tick bars
TickBarAggregator makes bars from trade ticks by clock interval, calendar period,
tick count, or traded volume:
import { TickBarAggregator } from 'openalgo-charts';
const agg = new TickBarAggregator({ mode: 'ticks', count: 100 }); // 100-tick bars
// or { mode: 'volume', perBar: 5000 } - constant-volume bars
// or { mode: 'interval', seconds: 30 } - 30-second bars
// or { mode: 'calendar', unit: 'month' } - months, quarters and years, which have no
// fixed length; pass { timezone } so the boundary is local midnight where it should be
ws.onTrade((t) => {
const u = agg.onTick({ time: t.timeSec, price: t.price, qty: t.qty });
if (u.isNew) series.setData([...bars, u.bar]); // append
else series.update(u.bar); // mutate the forming bar
});Here is a 250-tick chart built from a simulated trade stream:
View example code
const chart = lib.createChart(el);
const series = chart.addSeries('candlestick');
const agg = new lib.TickBarAggregator({ mode: 'ticks', count: 250 });
const bars = [];
let t = 1700000000, p = 100, seed = 3;
for (let i = 0; i < 12000; i++) {
seed = (seed * 1103515245 + 12345) & 2147483647;
p = Math.max(1, p + (seed / 2147483647 - 0.5) * 0.6);
t += 1;
const u = agg.onTick({ time: t, price: p, qty: 1 });
if (u.isNew) bars.push(u.bar); else bars[bars.length - 1] = u.bar;
}
series.setData(bars);
chart.timeScale.fitContent(bars.length);
return chart;Tick-count and volume bars require real trade ticks. A 1-minute OHLCV history cannot be re-bucketed into them - you need a tick feed (or a tick-recording backend). Bid/ask classification for footprint / order flow needs the same.