@pond-ts/financial
A trading calendar: a session schedule — open/close instants, holidays,
intraday breaks — plus the query surface pond's data ops and
@pond-ts/charts' trading-time axis need. @pond-ts/financial is
deliberately small: one class, TradingCalendar.
Install — npm install @pond-ts/financial pond-ts. Peer-depends on
pond-ts; the pond packages release together, so keep their ranges in
step.
The candle below is calendar-aware
Weekends genuinely don't exist in the data below — every bar is stamped at
a real trading session's open, with no bar for Saturday or Sunday. Handing
the calendar to <ChartContainer> collapses that closed time from the
axis too, so trading days sit flush against each other instead of a gap
opening up every weekend:
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>
);
}
<ChartContainer range={range} width={560} theme={theme} calendar={cal}>
<ChartRow height={220}>
<YAxis id="price" side="right" format="$,.0f" />
<Layers>
<Candlestick series={series} as="demo" showOHLC />
</Layers>
</ChartRow>
</ChartContainer>
calendar is the only new prop — everything else is the same
ChartContainer/ChartRow/Layers/Candlestick vocabulary from the
Learn charts track. No package coupling: the chart
only needs an object shaped like { discontinuities(): ... }, which
TradingCalendar happens to be.
Building a calendar
Two constructors, both first-class — pick whichever shape your data starts in:
import { TradingCalendar } from '@pond-ts/financial';
// From hours + a date range — a calendar that *generates* its sessions.
const cal = TradingCalendar.fromRules(
{ timeZone: 'America/New_York', open: '09:30', close: '16:00' },
{ from: '2026-01-05', to: '2026-02-13' },
);
// From an explicit session list — for a feed that already hands you
// dated open/close instants (a vendor calendar, a holiday API).
const cal2 = TradingCalendar.fromSessions(sessions);
SessionRules also takes weekmask (default Monday–Friday), breaks
(a lunch), holidays (dates with no session), and earlyCloses
(per-date override closes) — every hour is resolved DST-correctly
against the IANA timeZone you give it, not a fixed UTC offset.
Querying the calendar
cal.sessions(); // every session, ascending
cal.sessionOn('2026-01-06'); // the session on a date, or undefined
cal.isTradingDay('2026-01-10'); // false — a Saturday
cal.sessionContaining(instant); // the session an instant falls in
cal.isOpen(instant); // inside a session AND not inside a break
Feeding a chart-ready bucketing grid
sessionSequence/barSequence return a pond BoundedSequence — feed it
straight to .aggregate/.materialize and every bucket lands on a real
trading session, with no weekend/holiday buckets and no bucket spanning a
market closure:
const dailyBars = tickSeries.aggregate(cal.sessionSequence(), {
price: { from: 'price', using: 'last' },
});
const fiveMinBars = tickSeries.aggregate(cal.barSequence('5m'), {
price: { from: 'price', using: 'last' },
});
barSequence bars are session-aligned — they never span a session
boundary or an intraday break, and the final bar of each tradeable
segment truncates at the close rather than overrunning it.
Tagging events with their session
tagSessions appends a session-id column — use it as a partitionBy key
so stateful ops (rolling, fill, cumulative folds) don't bridge across
a session boundary a raw time-based window would silently cross:
const tagged = cal.tagSessions(series); // adds a "session" column
const smoothed = tagged
.partitionBy('session')
.rolling('5m', { price: 'avg' })
.collect();
Where to go next
- API reference — the generated, full-width reference: every method, option, and type.
- Learn charts — the tutorial track this page's chart reuses every primitive from.
- Resizable multi-panel layout — the canonical financial shape: a price chart over an indicator panel.
This page is a quickstart, not the full financial-charting guide — a
flagship end-to-end guide (OHLC data → aggregate rollups → Candlestick
variants → volume row → the forming-bar live pattern) is on the roadmap.
See PLAN.md's
"Docs site wave" for what's shipped and what's next.