Skip to main content

egui_charts/studies/builtin/
parkinson_vol.rs

1use 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/// Parkinson high-low range volatility.
9///
10/// `sqrt(mean(ln(high / low)^2) / (4 * ln(2))) * sqrt(252) * 100`.
11/// Annualisation assumes daily bars and 252 trading days per year (see #672).
12#[derive(Clone)]
13pub struct ParkinsonVol {
14    period: usize,
15    values: IndicatorValues,
16    colors: Vec<Color32>,
17    visible: bool,
18}
19
20impl ParkinsonVol {
21    /// Create a Parkinson study with a lookback of `period` bars (at least 1).
22    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    /// The values over `data`, every bar of which has finite inputs.
40    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        // Calculate per-bar ln(H/L)^2
49        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        // Rolling Parkinson estimator
61        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            // Parkinson = sqrt(1 / (4 * n * ln(2)) * sum(ln(H/L)^2))
79            let parkinson = (sum / (4.0 * n * ln2)).sqrt();
80
81            // Annualize and convert to percentage
82            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}