Price Scale
The price scale is the vertical axis. Most charts have one, the right scale by default, but TVL also supports a left scale, any number of overlay scales, and per-series scale binding. Each scale has its own mode ("normal", "logarithmic", "percentage", "indexed_to_100"), auto-scale toggle, margins, and tick density.
Use this page when you want to plot two series with different units, render returns instead of prices, leave room at the top of the chart for annotations, or just hide a scale.
What are the price-scale options useful for?
- Multi-unit overlays: Plot price on the right scale and volume on the left; each scale auto-fits its own series.
- Return analysis: Switch the scale mode to
"percentage"or"indexed_to_100"to compare instruments with different absolute prices. - Log-scale charts: Use
"logarithmic"mode for assets that move in percentage terms across multiple decades. - Annotation room: Increase
margin_topto leave whitespace above the data for markers and price-line labels.
Examples
Pick a scale mode: all four PriceScaleMode values
PriceScaleMode accepts "normal", "logarithmic", "percentage", and "indexed_to_100". "normal" is linear; "logarithmic" plots log of the value; "percentage" renders each point as a percent change from the first visible value; "indexed_to_100" renormalizes so the first visible point is exactly 100.
"percentage" and "indexed_to_100" are relative to the visible window: pan the chart and the reference point updates.
Set margins to leave headroom
The margin_top and margin_bottom fields on price_scale() are fractions in [0, 1] reserving whitespace at the top and bottom of the plot area. The defaults are around 0.2 / 0.1; bump margin_top higher to leave room for marker labels.
The same margin_top / margin_bottom apply when you pass a price_scale() to the left_price_scale= or overlay_price_scale= slots.
Turn off auto-scale to lock the visible range
By default the scale auto-fits to the visible data. Set auto_scale=False to freeze it; the user can still pan, but the axis numbers don’t shift on zoom.
Default the visible scale to the left
default_visible_price_scale_id accepts "left" or "right". It sets the scale every new series binds to unless the series explicitly sets price_scale_id. The default is "right"; set it to "left" to flip the convention.
For multi-axis layouts where some series have explicit price_scale_id and others don’t, this kwarg sets the default fallback. See multiple-axes for full multi-axis patterns.
Style the left price scale independently
The same price_scale() object configures any slot — pass one to left_price_scale= to style the left axis independently, with its own text colors, borders, and tick visibility.
entire_text_only=True is handy when tick labels would otherwise be clipped at the chart edge.
Tune tick-mark density
The tick_mark_density field on price_scale() sets the tick spacing, so it reads inverted: a lower value renders more ticks, a higher value fewer. The default is around 2.5. It applies to whichever slot you pass the object to (left_price_scale=, overlay_price_scale=).
Use sparse density on narrow dashboard tiles, dense on full-screen analytical charts.
Apply overlay-scale defaults
When a series binds to a numeric overlay scale (e.g. price_scale_id="vol"), TVL falls back to the overlay_price_scale= defaults. Pass a price_scale() whose styling applies to every overlay scale unless the series overrides it.
This is the standard “price + volume” pattern, where the histogram lives on a stub overlay scale at the bottom.
Invert the price scale
invert_scale=True on the price scale flips the axis top-to-bottom. Useful when plotting interest rate spreads where convention is “low rate at top, high rate at bottom” (or for some yield analytics).
Per-series scale options vs. chart-level
Each per-type constructor also accepts a price_scale=tvl.price_scale(...) object of its own. Series-level options take precedence over chart-level for the scale that series binds to, which helps when you want one overlay scale styled differently from another.
If you set mode on both the series’ price_scale and the chart’s, the per-series one wins. The same tvl.price_scale(...) object works in both places — it binds to whichever scale the series uses.
Style the baseline reference line
In "percentage" and "indexed_to_100" modes the scale draws a horizontal reference line at the base value. Pass base_line=tvl.base_line(...) (returns a BaseLine) on the series to style that line.
API Reference
Chart-level price scales are configured with tvl.price_scale(...), which returns
a PriceScale. Pass the result to right_price_scale=, left_price_scale=, or
overlay_price_scale= on tvl.chart(...); one instance can be reused across slots.
For the full tvl.chart signature, see the Chart container page.
Pass the result to right_price_scale=, left_price_scale=, or overlay_price_scale=. One instance can be reused across slots.
Returns: PriceScale A price-scale config for tvl.chart(...).
| Parameters | Type | Default | Description |
|---|---|---|---|
| visible | bool | None | None | Show the scale. (Ignored for the overlay scale.) |
| border_visible | bool | None | None | Show the scale border. |
| border_color | Literal['gray-50', 'gray-75', 'gray-100', 'gray-200', 'gray-300', 'gray-400', 'gray-500', 'gray-600', 'gray-700', 'gray-800', 'gray-900', 'red-100', 'red-200', 'red-300', 'red-400', 'red-500', 'red-600', 'red-700', 'red-800', 'red-900', 'red-1000', 'red-1100', 'red-1200', 'red-1300', 'red-1400', 'orange-100', 'orange-200', 'orange-300', 'orange-400', 'orange-500', 'orange-600', 'orange-700', 'orange-800', 'orange-900', 'orange-1000', 'orange-1100', 'orange-1200', 'orange-1300', 'orange-1400', 'yellow-100', 'yellow-200', 'yellow-300', 'yellow-400', 'yellow-500', 'yellow-600', 'yellow-700', 'yellow-800', 'yellow-900', 'yellow-1000', 'yellow-1100', 'yellow-1200', 'yellow-1300', 'yellow-1400', 'chartreuse-100', 'chartreuse-200', 'chartreuse-300', 'chartreuse-400', 'chartreuse-500', 'chartreuse-600', 'chartreuse-700', 'chartreuse-800', 'chartreuse-900', 'chartreuse-1000', 'chartreuse-1100', 'chartreuse-1200', 'chartreuse-1300', 'chartreuse-1400', 'celery-100', 'celery-200', 'celery-300', 'celery-400', 'celery-500', 'celery-600', 'celery-700', 'celery-800', 'celery-900', 'celery-1000', 'celery-1100', 'celery-1200', 'celery-1300', 'celery-1400', 'green-100', 'green-200', 'green-300', 'green-400', 'green-500', 'green-600', 'green-700', 'green-800', 'green-900', 'green-1000', 'green-1100', 'green-1200', 'green-1300', 'green-1400', 'seafoam-100', 'seafoam-200', 'seafoam-300', 'seafoam-400', 'seafoam-500', 'seafoam-600', 'seafoam-700', 'seafoam-800', 'seafoam-900', 'seafoam-1000', 'seafoam-1100', 'seafoam-1200', 'seafoam-1300', 'seafoam-1400', 'cyan-100', 'cyan-200', 'cyan-300', 'cyan-400', 'cyan-500', 'cyan-600', 'cyan-700', 'cyan-800', 'cyan-900', 'cyan-1000', 'cyan-1100', 'cyan-1200', 'cyan-1300', 'cyan-1400', 'blue-100', 'blue-200', 'blue-300', 'blue-400', 'blue-500', 'blue-600', 'blue-700', 'blue-800', 'blue-900', 'blue-1000', 'blue-1100', 'blue-1200', 'blue-1300', 'blue-1400', 'indigo-100', 'indigo-200', 'indigo-300', 'indigo-400', 'indigo-500', 'indigo-600', 'indigo-700', 'indigo-800', 'indigo-900', 'indigo-1000', 'indigo-1100', 'indigo-1200', 'indigo-1300', 'indigo-1400', 'purple-100', 'purple-200', 'purple-300', 'purple-400', 'purple-500', 'purple-600', 'purple-700', 'purple-800', 'purple-900', 'purple-1000', 'purple-1100', 'purple-1200', 'purple-1300', 'purple-1400', 'fuchsia-100', 'fuchsia-200', 'fuchsia-300', 'fuchsia-400', 'fuchsia-500', 'fuchsia-600', 'fuchsia-700', 'fuchsia-800', 'fuchsia-900', 'fuchsia-1000', 'fuchsia-1100', 'fuchsia-1200', 'fuchsia-1300', 'fuchsia-1400', 'magenta-100', 'magenta-200', 'magenta-300', 'magenta-400', 'magenta-500', 'magenta-600', 'magenta-700', 'magenta-800', 'magenta-900', 'magenta-1000', 'magenta-1100', 'magenta-1200', 'magenta-1300', 'magenta-1400', 'negative', 'notice', 'positive', 'info', 'accent', 'accent-100', 'accent-200', 'accent-300', 'accent-400', 'accent-500', 'accent-600', 'accent-700', 'accent-800', 'accent-900', 'accent-1000', 'accent-1100', 'accent-1200', 'accent-1300', 'accent-1400', 'bg', 'content-bg', 'subdued-content-bg', 'surface-bg', 'fg'] | str | None | None | Border CSS color. |
| auto_scale | bool | None | None | Auto-fit the scale to the visible data (default). Set False to fit once on load, then hold that range so the axis stays fixed as the user zooms and pans. |
| mode | Literal['normal', 'logarithmic', 'percentage', 'indexed_to_100'] | None | None | Scale mapping mode; see PriceScaleMode. |
| invert_scale | bool | None | None | Flip the axis top-to-bottom. |
| align_labels | bool | None | None | Align scale labels with chart pixels. |
| text_color | Literal['gray-50', 'gray-75', 'gray-100', 'gray-200', 'gray-300', 'gray-400', 'gray-500', 'gray-600', 'gray-700', 'gray-800', 'gray-900', 'red-100', 'red-200', 'red-300', 'red-400', 'red-500', 'red-600', 'red-700', 'red-800', 'red-900', 'red-1000', 'red-1100', 'red-1200', 'red-1300', 'red-1400', 'orange-100', 'orange-200', 'orange-300', 'orange-400', 'orange-500', 'orange-600', 'orange-700', 'orange-800', 'orange-900', 'orange-1000', 'orange-1100', 'orange-1200', 'orange-1300', 'orange-1400', 'yellow-100', 'yellow-200', 'yellow-300', 'yellow-400', 'yellow-500', 'yellow-600', 'yellow-700', 'yellow-800', 'yellow-900', 'yellow-1000', 'yellow-1100', 'yellow-1200', 'yellow-1300', 'yellow-1400', 'chartreuse-100', 'chartreuse-200', 'chartreuse-300', 'chartreuse-400', 'chartreuse-500', 'chartreuse-600', 'chartreuse-700', 'chartreuse-800', 'chartreuse-900', 'chartreuse-1000', 'chartreuse-1100', 'chartreuse-1200', 'chartreuse-1300', 'chartreuse-1400', 'celery-100', 'celery-200', 'celery-300', 'celery-400', 'celery-500', 'celery-600', 'celery-700', 'celery-800', 'celery-900', 'celery-1000', 'celery-1100', 'celery-1200', 'celery-1300', 'celery-1400', 'green-100', 'green-200', 'green-300', 'green-400', 'green-500', 'green-600', 'green-700', 'green-800', 'green-900', 'green-1000', 'green-1100', 'green-1200', 'green-1300', 'green-1400', 'seafoam-100', 'seafoam-200', 'seafoam-300', 'seafoam-400', 'seafoam-500', 'seafoam-600', 'seafoam-700', 'seafoam-800', 'seafoam-900', 'seafoam-1000', 'seafoam-1100', 'seafoam-1200', 'seafoam-1300', 'seafoam-1400', 'cyan-100', 'cyan-200', 'cyan-300', 'cyan-400', 'cyan-500', 'cyan-600', 'cyan-700', 'cyan-800', 'cyan-900', 'cyan-1000', 'cyan-1100', 'cyan-1200', 'cyan-1300', 'cyan-1400', 'blue-100', 'blue-200', 'blue-300', 'blue-400', 'blue-500', 'blue-600', 'blue-700', 'blue-800', 'blue-900', 'blue-1000', 'blue-1100', 'blue-1200', 'blue-1300', 'blue-1400', 'indigo-100', 'indigo-200', 'indigo-300', 'indigo-400', 'indigo-500', 'indigo-600', 'indigo-700', 'indigo-800', 'indigo-900', 'indigo-1000', 'indigo-1100', 'indigo-1200', 'indigo-1300', 'indigo-1400', 'purple-100', 'purple-200', 'purple-300', 'purple-400', 'purple-500', 'purple-600', 'purple-700', 'purple-800', 'purple-900', 'purple-1000', 'purple-1100', 'purple-1200', 'purple-1300', 'purple-1400', 'fuchsia-100', 'fuchsia-200', 'fuchsia-300', 'fuchsia-400', 'fuchsia-500', 'fuchsia-600', 'fuchsia-700', 'fuchsia-800', 'fuchsia-900', 'fuchsia-1000', 'fuchsia-1100', 'fuchsia-1200', 'fuchsia-1300', 'fuchsia-1400', 'magenta-100', 'magenta-200', 'magenta-300', 'magenta-400', 'magenta-500', 'magenta-600', 'magenta-700', 'magenta-800', 'magenta-900', 'magenta-1000', 'magenta-1100', 'magenta-1200', 'magenta-1300', 'magenta-1400', 'negative', 'notice', 'positive', 'info', 'accent', 'accent-100', 'accent-200', 'accent-300', 'accent-400', 'accent-500', 'accent-600', 'accent-700', 'accent-800', 'accent-900', 'accent-1000', 'accent-1100', 'accent-1200', 'accent-1300', 'accent-1400', 'bg', 'content-bg', 'subdued-content-bg', 'surface-bg', 'fg'] | str | None | None | Scale label color. |
| entire_text_only | bool | None | None | Render only complete labels (avoid clipping). |
| ticks_visible | bool | None | None | Show tick marks on the scale. |
| minimum_width | int | None | None | Minimum scale width in pixels. |
| ensure_edge_tick_marks_visible | bool | None | None | Force tick marks at the top/bottom edges. |
| tick_mark_density | float | None | None | Tick-mark spacing (default 2.5). Reads inverted: lower values render more ticks, higher values fewer. |
| margin_top | float | None | None | Top margin as a fraction (0-1). |
| margin_bottom | float | None | None | Bottom margin as a fraction (0-1). |
Create a BaseLine for a series' base_line= argument.
Returns: BaseLine A base-line config for base_line=.
| Parameters | Type | Default | Description |
|---|---|---|---|
| visible | bool | None | None | Show the base line (TVL default True). |
| color | Literal['gray-50', 'gray-75', 'gray-100', 'gray-200', 'gray-300', 'gray-400', 'gray-500', 'gray-600', 'gray-700', 'gray-800', 'gray-900', 'red-100', 'red-200', 'red-300', 'red-400', 'red-500', 'red-600', 'red-700', 'red-800', 'red-900', 'red-1000', 'red-1100', 'red-1200', 'red-1300', 'red-1400', 'orange-100', 'orange-200', 'orange-300', 'orange-400', 'orange-500', 'orange-600', 'orange-700', 'orange-800', 'orange-900', 'orange-1000', 'orange-1100', 'orange-1200', 'orange-1300', 'orange-1400', 'yellow-100', 'yellow-200', 'yellow-300', 'yellow-400', 'yellow-500', 'yellow-600', 'yellow-700', 'yellow-800', 'yellow-900', 'yellow-1000', 'yellow-1100', 'yellow-1200', 'yellow-1300', 'yellow-1400', 'chartreuse-100', 'chartreuse-200', 'chartreuse-300', 'chartreuse-400', 'chartreuse-500', 'chartreuse-600', 'chartreuse-700', 'chartreuse-800', 'chartreuse-900', 'chartreuse-1000', 'chartreuse-1100', 'chartreuse-1200', 'chartreuse-1300', 'chartreuse-1400', 'celery-100', 'celery-200', 'celery-300', 'celery-400', 'celery-500', 'celery-600', 'celery-700', 'celery-800', 'celery-900', 'celery-1000', 'celery-1100', 'celery-1200', 'celery-1300', 'celery-1400', 'green-100', 'green-200', 'green-300', 'green-400', 'green-500', 'green-600', 'green-700', 'green-800', 'green-900', 'green-1000', 'green-1100', 'green-1200', 'green-1300', 'green-1400', 'seafoam-100', 'seafoam-200', 'seafoam-300', 'seafoam-400', 'seafoam-500', 'seafoam-600', 'seafoam-700', 'seafoam-800', 'seafoam-900', 'seafoam-1000', 'seafoam-1100', 'seafoam-1200', 'seafoam-1300', 'seafoam-1400', 'cyan-100', 'cyan-200', 'cyan-300', 'cyan-400', 'cyan-500', 'cyan-600', 'cyan-700', 'cyan-800', 'cyan-900', 'cyan-1000', 'cyan-1100', 'cyan-1200', 'cyan-1300', 'cyan-1400', 'blue-100', 'blue-200', 'blue-300', 'blue-400', 'blue-500', 'blue-600', 'blue-700', 'blue-800', 'blue-900', 'blue-1000', 'blue-1100', 'blue-1200', 'blue-1300', 'blue-1400', 'indigo-100', 'indigo-200', 'indigo-300', 'indigo-400', 'indigo-500', 'indigo-600', 'indigo-700', 'indigo-800', 'indigo-900', 'indigo-1000', 'indigo-1100', 'indigo-1200', 'indigo-1300', 'indigo-1400', 'purple-100', 'purple-200', 'purple-300', 'purple-400', 'purple-500', 'purple-600', 'purple-700', 'purple-800', 'purple-900', 'purple-1000', 'purple-1100', 'purple-1200', 'purple-1300', 'purple-1400', 'fuchsia-100', 'fuchsia-200', 'fuchsia-300', 'fuchsia-400', 'fuchsia-500', 'fuchsia-600', 'fuchsia-700', 'fuchsia-800', 'fuchsia-900', 'fuchsia-1000', 'fuchsia-1100', 'fuchsia-1200', 'fuchsia-1300', 'fuchsia-1400', 'magenta-100', 'magenta-200', 'magenta-300', 'magenta-400', 'magenta-500', 'magenta-600', 'magenta-700', 'magenta-800', 'magenta-900', 'magenta-1000', 'magenta-1100', 'magenta-1200', 'magenta-1300', 'magenta-1400', 'negative', 'notice', 'positive', 'info', 'accent', 'accent-100', 'accent-200', 'accent-300', 'accent-400', 'accent-500', 'accent-600', 'accent-700', 'accent-800', 'accent-900', 'accent-1000', 'accent-1100', 'accent-1200', 'accent-1300', 'accent-1400', 'bg', 'content-bg', 'subdued-content-bg', 'surface-bg', 'fg'] | str | None | None | Line CSS color. |
| width | Literal[1, 2, 3, 4] | None | None | Stroke width in pixels; see LineWidth. |
| style | Literal['solid', 'dotted', 'dashed', 'large_dashed', 'sparse_dotted'] | None | None | Dash pattern; see LineStyle. |