Skip to content
ChartDeck ChartScript docs Account Open the terminal

Non-standard charts

What OHLC a script sees on Heikin Ashi, line, area and other chart types, on tick, range and seconds bars, and how to get Heikin Ashi values yourself.

ChartDeck can draw a market in many ways: candles, hollow candles, OHLC bars, a line, an area, Heikin Ashi, columns and more. It can also build bars that are not based on time at all: tick bars and range bars. This page explains what your script sees on each, so you always know which prices your calculations use.

#Chart types: your script always sees real prices

The chart type changes how bars are drawn, not the data underneath. On every chart type, open, high, low, close and volume are the market's real values for the bar:

Chart typeWhat it drawsWhat the script sees
Candles, OHLC barsthe real barsthe real bars
Hollow candles, volume candles, columns, high-lowthe real bars, styled differentlythe real bars
Line, area, step, markers, baseline, HLC arealines through the real closes (or highs, lows and closes)the real bars, all four prices
Heikin Ashismoothed candles computed from the real barsthe real bars, not the Heikin Ashi ones

This is deliberate. Heikin Ashi candles are averages: their "close" is a price that never traded. If indicators silently switched to those values when you changed the chart type, a stop level or an alert could sit at a price the market never printed. The price axis, the last-price mark, alerts and drawings follow the same rule.

As a result, chart.is_heikinashi is always false and chart.is_standard always true in this version: whatever the chart draws, the script runs on standard bars.

One output does care about the chart type: barcolor recolors candles and OHLC bars. On the other chart types the chart keeps its own colors. See bar coloring.

#Getting Heikin Ashi values

When you want Heikin Ashi values, ask for them explicitly. There are two ways.

ta.heikin_ashi() returns the Heikin Ashi open, high, low and close of the current bar, computed from the chart's bars:

ChartScriptFragmentOpen in editor
const [haO, haH, haL, haC] = ta.heikin_ashi()
plotcandle(haO, haH, haL, haC, "Heikin Ashi", { color: haC > haO ? color.up : color.down })

For Heikin Ashi bars on another timeframe or market, turn a market id into a Heikin Ashi id with ticker.heikinashi and request it. Inside the request, open, high, low and close are the Heikin Ashi values:

ChartScriptFragmentOpen in editor
const haId = ticker.heikinashi(market.id)
const haClose4h = request.market(haId, "240", close)
plot(haClose4h, "4h Heikin Ashi close", color.accent, { style: plot.style_stepline })

ticker.standard turns such an id back into the plain market. Build the id with these functions rather than typing it: a constant written by hand is checked for the plain VENUE:SYMBOL form and gets warning CS3104.

#Renko, Kagi, line break and point & figure

These chart types are not available on ChartDeck yet. chart.is_renko, chart.is_kagi, chart.is_linebreak, chart.is_pnf and chart.is_range are accepted (so imported scripts compile) and are always false; ticker.renko, ticker.kagi, ticker.linebreak and ticker.pointfigure are not available yet.

#Tick bars

On a tick chart (100T and so on), each bar holds exactly that many trades of the market, as the venue's public trade stream reports them. For the script:

  • open is the first trade's price, close the last, high and low the extremes, and volume the summed trade size in the base coin.
  • time is the time of the bar's first trade. Bars come faster when the market is busy and slower when it is quiet.
  • Venues count trades differently (some report one print per taker order, others every fill), so a 100-tick bar on one venue can span more time than on another. Compare tick charts within one venue.

#Range bars

On a range chart (100R, or a percent range such as 50P), a new bar starts each time price has moved the full range from the bar's low or high. Every closed bar spans exactly the range: its close sits on the boundary, and the next bar opens there.

When price jumps several ranges at once, ChartDeck fills the gap with whole range bars of zero volume so every bar keeps the same size. Scripts see those fill bars like any other. An average of volume, or a volume-weighted calculation, will count them as zeros, which is usually what you want; if it is not, skip bars where volume == 0.

#What changes for time on tick and range bars

These bars have no fixed length, so anything that assumes one needs care:

  • timeframe.isticks is true on both tick and range charts, and timeframe.isintraday is true too.
  • timeframe.in_seconds() and time_close carry no meaning here. Use time - time[1] to measure how long a bar took.
  • Requests for time-based timeframes ("60", "1D") and other markets work and line up by time. A request for a tick or range timeframe, or for the chart's own timeframe (""), gives na.
  • On some venues the history of tick and range bars starts when ChartDeck began recording the market; the chart says so.
  • A script runs on the bars the chart holds (at most its bars per run): these bars are built from recorded trades, so ChartDeck cannot fetch older ones for the script as it does on time charts. The same goes for seconds charts.

Timeframes lists every tick and range size.

#Seconds bars

Seconds charts (1S to 45S) are ordinary time bars, built from live trades. A second without any trade is a flat bar: open, high, low and close all equal the last price, and volume is 0. A script runs on seconds bars normally, and it can request a seconds timeframe from any chart: see Seconds timeframes.

#A whole example: bar speed on any chart

This indicator works on time, tick and range charts alike. It measures how long each bar took to form, which is the natural "speed of the market" reading on tick and range bars, and marks range-chart fill bars.

ChartScriptOpen in editor
//@chartscript 1
indicator("Bar speed")

const smooth = input.int(10, "Smoothing", { minval: 1, maxval: 100 })

const secs = bar_index > 0 ? (time - time[1]) / 1000 : na
const avgSecs = ta.sma(secs, smooth)

plot(secs, "Seconds to form", color.muted, { style: plot.style_columns })
plot(avgSecs, "Average", color.accent, 2)

const isFill = timeframe.isticks && volume == 0
plotshape(isFill, "Fill bar (no trades)", shape.circle, location.top, color.muted, { size: size.tiny })

state info: label = na
if (barstate.islast) {
    label.delete(info)
    const kind = timeframe.isticks ? "trade-built bars" : "time bars"
    info = label.new(
        bar_index,
        avgSecs,
        timeframe.period + ": " + kind,
        { style: label.style_label_left, textcolor: color.text, color: color.new(color.muted, 70) }
    )
}

Here secs is the time since the previous bar opened, that is, how long the previous bar took. On time charts it is simply the bar length, which is a handy check that the script reads time the way you think.

The ChartScript documentation is in English for now; translations will follow.