A BoundedSequence of intraday period-width bars, session-aligned:
bars never span a session boundary or an intraday break, and the final bar
of each tradeable segment is truncated at the close (a 90-minute grid
on a 6.5-hour session ends with a short bar). Labelled by bar-open instant.
This mirrors exchange_calendars.trading_index / pandas_market_calendars
bar-grid semantics, and — like sessionSequence — flows straight
through aggregate/materialize.
period is a pond DurationInput ("5m", "1h", or ms). With
range, only sessions overlapping it contribute bars (full sessions).
Optionalrange: InstantRangeA DiscontinuityProvider over this calendar's tradeable spans —
each session's [open, close) with its intraday breaks removed. Closed time
between sessions and inside a lunch break collapses to nothing. A
@pond-ts/charts trading-time scale consumes this (structurally — no
package coupling) to map value → pixel with the gaps excised.
spacing picks the axis metric (the trading-calendar RFC Q7):
'proportional' (default) — time is proportional within and across
sessions; a half-day is half as wide as a full day. The true-time axis.'uniform' — equal width per slot regardless of duration; the ordinal /
TradingView bar look. Without period each session is one slot
(the daily-candle view; intraday breaks are not collapsed within the
slot, since a daily bar spans the lunch). With period each
session-aligned bar (the barSequence grid — break-split, truncated
at each segment's close) is one slot, so an intraday uniform axis lays
every bar out at equal width. period is ignored for 'proportional'.With range, only sessions overlapping [start, end) contribute.
Collapse-point boundaries follow the segments each variant uses:
'proportional' and period-'uniform' split on breaks, so a lunch re-open
is a divider; session-'uniform' (no period) treats each session as one
slot, so it draws a divider only at session opens, not at breaks.
Whether the market is open at instant — inside a session and not inside one of its breaks.
Whether date (YYYY-MM-DD) is a trading day in this calendar.
The first session strictly after ref, or undefined. A numeric ref
compares against session opens (so a ref mid-session returns the next
session); a YYYY-MM-DD ref compares by trading date.
The last session strictly before ref, or undefined. A numeric ref
compares against session opens (a ref mid-session returns that session,
whose open precedes it); a YYYY-MM-DD ref compares by trading date.
The session labelled date (YYYY-MM-DD), or undefined if not a trading day.
All sessions, ascending by open.
A BoundedSequence of one interval per session — [open, close),
labelled by trading date. This is the daily-bar bucketing grid: feed it
straight to TimeSeries.aggregate / .materialize and every bucket is a
real trading session, with no weekend/holiday buckets and no bucket
spanning a market closure. Intraday breaks are inside the daily bar (a
daily bar spans the lunch) — use barSequence to break at them.
With range, only sessions overlapping [start, end) are included (full
sessions — bars are not clipped to the range; crop the result if exact
range edges are needed).
Optionalrange: InstantRangeSessions overlapping the instant range [start, end), ascending.
Append a session-id column to series: for each event, the open
instant of the session that contains it, or undefined if the event falls
in closed time (a gap between sessions, or outside the schedule). The id is
numeric and stable per session — use it as a partitionBy key so
stateful ops (rolling, fill, cumulative folds) don't bridge across a
session boundary (the align/rolling stopgap of the trading-calendar RFC,
Tidal Ask 2). Recover the session from an id via sessionOn /
sessionContaining.
stamped picks how a timestamp lands relative to a session's edges,
matching the feed's bar-stamp convention:
'open' (default) — a session owns [open, close). A bar stamped at its
open instant belongs to the session; one stamped at the close falls in
closed time (the next session hasn't opened). This is the tick / open-stamp
convention.'close' — a session owns (open, close]. A bar stamped at the close
(16:00 on a NYSE feed whose 16:00 bar covers 15:55–16:00) belongs to the
session that just closed, not to closed time; one stamped at the open
belongs to the previous session if it closed there. This is the OHLC /
close-stamp convention — the real-fixture close-boundary bars motivated it.The knob lives here because binning a bar series is where the stamp
convention bites; instantaneous point queries (sessionContaining,
isOpen) stay half-open [open, close), the unambiguous convention
for an instant. Two consequences of that split worth knowing:
'close'-tagged id via
sessionContaining at the close instant returns a different
session than the tag did (the query is half-open) — recover from the id
with sessionOn, or query at a mid-session instant.'close', a bar stamped exactly at a session's open is the
previous bar's close, so it tags to the previous session (or undefined
if a gap precedes the open — including the very first bar of the series).
A close-stamped feed does not emit an open-instant bar, so this only bites
a mixed/misaligned feed.Default column name "session" (override with column). Throws if a
column of that name already exists (a fresh column is appended, per
withColumn). O(n + sessions) — a single merge walk over the (sorted)
events and sessions; materializes the events once to read their instants.
StaticfromBuild a calendar by generating sessions from SessionRules over a date range.
StaticfromBuild a calendar from an explicit session list (validated + sorted). The first-class path.
A trading calendar: an ordered, non-overlapping schedule of Sessions plus the query surface the data ops and (later) the chart axis need.
A calendar is always defined by a session list — the canonical form. Rules are one way to produce that list (TradingCalendar.fromRules); a consumer with dated instants uses them directly (TradingCalendar.fromSessions). Both paths are first-class (RFC Amendment 2, Tidal Ask 1).
The schedule is finite — queries outside its covered range return empty /
undefined, they do not extrapolate. A rules calendar covers exactly the range it was generated over.