Feeding data

Tickline doesn't fetch anything itself: your app gives it candles and prices from wherever they come from (REST, WebSocket, SignalR, Firebase, a test file).

Candles

interface Candle {
  time: number;    // start of the candle, milliseconds since 1970 (UTC)
  open: number;
  high: number;
  low: number;
  close: number;
  volume?: number; // needed for the volume indicator
}

chart.setData(candles) replaces the history. It:

Times in seconds? Multiply by 1000. Times as strings? Use Date.parse(s).

Live trades and quotes: tick

chart.tick({ time: Date.now(), price: 68123.45, volume: 0.02 });

For quote feeds with bid and ask, pass the price you want to chart (often the bid, or the middle).

Whole live candles: update

chart.update({ time: 1793714700000, open: 1.1, high: 1.2, low: 1.0, close: 1.15, volume: 42 });

A candle with the same start time as the last one replaces it; a newer one is added; an older one is ignored.

Loading history page by page

Load a first page of candles, then older pages as the user scrolls back. The chart holds only what's been loaded, so it stays quick even when your server has years of data. Give it a history loader: a function that returns one page.

chart.setHistoryLoader(async ({ before, count, timeframe, symbol, signal }) => {
  // before: load candles older than this time (ms), or the latest ones when it's null
  const url = `/api/candles?symbol=${symbol}&tf=${timeframe}&limit=${count}` + (before ? `&before=${before}` : "");
  return (await fetch(url, { signal })).json(); // an array of candles, oldest first or in any order
});

What happens:

count is history.pageSize (default 500). chart.historyState is "idle", "loading", "error" or "end", and the historystate event (tl-historystate on the element) fires when it changes. chart.reloadHistory() starts again from the latest page. setHistoryLoader(null) stops.

All the words are in labels (loading, loadingHistory, historyStart, historyError, retry) for translation.

Answering the event instead

Without a loader, the chart fires requesthistory (tl-requesthistory on the element) when it needs older candles. Answer with respondWith and the chart shows the loading state and adds them itself:

chart.addEventListener("tl-requesthistory", (e) => {
  e.detail.respondWith(api.candles({ before: e.detail.before, limit: e.detail.count }));
});

Call respondWith straight away in the handler, with a promise (or an array). Or handle it all yourself and call prependData(older) when they arrive; then there's no loading state. Either way it asks once per oldest candle, so it won't flood your API while the user keeps dragging. Turn requests off with history: { request: false }.

Memory

The chart keeps at most history.maxCandles candles (default 100,000). Past that, it drops the oldest ones that are well out of view, for example after the user scrolled far back and returned to the live price. With a loader, scrolling back loads them again. Set maxCandles: 0 for no limit.

Prices and decimals

Decimals are worked out from the price (68,123.45 → 2, 1.11942 → 5, 0.443085 → 6). Set them yourself with precision:

<trade-chart precision="4"></trade-chart>

Numbers use English separators (68,123.45) unless you set a locale such as de-DE (68.123,45), or your own format.price function. See Configuration.

Time zones

Times are always UTC milliseconds in your data. The axis shows them in the user's local time by default. To show a fixed zone, set timezone in minutes from UTC:

<trade-chart timezone="0"></trade-chart>     <!-- UTC -->
<trade-chart timezone="420"></trade-chart>   <!-- UTC+7, e.g. Phnom Penh, Bangkok -->
<trade-chart timezone="-300"></trade-chart>  <!-- UTC−5 -->

Candles are grouped on UTC boundaries (days start at 00:00 UTC, weeks on Monday, months on the 1st), the same way most exchanges and brokers do it.

Timeframes

"5s", "1m", "15m", "1h", "4h", "1d", "1w", "1M" (month), or any number with s, m, h, d, w or M.

Changing the timeframe regroups the candles the chart has, but only into a larger timeframe (1m → 5m works; 5m → 1m can't be made up). Load fresh history for the new timeframe:

async function switchTimeframe(tf) {
  chart.setLoading(true);
  chart.setAttribute("timeframe", tf);  // or chart.setOptions({ timeframe: tf })
  chart.setData(await api.candles({ tf }));
  chart.setLoading(false);
}

Switching symbols

async function switchSymbol(symbol) {
  socket.close();
  chart.setLoading(true);                 // the loading state, inside the chart
  chart.setAttribute("symbol", symbol);
  chart.clearOverlays();                  // positions and alerts belonged to the old symbol
  chart.setData(await api.candles({ symbol }));
  chart.setLoading(false);
  socket = openSocket(symbol);
}

With a history loader, setting the symbol is enough: the chart clears its candles and loads the new symbol's latest page, showing placeholder candles meanwhile.

Drawings are kept until you call clearDrawings() or setDrawings(savedForThisSymbol); see Several charts and saved charts for saving a chart per symbol.

No live data

Without tick or update, it's a static chart of the history you gave it: no countdown or live marker, and the view isn't scrolled forward with the clock. A chart counts as live when its last candle ended less than three candles ago.

Reading data back

chart.data returns every candle the chart holds, oldest first, after grouping.

Edit this page on GitHub