DocumentationTimeframes & Tick Charts

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:

  1. Historical bars come from your data feed at a chosen interval (5s, 1m, 1h, D, W, …). The chart just draws them.
  2. Tick, volume, and sub-minute bars are aggregated live from a trade/quote stream using TickBarAggregator or CandleBuilder - 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.

live
Rendering live chart…
5-second candles aggregated from a per-second tick stream. Note the HH:MM:SS axis.
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:

live
Rendering live chart…
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.