Skip to main content

Trading-time axis

A trading-time axis is still a time axis — but it renders in trading time: closed-market spans (overnight, weekends, holidays) collapse out, and pixels are proportional to time within each session. A gap-free intraday candle chart, no dead space between sessions.

src/examples/financial-calendar-chart.tsx
import {
Candlestick,
ChartContainer,
ChartRow,
Layers,
YAxis,
} from '@pond-ts/charts';
import { useSiteChartTheme } from '@site/src/theme/useSiteChartTheme';
import {
demoCalendar,
demoDailyBars,
demoRange,
} from './lib/financial-fixtures';

export default function FinancialCalendarChart() {
const theme = useSiteChartTheme();
const cal = demoCalendar();
const series = demoDailyBars(cal);

return (
<ChartContainer
range={demoRange(cal)}
width={560}
theme={theme}
calendar={cal}
cursor="crosshair"
>
<ChartRow height={220}>
<YAxis id="price" side="right" format="$,.0f" width={50} />
<Layers>
<Candlestick series={series} as="demo" showOHLC />
</Layers>
</ChartRow>
</ChartContainer>
);
}

For the tutorial framing see Learn charts, chapter 9; the full financial walkthrough is the @pond-ts/financial section.

Getting one — the calendar prop

Hand <ChartContainer> a calendar and the axis becomes session-aware. calendar is the high-level sugar: the container derives the gap model from it itself. A @pond-ts/financial TradingCalendar is the usual source:

import { TradingCalendar } from '@pond-ts/financial';

const cal = TradingCalendar.fromRules(
{ timeZone: 'America/New_York', open: '09:30', close: '16:00' },
{ from: '2026-01-05', to: '2026-02-13' },
);

<ChartContainer calendar={cal} range={view} width={560}>
<ChartRow height={220}>
<YAxis id="price" side="right" format="$,.0f" />
<Layers>
<Candlestick series={bars} />
</Layers>
</ChartRow>
</ChartContainer>;

TradingCalendar.fromRules(rules, range) is the minimal path — rules is a { timeZone, open, close } (plus optional weekmask, breaks, holidays, earlyCloses), and the second range is { from, to } (the calendar's own span). The chart's view range is a separate [start, end] you choose; cal.sessions() gives you the session bounds to derive it from. For irregular sessions, TradingCalendar.fromSessions(...) takes an explicit session list.

@pond-ts/charts never imports @pond-ts/financial. The calendar prop is typed against a structural TradingCalendarLike shape (anything with a .discontinuities() method), so the charts package stays free of the financial dependency — you supply the calendar.

Memoize the calendar

Build the calendar (or provider) once and pass a stable reference — the x scale and frame rebuild whenever its identity changes. Construct it in a useMemo/useRef, never inline in the render.

Tuning — spacing and discontinuities

PropTypeDefaultPurpose
calendarTradingCalendarLikeHigh-level: the container derives the gap model from it.
spacing'proportional' | 'uniform''proportional'proportional = true time within a session; uniform = equal width per session (the TradingView ordinal look). Only with calendar.
discontinuitiesDiscontinuityProviderLow-level: pass the gap model yourself (calendar.discontinuities(...)). Takes precedence over calendar.

Most charts want calendar + the default proportional spacing. Reach for discontinuities only when you're constructing the provider some other way, or need uniform bar-width spacing over a custom period.

All three are time-axis only — they're ignored on a value or category axis.

What changes on the axis

  • Gaps collapse. Closed-market spans have zero pixel width; the axis walks a session-aware tick ladder (hour → day → week → … → year) rather than raw wall-clock ticks.
  • A second boundary row. The axis adds a boundary-context row — the date over intraday ticks, the year over daily/monthly ticks — plus a pinned left-edge label so you always know the enclosing period.
  • Session dividers. The container draws a divider at each session boundary (the discontinuity provider's session boundaries).
  • Line breaks at sessions. A LineChart can set sessionBreaks to break the line at each session discontinuity instead of connecting across the collapsed gap. This is a scale break (driven by the axis's collapsed gaps), orthogonal to gaps (a data break) — set both independently.

See also