egui_charts/studies/builtin/
parkinson_vol.rs1use super::over_finite_bars;
2use crate::model::Bar;
3use crate::studies::palette;
4use crate::studies::stepper::{update_window, usable_behind};
5use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
6use egui::Color32;
7
8#[derive(Clone)]
13pub struct ParkinsonVol {
14 period: usize,
15 values: IndicatorValues,
16 colors: Vec<Color32>,
17 visible: bool,
18}
19
20impl ParkinsonVol {
21 pub fn new(period: usize) -> Self {
23 Self {
24 period: period.max(1),
25 values: IndicatorValues::new(),
26 colors: vec![palette::PURPLE],
27 visible: true,
28 }
29 }
30}
31
32impl Default for ParkinsonVol {
33 fn default() -> Self {
34 Self::new(20)
35 }
36}
37
38impl ParkinsonVol {
39 fn compute(&self, data: &[Bar]) -> IndicatorValues {
41 let mut values = IndicatorValues::new();
42 if data.is_empty() {
43 return values;
44 }
45
46 let ln2 = 2.0_f64.ln();
47
48 let mut hl_sq = Vec::with_capacity(data.len());
50
51 for bar in data {
52 if bar.low > 0.0 {
53 let ln_hl = (bar.high / bar.low).ln();
54 hl_sq.push(ln_hl * ln_hl);
55 } else {
56 hl_sq.push(f64::NAN);
57 }
58 }
59
60 for i in 0..data.len() {
62 if i + 1 < self.period {
63 values.push(IndicatorValue::None);
64 continue;
65 }
66
67 let window = &hl_sq[i + 1 - self.period..=i];
68 let valid: Vec<f64> = window.iter().filter(|x| !x.is_nan()).copied().collect();
69
70 if valid.is_empty() {
71 values.push(IndicatorValue::None);
72 continue;
73 }
74
75 let n = valid.len() as f64;
76 let sum = valid.iter().sum::<f64>();
77
78 let parkinson = (sum / (4.0 * n * ln2)).sqrt();
80
81 let annualized = parkinson * 252.0_f64.sqrt() * 100.0;
83 values.push(IndicatorValue::Single(annualized));
84 }
85
86 values
87 }
88}
89
90impl Indicator for ParkinsonVol {
91 crate::studies::inputs::study_inputs!(self;
92 period: length("Period", 1, usize::MAX)
93 ; {});
94
95 fn value_format(&self) -> crate::scales::StudyFormat {
96 crate::scales::StudyFormat::Percent
97 }
98
99 fn name(&self) -> &str {
100 "Parkinson Vol"
101 }
102
103 fn desc(&self) -> &str {
104 "Parkinson High-Low Range Volatility - Annualised percentage assuming daily bars"
105 }
106
107 fn calculate(&mut self, data: &[Bar]) {
108 self.update(data, 0);
109 }
110
111 fn update(&mut self, data: &[Bar], from: usize) {
112 let mut values = std::mem::take(&mut self.values);
113 update_window(
114 &mut values,
115 data,
116 from,
117 usable_behind(self.period - 1, |b| b.high.is_finite() && b.low.is_finite()),
118 |data| {
119 over_finite_bars(
120 data,
121 |b| b.high.is_finite() && b.low.is_finite(),
122 |data| self.compute(data),
123 )
124 },
125 );
126 self.values = values;
127 }
128
129 fn is_incremental(&self) -> bool {
130 true
131 }
132
133 fn values(&self) -> &IndicatorValues {
134 &self.values
135 }
136
137 fn colors(&self) -> Vec<Color32> {
138 self.colors.clone()
139 }
140
141 fn set_colors(&mut self, colors: Vec<Color32>) {
142 if !colors.is_empty() {
143 self.colors = colors;
144 }
145 }
146
147 fn is_overlay(&self) -> bool {
148 false
149 }
150
151 fn is_visible(&self) -> bool {
152 self.visible
153 }
154
155 fn set_visible(&mut self, visible: bool) {
156 self.visible = visible;
157 }
158
159 fn clone_box(&self) -> Box<dyn Indicator> {
160 Box::new(self.clone())
161 }
162
163 fn line_names(&self) -> Vec<String> {
164 vec![format!("Parkinson Vol({})", self.period)]
165 }
166}