API Reference¶
Scale¶
BusdayScale
¶
BusdayScale(
axis,
bushours=(0, 24),
weekmask=None,
holidays=None,
busdaycal=None,
)
Bases: mscale.ScaleBase
Matplotlib scale that compresses off-hours and non-business days.
Maps datetime values to business-day coordinates. Days and hours outside the defined schedule are collapsed so that every visible unit on the axis corresponds to active time.
Registered as the "busday" scale automatically on import busdayaxis.
All parameters below except axis can be passed directly to
ax.set_xscale("busday", ...). axis is injected automatically by
Matplotlib and must not be passed explicitly.
| PARAMETER | DESCRIPTION | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
axis
|
Injected automatically by Matplotlib. Do not pass this via
TYPE:
|
||||||||||||||||||||||||
bushours
|
Active hours per weekday. Gaps between intervals (e.g. a lunch break) are collapsed on the axis just like overnight gaps or weekends.
Two accepted forms:
TYPE:
|
||||||||||||||||||||||||
weekmask
|
Which weekdays are business days (
Use a string of 7 characters (Mon–Sun), a space-separated list of
three-letter day names, or any format accepted by
TYPE:
|
||||||||||||||||||||||||
holidays
|
Extra non-business dates, regardless of weekmask. Dates on these
days are collapsed to zero width on the axis, identical to weekends.
Accepts any format understood by :func:
TYPE:
|
||||||||||||||||||||||||
busdaycal
|
Pre-built calendar combining a weekmask and holiday list. When set,
weekmask and holidays are ignored. Useful when reusing the same
calendar across multiple axes. Default Note that
TYPE:
|
Notes
- Clipping: Timestamps outside
bushoursare clipped to the nearest session boundary during the forward transform. For example, withbushours=(9, 17), both 08:30 and 09:00 map to the same axis position (the session open). With multiple intervals, times in a gap (e.g. during a lunch break) are clipped to the end of the preceding interval. The transform is therefore not perfectly invertible: the inverse always returns a time on a session boundary or within a session. -
Timezone handling: The scale expects timezone-naive inputs and treats them as wall-clock time. If your data carries timezone info (e.g. a UTC-aware
pandas.DatetimeIndex), convert to the market's local timezone and strip the tzinfo before plotting::dates = dates_utc.tz_convert("America/New_York").tz_localize(None)
Examples:
Compress weekends only (Mon–Fri default):
ax.set_xscale("busday")
Compress overnight gaps as well as weekends:
ax.set_xscale("busday", bushours=(9, 17))
Collapse a lunch break (12:00–13:00) in addition to overnight gaps:
ax.set_xscale("busday", bushours=[(9, 12), (13, 17)])
Per-day hours — Friday early close, Monday with a lunch break:
ax.set_xscale("busday", bushours={"Mon": [(9, 12), (13, 17)], "Fri": (9, 13)})
Show Sundays with custom hours; weekmask derived automatically (Sat excluded):
ax.set_xscale("busday", bushours={"Sun": (10, 18)})
FX-style session: Sunday open 22:00, Friday close 22:00, full days otherwise:
ax.set_xscale("busday", bushours={"Sun": (22, 24), "Fri": (0, 22)})
Middle-Eastern work week (Sun–Thu) with a holiday:
ax.set_xscale("busday", weekmask="Sun Mon Tue Wed Thu",
holidays=["2025-01-01"])
Reuse a pre-built numpy.busdaycalendar:
cal = np.busdaycalendar(weekmask="1111100", holidays=["2025-12-25"])
ax.set_xscale("busday", busdaycal=cal)
Custom tick placement with BusdayLocator:
import matplotlib.dates as mdates
ax.set_xscale("busday", bushours=(9, 17))
ax.xaxis.set_major_locator(
BusdayLocator(mdates.HourLocator(byhour=range(9, 18)))
)
register_scale
¶
register_scale()
Register the "busday" scale with Matplotlib.
Called automatically on import busdayaxis, so explicit calls are
not required. The function is idempotent — calling it again has no effect
and existing code that calls it explicitly continues to work.
After registration, ax.set_xscale("busday", ...) is available for the
lifetime of the Python session. The keyword arguments bushours,
weekmask, holidays, and busdaycal are forwarded directly to
busdayaxis.BusdayScale;
axis is injected by Matplotlib automatically and cannot be passed.
Examples:
import busdayaxis # scale is registered automatically
import matplotlib.pyplot as plt
import pandas as pd
dates = pd.date_range("2025-01-01", periods=10, freq="D")
values = range(10)
fig, ax = plt.subplots()
ax.plot(dates, values)
ax.set_xscale("busday", bushours=(9, 17)) # kwargs forwarded to BusdayScale
plt.show()
Locator¶
BusdayLocator
¶
BusdayLocator(base_locator=None, keep_midnight_ticks=None)
Bases: mdates.DateLocator
Tick locator that filters out ticks outside business hours and business days.
Wraps any Matplotlib date locator and discards ticks that fall on
non-business days or outside the active session defined by bushours.
The locator reads the business-hours and weekmask configuration from the
axis automatically (set by BusdayScale), so it stays in sync with the
scale without any extra configuration.
The "busday" scale is registered automatically on import busdayaxis.
BusdayLocator is set automatically when you call ax.set_xscale("busday").
| PARAMETER | DESCRIPTION |
|---|---|
base_locator
|
The underlying datetime locator that proposes tick candidates.
If None, falls back to
TYPE:
|
keep_midnight_ticks
|
Controls whether ticks at midnight (00:00) on business days are kept
even when they fall outside
TYPE:
|
Examples:
Hourly ticks during business hours (9–17)::
ax.set_xscale("busday", bushours=(9, 17))
ax.xaxis.set_major_locator(
BusdayLocator(mdates.HourLocator())
)
Daily ticks on business days only::
ax.xaxis.set_major_locator(BusdayLocator(mdates.DayLocator()))
DayLocator
¶
DayLocator(keep_midnight_ticks=None, **kwargs)
Bases: BusdayLocator
Business-day-aware wrapper around matplotlib.dates.DayLocator.
Places one tick per day (or every interval days), then discards any that fall on non-business days. Midnight ticks are always kept because daily ticks are placed at day boundaries.
All keyword arguments are forwarded to ~matplotlib.dates.DayLocator.
HourLocator
¶
HourLocator(keep_midnight_ticks=None, **kwargs)
Bases: BusdayLocator
Business-day-aware wrapper around matplotlib.dates.HourLocator.
Places ticks at the specified hours, then discards any that fall outside business days or business hours. Midnight ticks are not kept by default because hourly ticks are sub-daily.
All keyword arguments are forwarded to ~matplotlib.dates.HourLocator.
MicrosecondLocator
¶
MicrosecondLocator(keep_midnight_ticks=None, **kwargs)
Bases: BusdayLocator
Business-day-aware wrapper around matplotlib.dates.MicrosecondLocator.
Places ticks at the specified microseconds, then discards any that fall outside business days or business hours. Midnight ticks are not kept by default because microsecond-level ticks are sub-daily.
All keyword arguments are forwarded to ~matplotlib.dates.MicrosecondLocator.
MidBusdayLocator
¶
Bases: mdates.DateLocator
Places one tick at the midpoint of the business hours for each business day.
Useful for centering day labels within each session, even when business hours vary by weekday or differ from the standard 9–17 window.
Examples:
Center day labels as minor ticks::
ax.set_xscale("busday", bushours=(9, 17))
ax.xaxis.set_minor_locator(busdayaxis.MidBusdayLocator())
ax.xaxis.set_minor_formatter(mdates.DateFormatter("%a"))
MinuteLocator
¶
MinuteLocator(keep_midnight_ticks=None, **kwargs)
Bases: BusdayLocator
Business-day-aware wrapper around matplotlib.dates.MinuteLocator.
Places ticks at the specified minutes, then discards any that fall outside business days or business hours. Midnight ticks are not kept by default because minute-level ticks are sub-daily.
All keyword arguments are forwarded to ~matplotlib.dates.MinuteLocator.
SecondLocator
¶
SecondLocator(keep_midnight_ticks=None, **kwargs)
Bases: BusdayLocator
Business-day-aware wrapper around matplotlib.dates.SecondLocator.
Places ticks at the specified seconds, then discards any that fall outside business days or business hours. Midnight ticks are not kept by default because second-level ticks are sub-daily.
All keyword arguments are forwarded to ~matplotlib.dates.SecondLocator.
WeekdayLocator
¶
WeekdayLocator(keep_midnight_ticks=None, **kwargs)
Bases: BusdayLocator
Business-day-aware wrapper around matplotlib.dates.WeekdayLocator.
Places ticks on specified weekdays, then discards any that fall on non-business days (e.g. public holidays). Midnight ticks are always kept because weekday ticks are placed at day boundaries.
All keyword arguments are forwarded to ~matplotlib.dates.WeekdayLocator.
Utilities¶
mark_gaps
¶
mark_gaps(ax, style='vline', **kwargs)
Mark session-boundary gaps on a "busday"-scale axis.
Draws a visual indicator at every point where the axis collapses a gap (end of a session, overnight, weekend, holiday). Useful for signalling to the reader that time has been removed.
| PARAMETER | DESCRIPTION |
|---|---|
ax
|
Axis that must already use the
TYPE:
|
style
|
Visual style:
TYPE:
|
**kwargs
|
Forwarded to the underlying artists. Useful keys:
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
list of matplotlib artists
|
All artists added to the axes, so the caller can adjust them later. |
Examples:
ax.set_xscale("busday", bushours=(9, 17))
busdayaxis.mark_gaps(ax) # thin dashed vlines
busdayaxis.mark_gaps(ax, style="broken") # slash marks only
busdayaxis.mark_gaps(ax, style="both", color="steelblue", alpha=0.4)
holidays_from_exchange
¶
holidays_from_exchange(calendar, start, end)
Return non-trading weekdays as ISO date strings for use with
holidays=.
Extracts trading days from an exchange_calendars or
pandas_market_calendars calendar object and returns all weekdays in
[start, end] that are not trading days — i.e. the holidays and
irregular closures you can pass directly to holidays=.
The function duck-types the calendar object and tries all known calling
conventions, so no hard dependency on either library is introduced:
pandas_market_calendars uses schedule(start_date=, end_date=),
while exchange_calendars exposes sessions_in_range(start, end).
| PARAMETER | DESCRIPTION |
|---|---|
calendar
|
Any object that exposes either a
TYPE:
|
start
|
Inclusive date range to scan (e.g.
TYPE:
|
end
|
Inclusive date range to scan (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[str]
|
ISO date strings ( |
Examples:
With exchange_calendars:
import exchange_calendars as xcals
import busdayaxis
cal = xcals.get_calendar("XNYS")
holidays = busdayaxis.holidays_from_exchange(cal, "2025-01-01", "2025-12-31")
ax.set_xscale("busday", holidays=holidays)
With pandas_market_calendars:
import pandas_market_calendars as mcal
import busdayaxis
cal = mcal.get_calendar("NYSE")
holidays = busdayaxis.holidays_from_exchange(cal, "2025-01-01", "2025-12-31")
ax.set_xscale("busday", holidays=holidays)