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_top to 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(...).

ParametersTypeDefaultDescription
visiblebool |
None
NoneShow the scale. (Ignored for the overlay scale.)
border_visiblebool |
None
NoneShow the scale border.
border_colorLiteral['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
NoneBorder CSS color.
auto_scalebool |
None
NoneAuto-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.
modeLiteral['normal', 'logarithmic', 'percentage', 'indexed_to_100'] |
None
NoneScale mapping mode; see PriceScaleMode.
invert_scalebool |
None
NoneFlip the axis top-to-bottom.
align_labelsbool |
None
NoneAlign scale labels with chart pixels.
text_colorLiteral['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
NoneScale label color.
entire_text_onlybool |
None
NoneRender only complete labels (avoid clipping).
ticks_visiblebool |
None
NoneShow tick marks on the scale.
minimum_widthint |
None
NoneMinimum scale width in pixels.
ensure_edge_tick_marks_visiblebool |
None
NoneForce tick marks at the top/bottom edges.
tick_mark_densityfloat |
None
NoneTick-mark spacing (default 2.5). Reads inverted: lower values render more ticks, higher values fewer.
margin_topfloat |
None
NoneTop margin as a fraction (0-1).
margin_bottomfloat |
None
NoneBottom margin as a fraction (0-1).

Create a BaseLine for a series' base_line= argument.

Returns: BaseLine A base-line config for base_line=.

ParametersTypeDefaultDescription
visiblebool |
None
NoneShow the base line (TVL default True).
colorLiteral['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
NoneLine CSS color.
widthLiteral[1, 2, 3, 4] |
None
NoneStroke width in pixels; see LineWidth.
styleLiteral['solid', 'dotted', 'dashed', 'large_dashed', 'sparse_dotted'] |
None
NoneDash pattern; see LineStyle.