Price scale
Use independent scales when two series have different units or magnitudes. This scene adds a comparison at ten times the sample price to the first chart. Both histories contain only positive prices.
Try modes and margins
Section titled “Try modes and margins”Select Main or Comparison, then change its mode, invert it or adjust its
top and bottom margins. Crop vertically makes that scale manual;
Autoscale fits it again. Pan to see relative modes choose a new reference.
The controls and setup below run in the widget-only docs package.
Create PriceScale::new(&mut chart) once, then call its show(ui, &mut chart)
before chart.show(ui) on each frame.
Change either scale's mode and margins, crop it, then restore autoscale.
The interactive example needs JavaScript and WebAssembly. You can still use the chart setup alongside it.
use eframe::egui;use egui_charts::{ Chart, model::{Bar, SeriesScale}, scales::PriceScaleMode, widget::{SeriesId, SeriesOptions},};
/// Edit either of two independent scales on the same plot.pub struct PriceScale { comparison: SeriesId, selected: SeriesId,}
impl PriceScale { /// Add a positive-valued comparison with its own labelled price scale. pub fn new(chart: &mut Chart) -> Self { let bars = chart .bars(SeriesId::MAIN) .iter() .map(|bar| Bar::from_value(bar.time, bar.close * 10.0)) .collect(); let comparison = chart.add_series(SeriesOptions { name: "COMPARISON".into(), scale: SeriesScale::Separate(1), ..Default::default() }); chart.set_data(comparison, bars); Self { comparison, selected: SeriesId::MAIN, } }
/// Change only the selected scale; keep user edits between frames. pub fn show(&mut self, ui: &mut egui::Ui, chart: &mut Chart) { ui.horizontal_wrapped(|ui| { ui.label("Edit scale:"); ui.selectable_value(&mut self.selected, SeriesId::MAIN, "Main"); ui.selectable_value(&mut self.selected, self.comparison, "Comparison"); }); let scale = chart.series_price_scale_mut(self.selected).unwrap(); let previous = *scale.options(); let options = scale.options_mut(); ui.horizontal_wrapped(|ui| { egui::ComboBox::from_id_salt("mode") .selected_text(options.mode.to_string()) .show_ui(ui, |ui| { for mode in [ PriceScaleMode::Normal, PriceScaleMode::Logarithmic, PriceScaleMode::Percentage, PriceScaleMode::IndexedTo100, ] { ui.selectable_value(&mut options.mode, mode, mode.to_string()); } }); ui.checkbox(&mut options.invert_scale, "Invert"); }); ui.horizontal_wrapped(|ui| { let margins = &mut options.scale_margins; ui.label("Margins:"); ui.add( egui::DragValue::new(&mut margins.top) .range(0.0..=0.4) .speed(0.005) .prefix("Top "), ); ui.add( egui::DragValue::new(&mut margins.bottom) .range(0.0..=0.4) .speed(0.005) .prefix("Bottom "), ); }); // A manual range uses the previous mode's units; refit after an option edit. if *options != previous { scale.reset(); } ui.horizontal_wrapped(|ui| { if ui.button("Crop vertically").clicked() { let range = scale.range(); let inset = range.length() * 0.25; scale.set_range(range.min + inset, range.max - inset); } if ui.button("Autoscale").clicked() { scale.reset(); } ui.label(if scale.is_manual() { "Manual range" } else { "Automatic range" }); }); }}Assign an independent scale
Section titled “Assign an independent scale”SeriesOptions::scale
uses SeriesScale:
RightandLeftshare the pane’s corresponding axis.Separate(id)creates an additional labelled axis.Overlay(id)creates an independent scale without axis labels.
Series with the same assignment share a range and options. Distinct host-chosen
IDs keep their scales independent. Use
set_series_scale
to reassign a present series, or
series_price_scale_mut
to edit its current scale. Below the main pane, the series uses its
destination pane’s scale.
Choose the transform
Section titled “Choose the transform”PriceScaleOptions::mode
selects a PriceScaleMode.
For a price p and the series’ first visible close b:
| Mode | Scale units | Meaning |
|---|---|---|
| Normal | p | Equal differences occupy equal heights. |
| Logarithmic | ln(p) | Equal ratios occupy equal heights. |
| Percentage | 100 * (p - b) / abs(b) | The reference reads 0%. |
| IndexedTo100 | 100 + 100 * (p - b) / abs(b) | The reference reads 100. |
For example, a move from 100 to 110 reads +10% or 110 indexed. Prices 100, 200 and 400 have equal vertical spacing on a logarithmic scale because each step doubles the price. Logarithmic rendering requires strictly positive prices; zero and negative prices have no finite log coordinate. Choose Normal for data that can cross zero. A missing, non-finite or zero relative reference falls back to ordinary prices. Each series keeps its own first-visible-close reference, even when several share a relative scale.
After changing a mode through
PriceScale::options_mut,
call reset
to fit the data in the new units. The scene does this after an option edit.
invert_scale
reverses the vertical direction without changing the prices.
Leave margins or set a manual range
Section titled “Leave margins or set a manual range”scale_margins
contains top
and bottom
fractions of the pane height. The defaults are 0.1 each, leaving the data in the
middle 80%. Keep each fraction between 0 and 1 and their sum below 1; a sum of
1 or more leaves no drawable space, so the margins are ignored. With data from
100 to 120 and 10% margins, the total span is 20 / 0.8 = 25, so autoscale uses
97.5 to 122.5 in Normal mode.
set_range
sets a manual range in scale units, including logarithms or percentages when
that mode is active. Bounds must be finite and increasing with a finite span;
invalid bounds leave the range unchanged. Margins apply to automatic fitting.
Use reset to resume it after manual scaling or an axis drag.
For custom overlays, read
series_price_map
after Chart::show, then use
price_to_y
and y_to_price.
Before the pane is first drawn, its map is None. See
first-frame geometry.