egui_charts/studies/mod.rs
1//! Technical indicators, calculation helpers and study presentation.
2//!
3//! Implement [`Indicator`] or use [`CustomIndicator`] for a host-defined study;
4//! [`IndicatorRegistry`] calculates a group of studies without a widget. Enable
5//! `studies` for the built-in catalogue and `IndicatorFactory`. A chart owns the
6//! calculation and rendering lifecycle of studies added through `Chart::add_indicator`.
7//!
8//! # Calculating a study
9//!
10//! Outputs contain a contiguous column per line. Missing and warm-up results are
11//! NaN; [`IndicatorValues::value`] returns finite results as `Option<f64>`.
12//!
13//! ```
14//! # #[cfg(feature = "studies")] {
15//! use egui_charts::{model::Bar, studies::{Indicator, SMA}};
16//!
17//! let bars = vec![
18//! Bar::new("2024-01-01T00:00:00Z".parse().unwrap(), 100.0, 102.0, 99.0, 101.0, 10.0),
19//! Bar::new("2024-01-01T00:01:00Z".parse().unwrap(), 101.0, 103.0, 100.0, 103.0, 20.0),
20//! ];
21//! let mut sma = SMA::new(2);
22//! sma.calculate(&bars);
23//! assert_eq!(sma.values().value(0, 1), Some(102.0));
24//! # }
25//! ```
26//!
27//! [`helpers`] provides scalar calculations for custom studies, and [`Stepper`]
28//! provides incremental calculation state. [`StudyData`] carries auxiliary data
29//! requested from the host's feed.
30
31#[cfg(feature = "studies")]
32#[cfg_attr(docsrs, doc(cfg(feature = "studies")))]
33mod builtin;
34mod custom;
35mod error;
36pub use crate::data::{StudyData, StudyDataRequest};
37pub use error::{RefusedStudy, StudyError};
38#[cfg(feature = "studies")]
39#[cfg_attr(docsrs, doc(cfg(feature = "studies")))]
40pub mod factory;
41mod format;
42pub mod helpers;
43mod indicator_trait;
44pub mod palette;
45pub use format::StudyScale;
46mod inputs;
47mod metric;
48pub use inputs::{InputKind, InputValue, StudyInput, StudySource};
49pub use metric::MetricStudy;
50pub(crate) mod plots;
51pub(crate) mod projection;
52mod settings;
53pub(crate) mod table;
54pub use plots::{FillBoundary, LabelPlacement, StudyCandle, StudyFill, StudyLabel, StudyZone};
55pub use projection::StudyProjection;
56pub use settings::{
57 ConfiguredStudy, LevelStyle, PlotStyle, PlotType, StudyIntervalRange, StudySettings,
58 StudySettingsSnapshot,
59};
60pub use table::{StudyCell, StudyTable};
61mod values;
62pub use values::IndicatorValues;
63#[cfg(all(test, feature = "studies"))]
64mod reference;
65mod stepper;
66#[cfg(all(test, feature = "studies"))]
67mod test_support;
68pub use stepper::Stepper;
69
70#[cfg(feature = "studies")]
71#[cfg_attr(docsrs, doc(cfg(feature = "studies")))]
72pub use builtin::*;
73pub use custom::CustomIndicator;
74#[cfg(feature = "studies")]
75#[cfg_attr(docsrs, doc(cfg(feature = "studies")))]
76pub use factory::IndicatorFactory;
77pub use indicator_trait::{
78 CrossDirection, Indicator, IndicatorFill, IndicatorLineStyle, IndicatorMarker, IndicatorValue,
79 StudyPane,
80};
81
82mod chart_state;
83pub use chart_state::{ChartState, ZoomState};
84
85#[cfg(feature = "studies")]
86mod template;
87#[cfg(feature = "studies")]
88#[cfg_attr(docsrs, doc(cfg(feature = "studies")))]
89pub use template::{StudyTemplate, StudyTemplateSet};
90
91use crate::model::Bar;
92
93/// A registry that owns a collection of [`Indicator`] trait objects and
94/// provides batch operations (calculate all, clear, etc.).
95///
96/// Use the registry when you want to manage multiple indicators as a group
97/// and recalculate them together whenever the underlying bar data changes.
98///
99/// # Example
100///
101/// ```rust
102/// # #[cfg(feature = "studies")] {
103/// # let bars: Vec<egui_charts::model::Bar> = (0..60).map(|i| { let p = 100.0 + (i as f64 * 0.3).sin() * 5.0; egui_charts::model::Bar::new(chrono::Utc::now(), p, p + 1.0, p - 1.0, p + 0.5, 1_000.0) }).collect();
104/// use egui_charts::studies::{IndicatorRegistry, SMA, EMA};
105///
106/// let mut registry = IndicatorRegistry::new();
107/// registry.add(Box::new(SMA::new(20)));
108/// registry.add(Box::new(EMA::new(50)));
109/// registry.calculate_all(&bars);
110///
111/// for indicator in registry.indicators() {
112/// println!("{}: {} values", indicator.name(), indicator.values().len());
113/// }
114/// # }
115/// ```
116pub struct IndicatorRegistry {
117 indicators: Vec<Box<dyn Indicator>>,
118}
119
120impl IndicatorRegistry {
121 /// Create an empty registry with no indicators.
122 pub fn new() -> Self {
123 Self {
124 indicators: Vec::new(),
125 }
126 }
127
128 /// Append a boxed indicator to the registry.
129 pub fn add_indicator(&mut self, indicator: Box<dyn Indicator>) {
130 self.indicators.push(indicator);
131 }
132
133 /// Alias for [`add_indicator`](Self::add_indicator) for brevity.
134 pub fn add(&mut self, indicator: Box<dyn Indicator>) {
135 self.add_indicator(indicator);
136 }
137
138 /// Remove and return the indicator at `index`, or `None` if out of bounds.
139 pub fn remove_indicator(&mut self, index: usize) -> Option<Box<dyn Indicator>> {
140 if index < self.indicators.len() {
141 Some(self.indicators.remove(index))
142 } else {
143 None
144 }
145 }
146
147 /// Return a shared slice of all registered indicators.
148 pub fn indicators(&self) -> &[Box<dyn Indicator>] {
149 &self.indicators
150 }
151
152 /// Return a mutable slice of all registered indicators.
153 pub fn indicators_mut(&mut self) -> &mut [Box<dyn Indicator>] {
154 &mut self.indicators
155 }
156
157 /// Recalculate every indicator against the provided bar data.
158 ///
159 /// This iterates through all registered indicators and calls
160 /// [`Indicator::calculate`] on each one.
161 pub fn calculate_all(&mut self, data: &[Bar]) {
162 for indicator in &mut self.indicators {
163 indicator.calculate(data);
164 }
165 }
166
167 /// Bring every indicator up to date after `data[from..]` changed or grew,
168 /// given that `data[..from]` is unchanged since the previous call.
169 ///
170 /// Calls [`Indicator::update`] on each one; studies that are not
171 /// incremental recompute in full.
172 pub fn update_all(&mut self, data: &[Bar], from: usize) {
173 for indicator in &mut self.indicators {
174 indicator.update(data, from);
175 }
176 }
177
178 /// Remove all indicators from the registry.
179 pub fn clear(&mut self) {
180 self.indicators.clear();
181 }
182}
183
184impl Default for IndicatorRegistry {
185 fn default() -> Self {
186 Self::new()
187 }
188}