Skip to content
ChartDeck ChartScript docs Account Open the terminal

Footprints

The buy and sell volume at each price row inside a bar, the data the Footprint chart draws, with its busiest rows, delta extremes and Extremes readings.

A footprint splits each bar into price rows and shows who traded at each one: sells that hit the bid and buys that lifted the ask. The Footprint chart type draws it; a script reads the same data with the fp names. Footprints come with Pro and Elite. On other plans every fp value is na and the legend says why; the script still compiles and runs.

#Where the data comes from

fp reads the Footprint chart's own data for the chart's own market and exchange, in the chart's order: the live footprint built from the trades, then ChartDeck's recording, then days rebuilt from the trade archive. Another exchange is never used; on an aggregated chart the values are the aggregated footprint the chart draws, summed over the exchanges ChartDeck records. A market or timeframe without footprints (they exist for 1-minute to 1-day bars) reads na, and the legend says so.

Every value reads the bar's rows on the recorded grid: 50 rows per 1 % of the bar's close (BTC: $20 rows). That is the grid the chart's own marks are read on. The rows the chart draws follow your zoom; the values a script reads do not, so a script gives the same answer at any zoom, in Bar replay and on ChartDeck's servers.

#One value per bar

NameGives
fp.availabletrue when the bar has footprint rows
fp.row_sizethe row height in price behind the values below
fp.buy_vol, fp.sell_vol, fp.deltabuys, sells and buys minus sells, summed over the rows (base coin)
fp.pocthe middle of the busiest row (the bar's point of control)
fp.max_sell_px, fp.max_sell_qtythe row with the most sells: its middle and its sell volume
fp.max_buy_px, fp.max_buy_qtythe same for buys
fp.min_delta_px, fp.min_deltathe row with the most negative delta: where sellers hit hardest
fp.max_delta_px, fp.max_deltathe row with the most positive delta
fp.low_delta, fp.high_deltathe net delta of the bar's low and high zone (the bottom and top 25 % of its rows, 2 at least)
fp.low_read, fp.high_readthe chart's Extremes reading of that zone: 1 initiative, -1 responsive, 0 neither side clear

Prices are the middle of a row. When two rows tie, the lower one counts. A bar with fewer than 4 rows has no zones: its low_ and high_ values are na. The Extremes readings use the chart's default settings; the chart also leaves out bars much quieter than the others in view, which depends on the view, so a script compares volumes itself.

ChartScriptOpen in editor
//@chartscript 1
indicator("Footprint point of control", { overlay: true })
plot(fp.poc, "POC", { color: color.orange, style: plot.style_circles })
plotshape(fp.low_read == 1, "Selling at the low", shape.triangledown, location.belowbar, color.red)
plotshape(fp.high_read == 1, "Buying at the high", shape.triangleup, location.abovebar, color.green)

#Lower timeframes

The per-bar values work inside requests. On a 5-minute chart, request.lower_tf("1", ...) gives each bar the values of its 1-minute bars, oldest first:

ChartScriptOpen in editor
//@chartscript 1
indicator("Hardest selling inside the bar", { overlay: true })
const [px, d] = request.lower_tf("1", [fp.min_delta_px, fp.min_delta])
let worst: float = na
let at: float = na
for (let i = 0; i < d.size(); i++) {
    if (!na(d.get(i)) && (na(worst) || d.get(i) < worst)) {
        worst = d.get(i)
        at = px.get(i)
    }
}
plot(at, "Row of the hardest 1-minute selling", { color: color.red, style: plot.style_cross })

#The rows themselves

fp.rows(back) returns the rows of a bar, low to high, as fp.row values with price (the row's middle), buy, sell and delta. back counts bars: 0 is this bar. A bar wider than 400 rows gives the 400 around its point of control. fp.delta_at(price, back) gives the delta of the row holding a price, and fp.since() the time of the first bar with rows. These three read the chart's own bars: inside a request they read na.

ChartScriptOpen in editor
//@chartscript 1
indicator("Delta at the low", { overlay: true })
const atLow = fp.delta_at(low)
plotshape(atLow < 0 && close > fp.min_delta_px, "Sellers at the low, close above", shape.circle, location.belowbar, color.orange)

#Where the history starts

fp.since() is the time of the first bar with footprint rows among the bars your script runs on. Give it a timeframe and it says where the footprint the script reads at that timeframe starts: on a 5-minute chart that reads request.lower_tf("1", fp.*), fp.since("1") is the first 1-minute bar with rows, usually much later than fp.since() because a script reads about 10,000 bars of each timeframe (7 days of 1-minute bars, 35 days of 5-minute bars). Bars before it read na, never 0, and fp.available is false there.

ChartScriptOpen in editor
//@chartscript 1
indicator("Footprint history", { overlay: true })
const d = request.lower_tf("1", fp.min_delta)
if (barstate.islast) {
    if (na(fp.since("1"))) {
        legend.info("No 1-minute footprints here")
    } else {
        legend.info("1-minute footprint history starts at " + str.format_time(fp.since("1"), "yyyy-MM-dd HH:mm") + " UTC")
    }
}
plot(array.size(d), "1-minute bars read")

#The forming bar, replay and history

The newest bar's values move until it closes, as its footprint does: use barstate.isconfirmed for a value that will not change. In Bar replay a script keeps running and sees only the bars that had closed at the replay clock.

A script reads about the newest 10,000 bars of each timeframe (42 pages of 240 bars), counted separately for the chart's bars and for each requested timeframe. Older bars read na, never 0, and fp.available is false there; so do bars before the recording started. A script never starts a rebuild from the trade archive: days the archive has not rebuilt yet are na until someone opens them on a Footprint chart. A script whose run is longer than that (a 20,000-bar run on Elite) gets footprints on its newest 10,000 bars only.

All fp values together count as one of your plan's data series per script, once for the chart and once per requested timeframe. A script on ChartDeck's servers (a protected script, a script alert) reads the same pages with the same rules, so it gives the same values as on the chart.

A footprint is a reading aid, not a forecast: it shows what traded, not what will.

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