egui_charts/studies/builtin/
standard_error.rs1use crate::model::Bar;
2use crate::studies::palette;
3use crate::studies::stepper::update_window;
4use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
5use egui::Color32;
6
7#[derive(Clone)]
9pub struct StandardError {
10 period: usize,
11 values: IndicatorValues,
12 colors: Vec<Color32>,
13 visible: bool,
14}
15
16impl StandardError {
17 pub fn new(period: usize) -> Self {
19 Self {
20 period: period.max(2),
21 values: IndicatorValues::new(),
22 colors: vec![palette::ORANGE],
23 visible: true,
24 }
25 }
26
27 fn linreg(prices: &[f64]) -> (f64, f64) {
29 let n = prices.len() as f64;
30 let mut sum_x = 0.0;
31 let mut sum_y = 0.0;
32 let mut sum_xy = 0.0;
33 let mut sum_x2 = 0.0;
34
35 for (j, &y) in prices.iter().enumerate() {
36 let x = j as f64;
37 sum_x += x;
38 sum_y += y;
39 sum_xy += x * y;
40 sum_x2 += x * x;
41 }
42
43 let denom = n * sum_x2 - sum_x * sum_x;
44 if denom.abs() < 1e-15 {
45 return (0.0, sum_y / n);
46 }
47 let slope = (n * sum_xy - sum_x * sum_y) / denom;
48 let intercept = (sum_y - slope * sum_x) / n;
49 (slope, intercept)
50 }
51}
52
53impl Default for StandardError {
54 fn default() -> Self {
55 Self::new(20)
56 }
57}
58
59impl StandardError {
60 fn compute(&self, data: &[Bar]) -> IndicatorValues {
61 let mut values = IndicatorValues::new();
62
63 if data.is_empty() {
64 return values;
65 }
66
67 for i in 0..data.len() {
68 if i + 1 < self.period {
69 values.push(IndicatorValue::None);
70 continue;
71 }
72
73 let start = i + 1 - self.period;
74 let prices: Vec<f64> = data[start..=i].iter().map(|b| b.close).collect();
75 let (slope, intercept) = Self::linreg(&prices);
76
77 let n = self.period as f64;
78 let sum_sq_err: f64 = prices
79 .iter()
80 .enumerate()
81 .map(|(j, &y)| {
82 let predicted = intercept + slope * j as f64;
83 (y - predicted).powi(2)
84 })
85 .sum();
86
87 let se = if n > 2.0 {
89 (sum_sq_err / (n - 2.0)).sqrt()
90 } else {
91 0.0
92 };
93
94 values.push(IndicatorValue::Single(se));
95 }
96 values
97 }
98}
99
100impl Indicator for StandardError {
101 fn source(&self) -> Option<crate::studies::StudySource> {
102 Some(crate::studies::StudySource::Price(
103 crate::model::PriceSource::Close,
104 ))
105 }
106
107 crate::studies::inputs::study_inputs!(self;
108 period: length("Period", 2, usize::MAX)
109 ; {});
110
111 fn name(&self) -> &str {
112 "StdErr"
113 }
114
115 fn desc(&self) -> &str {
116 "Standard Error - Standard error of linear regression of close prices"
117 }
118
119 fn calculate(&mut self, data: &[Bar]) {
120 self.update(data, 0);
121 }
122
123 fn update(&mut self, data: &[Bar], from: usize) {
124 let mut values = std::mem::take(&mut self.values);
125 update_window(
126 &mut values,
127 data,
128 from,
129 |_data, from| from.saturating_sub(self.period - 1),
130 |data| self.compute(data),
131 );
132 self.values = values;
133 }
134
135 fn is_incremental(&self) -> bool {
136 true
137 }
138
139 fn values(&self) -> &IndicatorValues {
140 &self.values
141 }
142
143 fn colors(&self) -> Vec<Color32> {
144 self.colors.clone()
145 }
146
147 fn set_colors(&mut self, colors: Vec<Color32>) {
148 if !colors.is_empty() {
149 self.colors = colors;
150 }
151 }
152
153 fn is_overlay(&self) -> bool {
154 false
155 }
156
157 fn is_visible(&self) -> bool {
158 self.visible
159 }
160
161 fn set_visible(&mut self, visible: bool) {
162 self.visible = visible;
163 }
164
165 fn clone_box(&self) -> Box<dyn Indicator> {
166 Box::new(self.clone())
167 }
168
169 fn line_names(&self) -> Vec<String> {
170 vec![format!("StdErr({})", self.period)]
171 }
172}