Full documentation: https://muicharts.2plot.dev — the dedicated dash-mui-charts documentation site, with the complete API reference and deeper examples. This page is the quick-start overview.
MUI Charts
dash-mui-charts brings MUI X to Plotly Dash — 13 components as of 1.4.0: nine chart types, three tree views and a TimeClock, with an optional Pro tier for zoom, pan, brush and heatmaps.
This page is the catalogue summary with a taste of the charts; the full interactive documentation — 40+ pages with ~250 live examples covering every component and prop — lives at muicharts.2plot.dev.
| Component | License | Key Features |
|---|---|---|
| LineChart | Free + Pro | Line/area, stacking, zoom/pan (Pro), brush (Pro), reference lines, dateFormat (1.1.0) |
| BarChart | Free + Pro | Vertical/horizontal, stacked/normalized/diverging offsets, dataset mode, zoom + toolbar (Pro) |
| CandlestickChart | Free | OHLC candles, volume overlay, support/resistance reference lines, click events |
| PieChart | Free | Pie, donut, nested/concentric, gauge-arc, arc labels |
| ScatterChart | Free | Multi-series, z-axis color mapping, voronoi interaction |
| CompositeChart | Free + Pro | Layer scatter + line, zoom/pan (Pro), biaxial axes |
| Heatmap | Pro | Matrix visualization, continuous/piecewise color scales |
| SparklineChart | Free | Compact inline charts for KPI cards and tables |
| LiveTradingChart | Free + Pro | Real-time OHLCV candlestick streaming, volume, forecast, alerts |
| TreeView | Free | Data-driven items, expansion/selection control, checkbox propagation, inline label editing |
| SimpleTreeView | Free | Declarative tree built from children — dogfooded as the docs site's own sidebar |
| TreeViewPro | Pro | Kebab submenus, dividers and per-node menus (1.4.0) |
| TimeClock | Free | Clock-face time picking, controlled/uncontrolled, 12h/24h, pairs with DMC time inputs |
Installation
pip install dash-mui-charts
For Pro features, set your license key in .env:
MUI_PRO_API_KEY=your-license-key-here
Line Charts
# File: docs/dash_mui_charts/line_basic.py
import os
import random
import math
import dash_mantine_components as dmc
from dash_mui_charts import LineChart
MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
random.seed(42)
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
revenue = [random.randint(30, 90) for _ in months]
expenses = [random.randint(20, 65) for _ in months]
profit = [r - e for r, e in zip(revenue, expenses)]
temp_data = [round(18 + 12 * math.sin(2 * math.pi * i / 12 - math.pi / 2) + random.uniform(-2, 2), 1) for i in range(12)]
# Extra datasets
random.seed(77)
organic = [random.randint(10, 30) for _ in months]
paid = [random.randint(8, 25) for _ in months]
referral = [random.randint(5, 15) for _ in months]
# Curve comparison data
random.seed(11)
curve_data = [random.randint(20, 80) for _ in range(8)]
curve_labels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Mon']
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
component = dmc.Stack([
dmc.Text("Line Charts", fw=700, size="xl"),
dmc.Text("Versatile line and area charts with stacking, curves, reference lines, and biaxial axes.", size="sm", c="dimmed"),
# --- Multi-Series ---
dmc.Paper(
dmc.Stack([
dmc.Text("Multi-Series Line Chart", fw=600),
dmc.Text("Revenue vs expenses with profit area fill.", size="sm", c="dimmed"),
LineChart(
id="mc-line-basic",
licenseKey=MUI_KEY,
series=[
{'data': revenue, 'label': 'Revenue ($k)', 'color': '#1976d2', 'curve': 'monotoneX', 'showMark': True},
{'data': expenses, 'label': 'Expenses ($k)', 'color': '#f57c00', 'curve': 'monotoneX', 'showMark': True},
{'data': profit, 'label': 'Profit ($k)', 'color': '#388e3c', 'curve': 'monotoneX', 'area': True},
],
xAxis=[{'data': months, 'scaleType': 'point', 'label': 'Month'}],
yAxis=[{'label': 'Amount ($k)'}],
grid={'horizontal': True, 'vertical': False},
height=350,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
# --- Stacked Area ---
dmc.Paper(
dmc.Stack([
dmc.Text("Stacked Area Chart", fw=600),
dmc.Text("Stacked areas showing cumulative traffic sources.", size="sm", c="dimmed"),
LineChart(
id="mc-line-stacked",
licenseKey=MUI_KEY,
series=[
{'data': organic, 'label': 'Organic', 'color': '#66bb6a', 'area': True, 'stack': 'traffic', 'curve': 'monotoneX'},
{'data': paid, 'label': 'Paid', 'color': '#42a5f5', 'area': True, 'stack': 'traffic', 'curve': 'monotoneX'},
{'data': referral, 'label': 'Referral', 'color': '#ab47bc', 'area': True, 'stack': 'traffic', 'curve': 'monotoneX'},
],
xAxis=[{'data': months, 'scaleType': 'point'}],
yAxis=[{'label': 'Visitors (k)'}],
grid={'horizontal': True},
height=300,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
# --- Curve Interpolation Comparison ---
dmc.Paper(
dmc.Stack([
dmc.Text("Curve Interpolation", fw=600),
dmc.Text("Same data rendered with different curve algorithms.", size="sm", c="dimmed"),
dmc.SimpleGrid(cols={"base": 1, "md": 2}, children=[
dmc.Stack([
dmc.Badge("linear", color="blue", variant="light", size="sm"),
LineChart(
id="mc-line-curve-linear",
licenseKey=MUI_KEY,
series=[{'data': curve_data, 'label': 'Value', 'color': '#1976d2', 'curve': 'linear', 'showMark': True}],
xAxis=[{'data': curve_labels, 'scaleType': 'point'}],
height=180, grid={'horizontal': True},
),
], gap=4),
dmc.Stack([
dmc.Badge("monotoneX", color="green", variant="light", size="sm"),
LineChart(
id="mc-line-curve-mono",
licenseKey=MUI_KEY,
series=[{'data': curve_data, 'label': 'Value', 'color': '#388e3c', 'curve': 'monotoneX', 'showMark': True}],
xAxis=[{'data': curve_labels, 'scaleType': 'point'}],
height=180, grid={'horizontal': True},
),
], gap=4),
dmc.Stack([
dmc.Badge("natural", color="orange", variant="light", size="sm"),
LineChart(
id="mc-line-curve-natural",
licenseKey=MUI_KEY,
series=[{'data': curve_data, 'label': 'Value', 'color': '#f57c00', 'curve': 'natural', 'showMark': True}],
xAxis=[{'data': curve_labels, 'scaleType': 'point'}],
height=180, grid={'horizontal': True},
),
], gap=4),
dmc.Stack([
dmc.Badge("step", color="violet", variant="light", size="sm"),
LineChart(
id="mc-line-curve-step",
licenseKey=MUI_KEY,
series=[{'data': curve_data, 'label': 'Value', 'color': '#7b1fa2', 'curve': 'step', 'showMark': True}],
xAxis=[{'data': curve_labels, 'scaleType': 'point'}],
height=180, grid={'horizontal': True},
),
], gap=4),
]),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
# --- Reference Lines ---
dmc.Paper(
dmc.Stack([
dmc.Text("Reference Lines", fw=600),
dmc.Text("Temperature curve with comfort zone boundaries and a vertical launch marker.", size="sm", c="dimmed"),
LineChart(
id="mc-line-ref",
licenseKey=MUI_KEY,
series=[
{'data': temp_data, 'label': 'Temperature (°C)', 'color': '#ef5350', 'curve': 'natural', 'area': True},
],
xAxis=[{'data': months, 'scaleType': 'point', 'label': 'Month'}],
yAxis=[{'label': '°C', 'min': 0, 'max': 40}],
referenceLines=[
{'y': 25, 'label': 'Upper comfort', 'lineStyle': {'stroke': '#ff9800', 'strokeDasharray': '5 3'}},
{'y': 18, 'label': 'Lower comfort', 'lineStyle': {'stroke': '#2196f3', 'strokeDasharray': '5 3'}},
{'x': 'Jun', 'label': 'Summer', 'lineStyle': {'stroke': '#9e9e9e', 'strokeDasharray': '3 3'}},
],
grid={'horizontal': True},
height=320,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
# --- Biaxial ---
dmc.Paper(
dmc.Stack([
dmc.Text("Biaxial (Dual Y-Axis)", fw=600),
dmc.Text("Revenue on the left axis, growth rate on the right. Independent scales for different units.", size="sm", c="dimmed"),
LineChart(
id="mc-line-biaxial",
licenseKey=MUI_KEY,
series=[
{'data': revenue, 'label': 'Revenue ($k)', 'color': '#1976d2', 'curve': 'monotoneX', 'showMark': True, 'yAxisId': 'left'},
{'data': [round(random.uniform(-5, 20), 1) for _ in months], 'label': 'Growth (%)', 'color': '#ef5350', 'curve': 'monotoneX', 'area': True, 'yAxisId': 'right'},
],
xAxis=[{'data': months, 'scaleType': 'point'}],
yAxis=[
{'id': 'left', 'label': 'Revenue ($k)', 'position': 'left'},
{'id': 'right', 'label': 'Growth (%)', 'position': 'right'},
],
grid={'horizontal': True},
height=320,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
], gap="lg")
Line Charts — Pro Features
# File: docs/dash_mui_charts/line_pro.py
import os
import random
import math
from datetime import datetime, timedelta
import dash_mantine_components as dmc
from dash_mui_charts import LineChart
MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
random.seed(99)
# --- Signal vs Noise (100 points) ---
n = 100
x_vals = list(range(n))
signal = [round(50 + 20 * math.sin(2 * math.pi * i / 25) + random.gauss(0, 5), 1) for i in range(n)]
noise = [round(50 + random.gauss(0, 12), 1) for i in range(n)]
# --- Simulated 2-month hourly sensor data for slider preview ---
random.seed(42)
start_date = datetime(2025, 1, 1)
hours = 24 * 45 # 45 days
timestamps = []
temperature = []
humidity = []
for h in range(hours):
dt = start_date + timedelta(hours=h)
timestamps.append(int(dt.timestamp() * 1000))
day_of_year = dt.timetuple().tm_yday
hour = dt.hour
seasonal = 2.0 + 8.0 * math.sin(2 * math.pi * (day_of_year - 30) / 365)
daily_cycle = 4.0 * math.sin(2 * math.pi * (hour - 6) / 24)
temp = seasonal + daily_cycle + random.gauss(0, 1.2)
temperature.append(round(temp, 1))
hum_base = 65 - 1.5 * (temp - 5)
humidity.append(round(max(20, min(95, hum_base + random.gauss(0, 3))), 1))
# --- Brush data ---
random.seed(55)
brush_data = [random.randint(10, 90) for _ in range(50)]
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
component = dmc.Stack([
dmc.Text("Line Charts — Pro Features", fw=700, size="xl"),
dmc.Badge("Requires MUI Pro License", color="violet", variant="light", size="sm"),
# --- Zoom + Pan + Slider ---
dmc.Paper(
dmc.Stack([
dmc.Text("Zoom, Pan & Toolbar", fw=600),
dmc.Text("Scroll to zoom, drag to pan. Toolbar provides zoom in/out and export. 100 data points.", size="sm", c="dimmed"),
LineChart(
id="mc-line-zoom",
licenseKey=MUI_KEY,
series=[
{'data': signal, 'label': 'Signal', 'color': '#1976d2', 'curve': 'monotoneX'},
{'data': noise, 'label': 'Noise', 'color': '#ef5350', 'curve': 'linear', 'showMark': False},
],
xAxis=[{
'id': 'x-axis', 'data': x_vals, 'scaleType': 'linear', 'label': 'Sample Index',
'zoom': {'minSpan': 10, 'maxSpan': 100, 'panning': True},
}],
yAxis=[{'label': 'Value', 'min': 0, 'max': 100}],
initialZoom=[{'axisId': 'x-axis', 'start': 0, 'end': 40}],
showSlider=True,
showToolbar=True,
grid={'horizontal': True},
height=400,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
# --- Zoom Slider with Preview (Time Series) ---
dmc.Paper(
dmc.Stack([
dmc.Text("Zoom Slider with Preview", fw=600),
dmc.Group([
dmc.Text("45 days of simulated hourly sensor data. The slider shows a miniature preview of the full dataset.", size="sm", c="dimmed"),
dmc.Badge("slider.preview", color="teal", variant="light", size="xs"),
]),
LineChart(
id="mc-line-slider-preview",
licenseKey=MUI_KEY,
height=420,
series=[{
'id': 'temperature',
'data': temperature,
'label': 'Temperature (°C)',
'color': '#ef5350',
'showMark': False,
'area': True,
'curve': 'natural',
}],
xAxis=[{
'id': 'time-axis',
'data': timestamps,
'scaleType': 'time',
'label': 'Date',
'tickMinStep': 3600 * 1000 * 24,
'tickLabelStyle': {'angle': 35, 'fontSize': 11, 'textAnchor': 'start'},
'height': 50,
'zoom': {
'minSpan': 2,
'panning': True,
'filterMode': 'discard',
'slider': {'enabled': True, 'preview': True},
},
}],
yAxis=[{'label': '°C', 'width': 55, 'domainLimit': 'nice'}],
grid={'horizontal': True},
margin={'left': 65, 'right': 20, 'top': 20, 'bottom': 70},
initialZoom=[{'axisId': 'time-axis', 'start': 0, 'end': 25}],
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
# --- Biaxial with Slider Preview ---
dmc.Paper(
dmc.Stack([
dmc.Text("Biaxial Chart with Slider Preview", fw=600),
dmc.Text("Temperature and humidity on dual axes. filterMode='discard' auto-adjusts y-axis to visible range.", size="sm", c="dimmed"),
LineChart(
id="mc-line-biaxial-preview",
licenseKey=MUI_KEY,
height=420,
series=[
{'id': 'temp', 'data': temperature, 'label': 'Temperature (°C)', 'color': '#ef5350', 'showMark': False, 'curve': 'natural', 'yAxisId': 'temp-axis'},
{'id': 'hum', 'data': humidity, 'label': 'Humidity (%)', 'color': '#42a5f5', 'showMark': False, 'curve': 'natural', 'yAxisId': 'hum-axis'},
],
xAxis=[{
'id': 'biaxial-time',
'data': timestamps,
'scaleType': 'time',
'tickMinStep': 3600 * 1000 * 24,
'tickLabelStyle': {'angle': 35, 'fontSize': 11, 'textAnchor': 'start'},
'height': 50,
'zoom': {
'minSpan': 2, 'panning': True, 'filterMode': 'discard',
'slider': {'enabled': True, 'preview': True},
},
}],
yAxis=[
{'id': 'temp-axis', 'label': '°C', 'position': 'left', 'width': 50, 'domainLimit': 'nice', 'labelStyle': {'fill': '#ef5350'}},
{'id': 'hum-axis', 'label': '%', 'position': 'right', 'width': 50, 'domainLimit': 'nice', 'labelStyle': {'fill': '#42a5f5'}},
],
grid={'horizontal': True},
margin={'left': 60, 'right': 60, 'top': 20, 'bottom': 70},
initialZoom=[{'axisId': 'biaxial-time', 'start': 30, 'end': 60}],
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
# --- Brush Selection ---
dmc.Paper(
dmc.Stack([
dmc.Text("Brush Selection", fw=600),
dmc.Text("Click and drag to select a range. The brush overlay shows selected values.", size="sm", c="dimmed"),
LineChart(
id="mc-line-brush",
licenseKey=MUI_KEY,
series=[
{'data': brush_data, 'label': 'Metric A', 'color': '#7b1fa2', 'curve': 'monotoneX'},
],
xAxis=[{'data': list(range(50)), 'scaleType': 'linear', 'label': 'Day'}],
brushConfig={'enabled': True, 'preventTooltip': True},
brushOverlay='values',
grid={'horizontal': True},
height=300,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
# --- Highlighting ---
dmc.Paper(
dmc.Stack([
dmc.Text("Item & Axis Highlighting", fw=600),
dmc.Text("Hover to see per-series highlight scoping — the active series stays vivid while others fade.", size="sm", c="dimmed"),
LineChart(
id="mc-line-highlight",
licenseKey=MUI_KEY,
series=[
{'data': [random.randint(100, 250) for _ in range(12)], 'label': 'Sales 2023', 'color': '#1976d2', 'curve': 'monotoneX', 'showMark': True,
'highlightScope': {'highlight': 'series', 'fade': 'global'}},
{'data': [random.randint(120, 280) for _ in range(12)], 'label': 'Sales 2024', 'color': '#388e3c', 'curve': 'monotoneX', 'showMark': True,
'highlightScope': {'highlight': 'series', 'fade': 'global'}},
{'data': [random.randint(80, 200) for _ in range(12)], 'label': 'Sales 2025', 'color': '#f57c00', 'curve': 'monotoneX', 'showMark': True,
'highlightScope': {'highlight': 'series', 'fade': 'global'}},
],
xAxis=[{'data': ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 'scaleType': 'point'}],
yAxis=[{'label': 'Revenue ($k)'}],
axisHighlight={'x': 'band', 'y': 'line'},
grid={'horizontal': True},
height=350,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
], gap="lg")
Tick Configuration & Date Formatting
New in 1.1.0 — dateFormat and dateTickFormat let you format time-scale axes directly from Python without writing JavaScript. Use scaleType: 'time' with epoch-ms timestamps, control tick density with tickMinStep, and rotate labels with tickLabelStyle.
| Prop | Description |
|---|---|
dateFormat | Format string for tooltip labels (e.g. "MMM d, YYYY") |
dateTickFormat | Format string for axis tick labels (e.g. "M/d") |
tickMinStep | Minimum ms between ticks — use 86400000 * 7 for weekly ticks |
tickNumber | Approximate target tick count (D3 rounds for readability) |
tickLabelStyle | {angle, fontSize, textAnchor} for rotated labels |
Format tokens: YYYY (2025), MMM (Jan), MM (01), M (1), dd (01), d (1), HH (00–23), mm (00–59)
# File: docs/dash_mui_charts/tick_hover.py
"""Tick Configuration & Date Formatting — v1.1.0 dateFormat/dateTickFormat."""
import os
import math
import random
import json
from datetime import datetime, timedelta
import dash_mantine_components as dmc
from dash import html, callback, Input, Output
from dash_mui_charts import LineChart
MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
# ---------------------------------------------------------------------------
# Data — 90-day sensor readings, two sensors
# ---------------------------------------------------------------------------
random.seed(42)
def _make_sensor(start, n, base, noise, trend):
vals = []
for i in range(n):
vals.append(round(base + trend * i + noise * math.sin(i * 0.3) + random.gauss(0, noise * 0.3), 1))
return vals
_start = datetime(2025, 1, 1)
_n = 90
# Epoch-ms timestamps — required for scaleType: 'time'
timestamps = [
int((_start + timedelta(days=i)).timestamp()) * 1000
for i in range(_n)
]
sensor_a = _make_sensor(_start, _n, base=68, noise=5, trend=0.04)
sensor_b = _make_sensor(_start, _n, base=55, noise=3.5, trend=0.02)
WARNING_THRESHOLD = 78
# Day-45 event marker (epoch-ms)
event_ts = int((_start + timedelta(days=45)).timestamp()) * 1000
# ---------------------------------------------------------------------------
# Chart 1 — dateFormat / dateTickFormat (v1.1.0) + reference lines
# ---------------------------------------------------------------------------
chart_time_scale = dmc.Paper(dmc.Stack([
dmc.Group([
dmc.Text("Time-Scale Axis (v1.1.0 dateFormat)", fw=600),
dmc.Badge("1.1.0", color="teal", variant="light", size="sm"),
]),
dmc.Text(
"Use scaleType: 'time' with epoch-ms timestamps. "
"dateFormat controls the tooltip label; dateTickFormat controls axis tick labels. "
"tickMinStep limits density; tickLabelStyle.angle rotates labels for readability.",
size="sm", c="dimmed",
),
LineChart(
id="mc-th-time",
licenseKey=MUI_KEY,
height=340,
series=[
{
"data": sensor_a,
"label": "Sensor A (°F)",
"color": "#1976d2",
"curve": "monotoneX",
"showMark": False,
"highlightScope": {"highlight": "series", "fade": "global"},
},
{
"data": sensor_b,
"label": "Sensor B (°F)",
"color": "#7b1fa2",
"curve": "monotoneX",
"showMark": False,
"highlightScope": {"highlight": "series", "fade": "global"},
},
],
xAxis=[{
"id": "mc-th-x",
"data": timestamps,
"scaleType": "time",
# v1.1.0 built-in date formatting — no JavaScript needed
"dateFormat": "MMM d, YYYY", # tooltip label
"dateTickFormat": "M/d", # axis tick labels
"tickMinStep": 86400 * 1000 * 7, # min 1 week between ticks
"tickNumber": 10,
"tickLabelStyle": {"angle": 35, "fontSize": 11, "textAnchor": "start"},
"height": 70, # extra space for angled labels
}],
yAxis=[{"label": "Temperature (°F)", "min": 48, "max": 85}],
grid={"horizontal": True},
axisHighlight={"x": "line"},
tooltip={"trigger": "axis"},
margin={"left": 65, "right": 20, "top": 20, "bottom": 90},
referenceLines=[
{
"y": WARNING_THRESHOLD,
"label": "Warning (78°F)",
"lineStyle": {"stroke": "#ff9800", "strokeWidth": 2, "strokeDasharray": "5 5"},
"labelStyle": {"fill": "#ff9800", "fontWeight": "bold"},
"labelAlign": "end",
},
{
"x": event_ts,
"label": "Calibration",
"lineStyle": {"stroke": "#9c27b0", "strokeWidth": 2, "strokeDasharray": "4 4"},
"labelStyle": {"fill": "#9c27b0"},
"labelAlign": "start",
},
],
),
dmc.Alert(
dmc.Code(
'xAxis=[{"scaleType": "time", "data": epoch_ms, '
'"dateFormat": "MMM d, YYYY", "dateTickFormat": "M/d", '
'"tickMinStep": 86400_000 * 7}]'
),
color="blue", variant="light",
),
], gap="sm"), p="lg", radius="md", style=GLASS)
# ---------------------------------------------------------------------------
# Chart 2 — axis highlight + click event
# ---------------------------------------------------------------------------
chart_click = dmc.Paper(dmc.Stack([
dmc.Text("Axis Highlight & Click Events", fw=600),
dmc.Text(
"axisHighlight: {'x': 'line'} draws a crosshair; "
"tooltip: {'trigger': 'axis'} shows all series. "
"Click any point to capture data via the clickData output prop.",
size="sm", c="dimmed",
),
LineChart(
id="mc-th-click",
licenseKey=MUI_KEY,
height=300,
series=[
{
"data": sensor_a,
"label": "Sensor A",
"color": "#1976d2",
"curve": "monotoneX",
"showMark": True,
"highlightScope": {"highlight": "item", "fade": "global"},
},
{
"data": sensor_b,
"label": "Sensor B",
"color": "#7b1fa2",
"curve": "monotoneX",
"showMark": True,
"highlightScope": {"highlight": "item", "fade": "global"},
},
],
xAxis=[{
"data": timestamps,
"scaleType": "time",
"dateFormat": "MMM d",
"dateTickFormat": "M/d",
"tickMinStep": 86400 * 1000 * 14,
"tickNumber": 6,
}],
yAxis=[{"label": "°F"}],
grid={"horizontal": True},
axisHighlight={"x": "line", "y": "line"},
tooltip={"trigger": "item"},
margin={"left": 55, "right": 20, "top": 20, "bottom": 50},
),
dmc.Text("Click output:", size="xs", c="dimmed"),
html.Pre(
id="mc-th-click-out",
children="Click a point to see data...",
style={
"fontSize": "11px", "margin": 0,
"padding": "8px 12px", "borderRadius": "6px",
"background": "light-dark(#f8f9fa, #1a1b1e)",
"border": "1px solid light-dark(#dee2e6, #373a40)",
},
),
], gap="sm"), p="lg", radius="md", style=GLASS)
component = dmc.Stack([
dmc.Text("Tick Configuration & Date Formatting", fw=700, size="xl"),
dmc.Text(
"Best practices for time-scale axes — epoch-ms data, "
"built-in date formatting (v1.1.0), angled ticks, reference lines, and click events.",
size="sm", c="dimmed",
),
chart_time_scale,
chart_click,
], gap="lg")
# ---------------------------------------------------------------------------
# Callback — click event display
# ---------------------------------------------------------------------------
@callback(
Output("mc-th-click-out", "children"),
Input("mc-th-click", "clickData"),
prevent_initial_call=True,
)
def show_click(data):
if not data:
return "Click a point to see data..."
return json.dumps(data, indent=2)
Pie Charts
# File: docs/dash_mui_charts/pie_charts.py
import random
import dash_mantine_components as dmc
from dash_mui_charts import PieChart
random.seed(77)
PALETTE = ['#1976d2', '#388e3c', '#f57c00', '#d32f2f', '#7b1fa2', '#00838f', '#c62828', '#4527a0']
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
market_data = [
{'id': 0, 'value': 35, 'label': 'Chrome', 'color': '#4285f4'},
{'id': 1, 'value': 25, 'label': 'Safari', 'color': '#007aff'},
{'id': 2, 'value': 18, 'label': 'Firefox', 'color': '#ff7139'},
{'id': 3, 'value': 12, 'label': 'Edge', 'color': '#0078d7'},
{'id': 4, 'value': 10, 'label': 'Other', 'color': '#9e9e9e'},
]
sales_data = [
{'id': i, 'value': random.randint(15, 50), 'label': cat}
for i, cat in enumerate(['Electronics', 'Clothing', 'Food', 'Books', 'Toys'])
]
inner_data = [
{'id': 0, 'value': 60, 'label': 'Desktop', 'color': '#1565c0'},
{'id': 1, 'value': 35, 'label': 'Mobile', 'color': '#2e7d32'},
{'id': 2, 'value': 5, 'label': 'Tablet', 'color': '#e65100'},
]
outer_data = [
{'id': 0, 'value': 30, 'label': 'Windows', 'color': '#42a5f5'},
{'id': 1, 'value': 20, 'label': 'macOS', 'color': '#66bb6a'},
{'id': 2, 'value': 10, 'label': 'Linux', 'color': '#ef5350'},
{'id': 3, 'value': 20, 'label': 'iOS', 'color': '#ab47bc'},
{'id': 4, 'value': 15, 'label': 'Android', 'color': '#ffa726'},
{'id': 5, 'value': 5, 'label': 'iPadOS', 'color': '#26c6da'},
]
component = dmc.Stack([
dmc.Text("Pie Charts", fw=700, size="xl"),
dmc.Text("Pie, donut, nested, and gauge-arc variations.", size="sm", c="dimmed"),
dmc.SimpleGrid(cols={"base": 1, "md": 2}, children=[
dmc.Paper(
dmc.Stack([
dmc.Text("Pie Chart", fw=600),
dmc.Text("Browser market share with arc labels.", size="sm", c="dimmed"),
PieChart(id="mc-pie-basic", data=market_data, arcLabel='formattedValue', arcLabelMinAngle=25, colors=PALETTE, height=280),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
dmc.Paper(
dmc.Stack([
dmc.Text("Donut Chart", fw=600),
dmc.Text("Sales by category with inner radius cutout.", size="sm", c="dimmed"),
PieChart(id="mc-pie-donut", data=sales_data, innerRadius=60, cornerRadius=4, paddingAngle=2, colors=PALETTE, height=280),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
]),
dmc.Paper(
dmc.Stack([
dmc.Text("Nested Concentric Pie", fw=600),
dmc.Text("Inner ring: device type. Outer ring: OS breakdown.", size="sm", c="dimmed"),
PieChart(
id="mc-pie-nested",
series=[
{'data': inner_data, 'innerRadius': 0, 'outerRadius': 70, 'highlightScope': {'fade': 'global', 'highlight': 'item'}},
{'data': outer_data, 'innerRadius': 80, 'outerRadius': 110, 'highlightScope': {'fade': 'global', 'highlight': 'item'}},
],
height=300,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
dmc.Paper(
dmc.Stack([
dmc.Text("Gauge-Style Arc", fw=600),
dmc.Text("Half-circle gauge using startAngle/endAngle.", size="sm", c="dimmed"),
dmc.SimpleGrid(cols=2, children=[
PieChart(
id=f"mc-pie-gauge-{i}",
data=[
{'id': 0, 'value': val, 'label': label, 'color': color},
{'id': 1, 'value': 100 - val, 'color': 'light-dark(#e0e0e0, #424242)'},
],
startAngle=-90, endAngle=90, innerRadius=50, outerRadius=80, arcLabel='value', height=160,
)
for i, (val, label, color) in enumerate([
(72, 'CPU', '#1976d2'), (88, 'Disk', '#f57c00'),
])
]),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
], gap="lg")
Scatter Charts
# File: docs/dash_mui_charts/scatter_charts.py
import random
import dash_mantine_components as dmc
from dash_mui_charts import ScatterChart
random.seed(55)
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
def make_cluster(cx, cy, n=30, spread=15):
return [{'x': round(cx + random.gauss(0, spread), 1), 'y': round(cy + random.gauss(0, spread), 1), 'id': i} for i in range(n)]
cluster_a = make_cluster(150, 200, 40)
cluster_b = make_cluster(300, 150, 35)
cluster_c = make_cluster(220, 350, 30)
z_data = [{'x': round(random.uniform(0, 100), 1), 'y': round(random.uniform(0, 100), 1), 'z': round(random.uniform(0, 100), 1), 'id': i} for i in range(60)]
component = dmc.Stack([
dmc.Text("Scatter Charts", fw=700, size="xl"),
dmc.Text("Multi-series scatter plots with clustering, z-axis coloring, and voronoi interaction.", size="sm", c="dimmed"),
dmc.Paper(
dmc.Stack([
dmc.Text("Multi-Series Clusters", fw=600),
dmc.Text("Three distinct clusters with voronoi hover detection.", size="sm", c="dimmed"),
ScatterChart(
id="mc-scatter-clusters",
series=[
{'id': 'cluster-a', 'label': 'Group A', 'data': cluster_a, 'color': '#1976d2', 'markerSize': 5},
{'id': 'cluster-b', 'label': 'Group B', 'data': cluster_b, 'color': '#388e3c', 'markerSize': 5},
{'id': 'cluster-c', 'label': 'Group C', 'data': cluster_c, 'color': '#f57c00', 'markerSize': 5},
],
xAxis=[{'label': 'Feature X', 'min': 50, 'max': 400}],
yAxis=[{'label': 'Feature Y', 'min': 50, 'max': 450}],
voronoiMaxRadius=40,
grid={'horizontal': True, 'vertical': True},
height=380,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
dmc.Paper(
dmc.Stack([
dmc.Text("Z-Axis Color Mapping", fw=600),
dmc.Text("Third dimension mapped to a continuous color scale.", size="sm", c="dimmed"),
ScatterChart(
id="mc-scatter-zaxis",
series=[
{'id': 'z-series', 'label': 'Intensity', 'data': z_data, 'markerSize': 7, 'highlightScope': {'highlight': 'item', 'fade': 'global'}},
],
zAxis=[{'colorMap': {'type': 'continuous', 'min': 0, 'max': 100, 'color': ['#e3f2fd', '#1565c0']}}],
xAxis=[{'label': 'X', 'min': -5, 'max': 105}],
yAxis=[{'label': 'Y', 'min': -5, 'max': 105}],
voronoiMaxRadius=25,
grid={'horizontal': True, 'vertical': True},
height=380,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
], gap="lg")
Composite Charts
# File: docs/dash_mui_charts/composite_charts.py
import os
import random
import math
import dash_mantine_components as dmc
from dash_mui_charts import CompositeChart
MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
random.seed(33)
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
n = 50
scatter_data = [{'x': i, 'y': round(20 + 0.4 * i + random.gauss(0, 4), 1), 'id': i} for i in range(n)]
trend_line = [round(20 + 0.4 * i, 1) for i in range(n)]
component = dmc.Stack([
dmc.Text("Composite Charts", fw=700, size="xl"),
dmc.Text("Layer scatter points and line series on a single chart surface.", size="sm", c="dimmed"),
dmc.Paper(
dmc.Stack([
dmc.Text("Scatter + Trend Line", fw=600),
dmc.Text("Sensor readings with a linear regression overlay and reference limits.", size="sm", c="dimmed"),
CompositeChart(
id="mc-composite-trend", licenseKey=MUI_KEY,
series=[
{'type': 'scatter', 'id': 'readings', 'label': 'Readings', 'data': scatter_data, 'markerSize': 4, 'color': '#1976d2'},
{'type': 'line', 'id': 'trend', 'label': 'Trend', 'data': trend_line, 'curve': 'linear', 'showMark': False, 'color': '#ff7043'},
],
xAxis=[{'id': 'x', 'data': list(range(n)), 'scaleType': 'linear', 'label': 'Sample', 'zoom': {'minSpan': 10, 'panning': True}}],
yAxis=[{'label': 'Value'}],
referenceLines=[
{'y': 35, 'label': 'Upper limit', 'lineStyle': {'stroke': '#d32f2f', 'strokeDasharray': '6 3'}},
{'y': 15, 'label': 'Lower limit', 'lineStyle': {'stroke': '#1565c0', 'strokeDasharray': '6 3'}},
],
initialZoom=[{'axisId': 'x', 'start': 0, 'end': 60}],
showSlider=True, voronoiMaxRadius=20,
grid={'horizontal': True},
height=400,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
dmc.Paper(
dmc.Stack([
dmc.Text("Biaxial Composite", fw=600),
dmc.Text("Temperature scatter on left axis, humidity line on right axis.", size="sm", c="dimmed"),
CompositeChart(
id="mc-composite-biaxial", licenseKey=MUI_KEY,
series=[
{'type': 'scatter', 'id': 'temp', 'label': 'Temperature (°C)',
'data': [{'x': i, 'y': round(22 + random.gauss(0, 3), 1), 'id': i} for i in range(24)],
'markerSize': 5, 'color': '#ef5350', 'yAxisId': 'left'},
{'type': 'line', 'id': 'humidity', 'label': 'Humidity (%)',
'data': [round(60 + 15 * math.sin(2 * math.pi * i / 24) + random.uniform(-3, 3), 1) for i in range(24)],
'curve': 'natural', 'showMark': False, 'color': '#42a5f5', 'area': True, 'yAxisId': 'right'},
],
xAxis=[{'data': list(range(24)), 'scaleType': 'linear', 'label': 'Hour'}],
yAxis=[{'id': 'left', 'label': '°C', 'position': 'left'}, {'id': 'right', 'label': '%', 'position': 'right'}],
grid={'horizontal': True},
height=350,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
], gap="lg")
Heatmap Charts
# File: docs/dash_mui_charts/heatmap_charts.py
import os
import random
import dash_mantine_components as dmc
from dash_mui_charts import Heatmap
MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
random.seed(88)
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
hours = [f'{h}:00' for h in range(6, 22)]
activity_data = []
for xi, day in enumerate(days):
for yi, hour in enumerate(hours):
base = 30 if xi < 5 and 3 <= yi <= 10 else 10
val = max(0, min(100, base + random.randint(-10, 40)))
activity_data.append([xi, yi, val])
labels = ['Revenue', 'Traffic', 'Conversions', 'Ad Spend', 'Satisfaction']
corr_data = []
for i in range(len(labels)):
for j in range(len(labels)):
val = 1.0 if i == j else round(random.uniform(-0.3, 0.95), 2)
corr_data.append([i, j, val])
component = dmc.Stack([
dmc.Text("Heatmap Charts", fw=700, size="xl"),
dmc.Group([
dmc.Text("Matrix visualization with continuous and piecewise color scales.", size="sm", c="dimmed"),
dmc.Badge("Pro Feature", color="violet", variant="light", size="sm"),
]),
dmc.Paper(
dmc.Stack([
dmc.Text("Website Activity Heatmap", fw=600),
dmc.Text("Visitor activity by day and hour. Darker = more active.", size="sm", c="dimmed"),
Heatmap(
id="mc-heatmap-activity", licenseKey=MUI_KEY,
data=activity_data,
xAxis={'data': days, 'label': 'Day'},
yAxis={'data': hours, 'label': 'Hour'},
colorScale={'type': 'continuous', 'min': 0, 'max': 100, 'colors': ['#e3f2fd', '#1565c0']},
height=400,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
dmc.Paper(
dmc.Stack([
dmc.Text("Correlation Matrix", fw=600),
dmc.Text("Business metric correlations with diverging color scale.", size="sm", c="dimmed"),
Heatmap(
id="mc-heatmap-corr", licenseKey=MUI_KEY,
data=corr_data,
xAxis={'data': labels},
yAxis={'data': labels},
colorScale={'type': 'piecewise', 'thresholds': [-0.5, 0, 0.5], 'colors': ['#d32f2f', '#ffcdd2', '#c8e6c9', '#2e7d32']},
height=350,
),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
], gap="lg")
Sparkline Charts
# File: docs/dash_mui_charts/sparkline_charts.py
import random
import dash_mantine_components as dmc
from dash_mui_charts import SparklineChart
random.seed(66)
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
def spark_data(n=12, base=50, spread=20):
return [max(0, round(base + random.uniform(-spread, spread), 1)) for _ in range(n)]
revenue_spark = spark_data(12, 60, 15)
users_spark = spark_data(12, 200, 50)
errors_spark = [random.randint(0, 8) for _ in range(12)]
cpu_spark = spark_data(20, 45, 25)
component = dmc.Stack([
dmc.Text("Sparkline Charts", fw=700, size="xl"),
dmc.Text("Compact inline charts for dashboards, tables, and KPI cards.", size="sm", c="dimmed"),
dmc.SimpleGrid(cols={"base": 1, "sm": 2, "md": 4}, children=[
dmc.Paper(
dmc.Stack([
dmc.Text("Revenue", size="sm", c="dimmed"),
dmc.Group([dmc.Text(f"${sum(revenue_spark):.0f}k", fw=700, size="xl"), dmc.Badge("+12%", color="green", variant="light", size="sm")], justify="space-between"),
SparklineChart(id="mc-spark-revenue", data=revenue_spark, plotType='line', color='#1976d2', area=True, curve='monotoneX', height=40, showTooltip=True),
], gap="xs"),
p="md", radius="md", style=GLASS,
),
dmc.Paper(
dmc.Stack([
dmc.Text("Active Users", size="sm", c="dimmed"),
dmc.Group([dmc.Text(f"{users_spark[-1]:.0f}", fw=700, size="xl"), dmc.Badge("+8%", color="green", variant="light", size="sm")], justify="space-between"),
SparklineChart(id="mc-spark-users", data=users_spark, plotType='line', color='#388e3c', area=True, curve='monotoneX', height=40, showTooltip=True),
], gap="xs"),
p="md", radius="md", style=GLASS,
),
dmc.Paper(
dmc.Stack([
dmc.Text("Errors", size="sm", c="dimmed"),
dmc.Group([dmc.Text(f"{errors_spark[-1]}", fw=700, size="xl"), dmc.Badge("-3%", color="red", variant="light", size="sm")], justify="space-between"),
SparklineChart(id="mc-spark-errors", data=errors_spark, plotType='bar', color='#ef5350', height=40, showTooltip=True),
], gap="xs"),
p="md", radius="md", style=GLASS,
),
dmc.Paper(
dmc.Stack([
dmc.Text("CPU Load", size="sm", c="dimmed"),
dmc.Group([dmc.Text(f"{cpu_spark[-1]:.0f}%", fw=700, size="xl"), dmc.Badge("stable", color="gray", variant="light", size="sm")], justify="space-between"),
SparklineChart(id="mc-spark-cpu", data=cpu_spark, plotType='line', color='#f57c00', curve='linear', height=40, showTooltip=True),
], gap="xs"),
p="md", radius="md", style=GLASS,
),
]),
dmc.Paper(
dmc.Stack([
dmc.Text("Sparkline Variations", fw=600),
dmc.Text("Different plot types, curves, and configurations.", size="sm", c="dimmed"),
dmc.SimpleGrid(cols=3, children=[
dmc.Stack([
dmc.Text("Line (monotoneX)", size="xs", c="dimmed", ta="center"),
SparklineChart(id="mc-spark-v1", data=spark_data(), plotType='line', color='#1976d2', curve='monotoneX', height=60, area=True, showTooltip=True),
], gap="xs"),
dmc.Stack([
dmc.Text("Line (step)", size="xs", c="dimmed", ta="center"),
SparklineChart(id="mc-spark-v2", data=spark_data(), plotType='line', color='#7b1fa2', curve='step', height=60, showTooltip=True),
], gap="xs"),
dmc.Stack([
dmc.Text("Bar", size="xs", c="dimmed", ta="center"),
SparklineChart(id="mc-spark-v3", data=spark_data(8, 30, 20), plotType='bar', color='#388e3c', height=60, showTooltip=True),
], gap="xs"),
]),
], gap="sm"),
p="lg", radius="md", style=GLASS,
),
], gap="lg")
Live Trading Chart
Real-time OHLCV candlestick streaming with volume bars, a forecast line with uncertainty bands, and automatic swing-point alert labels. All simulation parameters are controllable at runtime.
| Feature | Description |
|---|---|
| Candlesticks | OHLCV bars with green/red coloring |
| Volume | Optional volume histogram (bottom panel) |
| Forecast | Forward projection with uncertainty shading |
| Alerts | Configurable via alertProbability (chance per tick) and alertThresholdPct (minimum % move) |
| Alert colors | alertUpColor / alertDownColor for label styling |
| Zoom / Slider | Pro — range slider preview |
# File: docs/dash_mui_charts/live_trading.py
"""Live Trading Chart — real-time OHLCV candlestick simulation with swing-point alerts."""
import os
import dash_mantine_components as dmc
from dash import html, callback, Input, Output, State, ctx
from dash_mui_charts import LiveTradingChart
MUI_KEY = os.getenv('MUI_PRO_API_KEY', '')
GLASS = {
"background": "light-dark(rgba(255,255,255,0.55), rgba(30,30,30,0.55))",
"backdropFilter": "blur(16px) saturate(1.8)",
"WebkitBackdropFilter": "blur(16px) saturate(1.8)",
"border": "1px solid light-dark(rgba(255,255,255,0.5), rgba(255,255,255,0.08))",
}
component = dmc.Stack([
dmc.Text("Live Trading Chart", fw=700, size="xl"),
dmc.Text(
"Real-time candlestick simulation with OHLCV data, volume bars, "
"forecast line with uncertainty bands, and swing-point alert labels. "
"Alerts only fire at confirmed local highs and lows — not on every candle.",
size="sm", c="dimmed",
),
dmc.Paper(dmc.Stack([
# ── Controls Row 1: Buttons + Toggles ─────────────────────────────
dmc.Group([
dmc.Group([
dmc.Button("Start", id="mc-lt-start", color="green", size="sm"),
dmc.Button("Stop", id="mc-lt-stop", color="yellow", size="sm", variant="outline"),
dmc.Button("Reset", id="mc-lt-reset", color="red", size="sm", variant="outline"),
], gap="xs"),
dmc.Group([
dmc.Switch(id="mc-lt-volume", label="Volume", checked=True, size="sm"),
dmc.Switch(id="mc-lt-labels", label="Price Labels", checked=False, size="sm"),
dmc.Switch(id="mc-lt-slider", label="Zoom Preview", checked=True, size="sm"),
], gap="md"),
], justify="space-between"),
# ── Controls Row 2: Sliders ────────────────────────────────────────
dmc.SimpleGrid(cols={"base": 1, "sm": 2, "lg": 4}, children=[
dmc.Stack([
dmc.Text("Speed (ms/tick)", size="xs", fw=500),
dmc.Slider(
id="mc-lt-speed", value=200, min=50, max=1000, step=50,
marks=[
{"value": 50, "label": "50"},
{"value": 200, "label": "200"},
{"value": 500, "label": "500"},
{"value": 1000, "label": "1s"},
],
),
], gap=4),
dmc.Stack([
dmc.Text("Volatility", size="xs", fw=500),
dmc.Slider(
id="mc-lt-volatility", value=2.0, min=0.5, max=8.0, step=0.5,
marks=[
{"value": 0.5, "label": "0.5"},
{"value": 2.0, "label": "2.0"},
{"value": 8.0, "label": "8.0"},
],
),
], gap=4),
dmc.Stack([
dmc.Text("Drift", size="xs", fw=500),
dmc.Slider(
id="mc-lt-drift", value=0.1, min=-0.5, max=0.5, step=0.05,
marks=[
{"value": -0.5, "label": "-0.5"},
{"value": 0, "label": "0"},
{"value": 0.5, "label": "+0.5"},
],
),
], gap=4),
dmc.Stack([
dmc.Text("Window", size="xs", fw=500),
dmc.Slider(
id="mc-lt-window", value=80, min=30, max=200, step=10,
marks=[
{"value": 30, "label": "30"},
{"value": 80, "label": "80"},
{"value": 200, "label": "200"},
],
),
], gap=4),
]),
# ── Stats Row ──────────────────────────────────────────────────────
dmc.Group([
dmc.Stack([
dmc.Text("Price", size="xs", c="dimmed"),
dmc.Text(id="mc-lt-price", children="$100.00", fw=700, size="lg"),
], gap=0, align="center"),
dmc.Stack([
dmc.Text("Ticks", size="xs", c="dimmed"),
dmc.Text(id="mc-lt-ticks", children="0", fw=700, size="lg"),
], gap=0, align="center"),
dmc.Stack([
dmc.Text("Alerts", size="xs", c="dimmed"),
dmc.Text(id="mc-lt-alerts", children="0", fw=700, size="lg"),
], gap=0, align="center"),
dmc.Stack([
dmc.Text("Status", size="xs", c="dimmed"),
dmc.Text(id="mc-lt-status", children="Stopped", fw=700, size="lg", c="dimmed"),
], gap=0, align="center"),
], gap="xl"),
# ── Chart ──────────────────────────────────────────────────────────
LiveTradingChart(
id="mc-lt-chart",
licenseKey=MUI_KEY,
height=520,
running=False,
intervalMs=200,
seed=42,
windowSize=80,
forecastSize=20,
initialPrice=100,
volatility=0.02,
drift=0.001,
forecastVolatility=1.5,
# Alert tuning — low probability + high threshold keeps labels sparse
alertProbability=0.03, # 3% chance per tick (vs default 8%)
alertThresholdPct=3.0, # minimum 3% move to qualify
alertUpColor="#4caf50",
alertDownColor="#f44336",
showVolume=True,
showLabels=False,
showSlider=True,
volumeHeightPct=20,
margin={"left": 75, "right": 30, "top": 20, "bottom": 50},
),
# ── Alert History ──────────────────────────────────────────────────
dmc.Text("Recent Alerts", fw=600, size="sm"),
html.Pre(
id="mc-lt-log",
children="Alerts will appear here once running...",
style={
"fontSize": "11px", "maxHeight": "130px",
"overflow": "auto", "margin": 0,
"padding": "8px 12px", "borderRadius": "6px",
"background": "light-dark(#f8f9fa, #1a1b1e)",
"border": "1px solid light-dark(#dee2e6, #373a40)",
},
),
], gap="md"), p="lg", radius="md", style=GLASS),
], gap="md")
# ── Callbacks ─────────────────────────────────────────────────────────────────
@callback(
Output("mc-lt-chart", "running"),
Input("mc-lt-start", "n_clicks"),
Input("mc-lt-stop", "n_clicks"),
prevent_initial_call=True,
)
def toggle_running(_start, _stop):
return ctx.triggered_id == "mc-lt-start"
@callback(
Output("mc-lt-chart", "resetTrigger"),
Input("mc-lt-reset", "n_clicks"),
prevent_initial_call=True,
)
def reset_chart(n):
return n or 0
@callback(Output("mc-lt-chart", "intervalMs"), Input("mc-lt-speed", "value"))
def set_speed(val): return val or 200
@callback(Output("mc-lt-chart", "volatility"), Input("mc-lt-volatility", "value"))
def set_volatility(val): return (val or 2.0) / 100
@callback(Output("mc-lt-chart", "drift"), Input("mc-lt-drift", "value"))
def set_drift(val): return (val or 0) / 100
@callback(Output("mc-lt-chart", "windowSize"), Input("mc-lt-window", "value"))
def set_window(val): return val or 80
@callback(Output("mc-lt-chart", "showVolume"), Input("mc-lt-volume", "checked"))
def set_volume(v): return v if v is not None else True
@callback(Output("mc-lt-chart", "showLabels"), Input("mc-lt-labels", "checked"))
def set_labels(v): return v if v is not None else False
@callback(Output("mc-lt-chart", "showSlider"), Input("mc-lt-slider", "checked"))
def set_slider(v): return v if v is not None else True
@callback(
Output("mc-lt-price", "children"),
Output("mc-lt-price", "c"),
Output("mc-lt-ticks", "children"),
Output("mc-lt-status", "children"),
Output("mc-lt-status", "c"),
Input("mc-lt-chart", "currentPrice"),
Input("mc-lt-chart", "tickCount"),
State("mc-lt-chart", "running"),
)
def update_stats(price, ticks, running):
p = price if price is not None else 100.0
return (
f"${p:,.2f}",
"green" if p >= 100 else "red",
str(ticks or 0),
"Running" if running else "Stopped",
"green" if running else "dimmed",
)
@callback(
Output("mc-lt-alerts", "children"),
Output("mc-lt-log", "children"),
Input("mc-lt-chart", "alertHistory"),
)
def update_alerts(alerts):
if not alerts:
return "0", "Alerts will appear here once running..."
recent = list(reversed(alerts[-15:]))
lines = [
f"[Tick {a.get('tick', '?'):>5}] "
f"{'UP' if a.get('type') == 'up' else 'DN'} "
f"${a.get('price', 0):>8.2f} ({a.get('message', '')})"
for a in recent
]
return str(len(alerts)), "\n".join(lines)
LineChart Properties
| Property | Type | Default | Description |
|---|---|---|---|
series | list | Required | Array of series objects with data, label, color, curve, area, stack |
xAxis | list | None | X-axis config: data, scaleType, label, zoom (Pro) |
yAxis | list | None | Y-axis config: label, position, min, max |
referenceLines | list | None | Horizontal/vertical reference lines |
licenseKey | string | "" | MUI Pro license key (for zoom, brush, slider) |
initialZoom | list | None | Initial zoom state (Pro) |
showSlider | bool | False | Show zoom range slider (Pro) |
showToolbar | bool | False | Show zoom/export toolbar (Pro) |
brushConfig | dict | None | Brush selection config (Pro) |
grid | dict | None | Grid lines: {horizontal: bool, vertical: bool} |
height | int | 300 | Chart height in pixels |
colors | list | None | Custom color palette |
axisHighlight | dict | None | {x: 'none'/'line'/'band', y: 'none'/'line'} |
tooltip | dict | None | {trigger: 'axis'/'item'/'none'} |
clickData | dict | Output | Click event data |
highlightedItem | dict | None | Controlled highlight state |
dateFormat | string | None | 1.1.0 — Tooltip date format for scaleType: 'time' (e.g. "MMM d, YYYY") |
dateTickFormat | string | None | 1.1.0 — Axis tick date format for scaleType: 'time' (e.g. "M/d") |
PieChart Properties
| Property | Type | Default | Description |
|---|---|---|---|
data | list | None | Array of {id, value, label, color} objects |
series | list | None | Multi-series for nested pies |
innerRadius | int | 0 | Donut hole radius (>0 = donut) |
outerRadius | int | None | Outer radius |
cornerRadius | int | 0 | Rounded slice corners |
paddingAngle | int | 0 | Gap between slices (degrees) |
startAngle | int | 0 | Arc start angle |
endAngle | int | 360 | Arc end angle |
arcLabel | string | None | 'value', 'label', 'formattedValue' |
arcLabelMinAngle | int | 0 | Min angle to show label |
height | int | 300 | Chart height |
ScatterChart Properties
| Property | Type | Default | Description |
|---|---|---|---|
series | list | Required | Array of series with data: [{x, y, z?, id}] |
zAxis | list | None | Z-axis color mapping config |
voronoiMaxRadius | int | None | Proximity hover radius |
xAxis | list | None | X-axis config |
yAxis | list | None | Y-axis config |
height | int | 300 | Chart height |
Heatmap Properties (Pro)
| Property | Type | Default | Description |
|---|---|---|---|
data | list | Required | Array of [x_index, y_index, value] |
xAxis | dict | Required | {data: [...], label: "..."} |
yAxis | dict | Required | {data: [...], label: "..."} |
colorScale | dict | Required | {type, min, max, colors} or {type, thresholds, colors} |
licenseKey | string | Required | MUI Pro license key |
height | int | 300 | Chart height |
LiveTradingChart Properties
| Property | Type | Default | Description |
|---|---|---|---|
running | bool | False | Start / stop the simulation tick |
intervalMs | int | 200 | Milliseconds between ticks |
windowSize | int | 80 | Number of visible candles |
forecastSize | int | 20 | Candles projected ahead |
initialPrice | float | 100 | Starting price |
volatility | float | 0.02 | Per-tick price volatility (e.g. 0.02 = 2%) |
drift | float | 0.001 | Per-tick price drift (positive = upward trend) |
forecastVolatility | float | 1.5 | Uncertainty band width multiplier |
seed | int | None | Random seed for reproducibility |
showVolume | bool | True | Show volume histogram |
showLabels | bool | False | Show price labels on candles |
showSlider | bool | False | Show zoom range slider (Pro) |
volumeHeightPct | int | 20 | Volume panel height as % of chart height |
alertProbability | float | 0.08 | Probability of an alert firing per tick (lower = fewer labels) |
alertThresholdPct | float | 2.0 | Minimum % price change required to trigger an alert |
alertUpColor | string | "#4caf50" | Label color for upward alert moves |
alertDownColor | string | "#f44336" | Label color for downward alert moves |
margin | dict | None | Chart margins {left, right, top, bottom} |
licenseKey | string | "" | MUI Pro license key |
resetTrigger | int | None | Increment to reset simulation |
currentPrice | float | Output | Current simulated price |
tickCount | int | Output | Number of ticks elapsed |
alertHistory | list | Output | Array of {type, tick, price, message} alert objects |
Contributing
Contributions welcome! Visit the GitHub repo.
License
MIT License (component). MUI X Pro license required for Pro features.