Matplotlib with market-microstructure sanity: gap compression, LOD decimation, and trader-style interaction.
Project description
TradeAxis
Matplotlib with market-microstructure sanity.
TradeAxis is a thin wrapper that composes gap-compression, level-of-detail decimation, and trader-style mouse interaction onto a standard matplotlib Axes. No subclassing, no new API to learn -- your ax stays a normal ax.
Features
- Gap compression -- Overnight/weekend gaps are detected automatically and removed. Session breaks are marked with clean dashed separators.
- LOD decimation -- Millions of points render instantly. On zoom, the visible data is refined to full resolution. Min/max bucket downsampling ensures spikes are never hidden.
- Trader-style interaction -- Left-drag to pan, right-drag to zoom, middle-click for a snap-to-data crosshair with datetime + value annotation. Press Escape to dismiss.
Installation
pip install tradeaxis
Or with Poetry (from source):
git clone https://github.com/youruser/tradeaxis.git
cd tradeaxis
poetry install
Quick start
import matplotlib.pyplot as plt
import numpy as np
import tradeaxis as ta
# Generate synthetic multi-day 1-min data
def make_day(date):
base = np.datetime64(date)
start = base + np.timedelta64(9 * 60 + 30, "m")
end = base + np.timedelta64(16 * 60, "m")
return np.arange(start, end, np.timedelta64(1, "m"))
timestamps = np.concatenate([make_day("2026-01-05"), make_day("2026-01-06")])
prices = 150 + np.cumsum(np.random.randn(len(timestamps)) * 0.02)
# One line to enhance the axes
fig, ax = plt.subplots(figsize=(14, 5))
chart = ta.wrap(ax, timestamps, prices)
# Everything below is normal matplotlib
ax.set_title("AAPL 1-min")
ax.set_ylabel("Price ($)")
ax.axhline(prices.mean(), color="red", ls="--", alpha=0.5, label="Mean")
ax.legend()
plt.show()
Interaction controls
| Action | Effect |
|---|---|
| Left-drag | Pan along the x-axis |
| Right-drag up | Zoom in (centered at cursor) |
| Right-drag down | Zoom out (centered at cursor) |
| Middle-click | Snap crosshair to nearest data point, show datetime + value |
| Escape | Dismiss crosshair |
API reference
tradeaxis.wrap(ax, timestamps, values, *, gap="auto", show_separators=True, interaction=True)
Enhance a standard matplotlib Axes with gap compression, LOD decimation, and interaction.
Parameters:
| Name | Type | Default | Description |
|---|---|---|---|
ax |
matplotlib.axes.Axes |
-- | The axes to enhance. |
timestamps |
array[datetime64] |
-- | Sorted timestamps. |
values |
array[float] |
-- | Y-values (prices, volumes, etc.). |
gap |
"auto" / timedelta / callable |
"auto" |
Gap detection strategy. "auto" uses median heuristic. A timedelta sets a fixed threshold. A callable receives the timestamps array and returns a boolean mask. |
show_separators |
bool |
True |
Draw dashed vertical lines at session breaks. |
interaction |
bool |
True |
Enable pan/zoom/crosshair mouse handling. |
Returns: Chart -- a handle for the enhanced chart. The ax is still a normal matplotlib Axes.
tradeaxis.plot_intraday(timestamps, values, *, gap="auto", show_separators=True, interaction=True, figsize=(14, 6))
Convenience function that creates a new figure + axes and calls wrap().
Chart methods
| Method | Description |
|---|---|
chart.to_index(datetime) |
Convert a datetime to compressed index space (for adding custom annotations). |
chart.to_datetime(idx) |
Convert a compressed index back to a datetime. |
chart.detach() |
Remove all TradeAxis artifacts and restore the axes to vanilla matplotlib. |
Working with compressed x-axis
The x-axis uses a compressed integer index, not wall-clock time. This means distances are proportional to number of data points, not elapsed time. When adding custom annotations:
# Convert a datetime to index space, then annotate normally
idx = chart.to_index(np.datetime64("2026-01-05T12:00"))
ax.axvline(x=idx, color="green", ls=":")
How it works
-
TimeMapping detects gaps in the timestamp sequence (via median-heuristic or user-specified threshold) and remaps datetimes to a contiguous integer index. A
FuncFormattermaps these integers back to readable time labels on the x-axis. -
LODRenderer decimates large datasets using min/max bucket downsampling. For each bucket of points, only the minimum and maximum values are kept (interleaved in x-order), preserving spikes. An LRU cache avoids recomputation during repeated pans.
-
InteractionController implements a simple state machine (
IDLE -> PAN / ZOOM) over matplotlib canvas events. The crosshair snaps to the nearest data point via binary search.
All of this is composed onto a user-owned Axes via callbacks and artist injection -- no subclassing, no monkey-patching.
Development
poetry install
poetry run pytest -v
Examples notebook:
poetry run jupyter notebook examples/showcase.ipynb
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tradeaxis-0.1.1.tar.gz.
File metadata
- Download URL: tradeaxis-0.1.1.tar.gz
- Upload date:
- Size: 10.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.3 CPython/3.11.9 Darwin/25.3.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
22585d9bd6a30c4cdd47d2544eecb4915f1604f4bfd12137273063307f20ab11
|
|
| MD5 |
b09177b5726992a88565ff02f33ccee8
|
|
| BLAKE2b-256 |
4ba31690eabb31bd281a11ad2031c1de1ec43b0f7f449c53896d03bb8bf484be
|
File details
Details for the file tradeaxis-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tradeaxis-0.1.1-py3-none-any.whl
- Upload date:
- Size: 11.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/2.3.3 CPython/3.11.9 Darwin/25.3.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
740504dc4cd3e70251bc1f4e28d92838a5df84288be2b8373a9949d687285c77
|
|
| MD5 |
fc7f104ec31b8a0c5c6eb5bafe305774
|
|
| BLAKE2b-256 |
445df0282c4fd5ac7d87687749b8a28fd2d3678bafe823d064e685c9dc924c57
|