Note
Click here to download the full example code
Multiple Different Artists¶
Most standard Matplotlib artists work on the busday scale.

import matplotlib.dates as mdates
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import busdayaxis
busdayaxis.register_scale()
# define dummy data
num_days = 20
dates = pd.date_range("2025-01-01", periods=num_days * 24, freq="h")
returns = np.random.normal(0, 0.002, len(dates))
returns[~np.is_busday(np.array(dates, dtype="datetime64[D]"))] = 0.0
prices = 100 * (1 + pd.Series(returns, index=dates)).cumprod()
eod = prices[(dates.hour == 16) & (dates.weekday < 5)]
rng = np.random.default_rng(0)
eod_high = eod + rng.uniform(0.1, 0.6, len(eod))
eod_low = eod - rng.uniform(0.1, 0.6, len(eod))
volume = pd.Series(rng.integers(1_000, 10_000, len(eod)), index=eod.index)
price_min = prices.values.min()
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 7), sharey=True)
fig.suptitle("Multiple Artists on busday Scale", fontsize=14)
ax1_vol = ax1.twinx()
ax2_vol = ax2.twinx()
full_days = pd.date_range(dates.min().normalize(), dates.max().normalize(), freq="D")
def _draw(ax, ax_vol, *, busday: bool):
# Volume bars (drawn first so price renders on top)
ax_vol.bar(volume.index, volume.values, width=0.8, alpha=0.25, color="green")
ax_vol.set_ylabel("Volume", color="green", fontsize=8)
ax_vol.tick_params(axis="y", labelcolor="green", labelsize=7)
if busday:
ax_vol.set_xscale("busday")
# Price artists
ax.fill_between(dates, prices.values, price_min, alpha=0.12)
ax.plot(dates, prices.values, linewidth=1.3)
ax.step(
eod.index,
eod.values,
where="post",
linewidth=0.9,
linestyle="--",
color="orange",
alpha=0.8,
label="Daily close (step)",
)
ax.scatter(eod.index, eod.values, s=25, zorder=5, label="End-of-Day (scatter)")
ax.axhline(
100,
linestyle=":",
linewidth=1.0,
color="black",
alpha=0.5,
label="Initial price (axhline)",
)
ax.set_ylabel("Price")
ax.legend(fontsize=7, loc="upper left")
ax.set_zorder(ax_vol.get_zorder() + 1)
ax.patch.set_visible(False)
# --- Calendar axis ---
_draw(ax1, ax1_vol, busday=False)
ax1.set_title("Calendar Time (scale='linear')")
ax1.xaxis.set_major_locator(mdates.DayLocator())
ax1.xaxis.set_major_formatter(mdates.DateFormatter("%a %d %b"))
ax1.tick_params(axis="x", rotation=90)
for d in full_days:
if d.weekday() == 5:
ax1.axvspan(d, d + pd.Timedelta(days=2), color="grey", alpha=0.15, linewidth=0)
# --- Business axis ---
_draw(ax2, ax2_vol, busday=True)
ax2.set_xscale("busday")
ax2.set_title("Business Time (scale='busday')")
ax2.xaxis.set_major_locator(busdayaxis.DayLocator())
ax2.xaxis.set_major_formatter(mdates.DateFormatter("%a %d %b"))
ax2.tick_params(axis="x", rotation=90)
for d in full_days:
if d.weekday() == 5:
ax2.axvline(d, linestyle="--", linewidth=0.8, alpha=0.6)
ax2.axvline(d + pd.Timedelta(days=2), linestyle="--", linewidth=0.8, alpha=0.6)
_ = plt.tight_layout(rect=[0, 0, 1, 0.96])
Total running time of the script: ( 0 minutes 0.547 seconds)
Download Python source code: plot_6_artists.py