Order flow
Footprint bars
Bid and ask volume at every price inside the bar, with imbalances marked and a delta strip underneath.
Footprint bars
@ortex-charts/financialLoading footprint bars…
Bid and ask volume at every price inside the bar, with imbalances marked and a delta strip underneath.
footprint.ts
import { createFinancialChart, footprintFromTicks, footprintTotals, mergeFootprint } from "@ortex-charts/financial";
import { parseResolution, ticksToBars, type Bar, type Tick } from "@ortex-charts/math";
const RESOLUTION = parseResolution("30");
const TICK = 0.1; // Height of one footprint cell, in price units.
// The public sample feed carries prices only: no sizes and no aggressor
// side. Every print here is therefore given an illustrative size, and
// `footprintFromTicks` splits bid from ask with the tick rule, which is the
// estimate every platform without aggressor flags is built on. A feed that
// reports the aggressor sets `side` on each tick and the split is exact.
const flow = footprintFromTicks(prints, RESOLUTION, TICK, TIME_ZONE);
const bars = ticksToBars(prints, RESOLUTION, TIME_ZONE);
const chart = createFinancialChart(box, {
theme,
timeZone: TIME_ZONE,
resolution: "30",
// `mergeFootprint` copies each bar's aggressor totals onto the bar, so
// the delta and CVD indicators read them instead of guessing.
data: mergeFootprint(bars, flow),
volume: false,
seriesOptions: { priceFormat: { type: "price", precision: 2, minMove: 0.01 } },
timeScale: { barSpacing: 58 },
});
const footprint = chart.setFootprint(flow, { tickSize: TICK, imbalanceRatio: 3, showTotals: true, showPoc: true });
chart.chart.subscribeCrosshairMove((e) => {
const bar = e.time === null ? undefined : footprint.flowAt(e.time);
if (!bar) return;
const t = footprintTotals(bar);
status(
`Bid ${t.sell.toLocaleString()} Ask ${t.buy.toLocaleString()} Delta ${t.delta >= 0 ? "+" : ""}${t.delta.toLocaleString()} Point of control ${t.poc.toFixed(2)}`,
);
});