egui_charts/studies/builtin/
zero_trend_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)]
14pub struct ZeroTrendVol {
15 period: usize,
16 values: IndicatorValues,
17 colors: Vec<Color32>,
18 visible: bool,
19}
20
21impl ZeroTrendVol {
22 pub fn new(period: usize) -> Self {
24 Self {
25 period: period.max(1),
26 values: IndicatorValues::new(),
27 colors: vec![palette::LIGHT_BLUE],
28 visible: true,
29 }
30 }
31}
32
33impl Default for ZeroTrendVol {
34 fn default() -> Self {
35 Self::new(20)
36 }
37}
38
39impl ZeroTrendVol {
40 fn compute(&self, data: &[Bar]) -> IndicatorValues {
42 let returns: Vec<_> = data
43 .windows(2)
44 .map(|bars| {
45 if bars[0].close > 0.0 && bars[1].close > 0.0 {
46 (bars[1].close / bars[0].close).ln()
47 } else {
48 f64::NAN
49 }
50 })
51 .collect();
52 (0..data.len())
53 .map(|i| {
54 if i < self.period {
55 return IndicatorValue::None;
56 }
57 let window = &returns[i - self.period..i];
58 if window.iter().any(|r| !r.is_finite()) {
59 return IndicatorValue::None;
60 }
61 let mean_squared = window.iter().map(|r| r * r).sum::<f64>() / self.period as f64;
62 IndicatorValue::Single(mean_squared.sqrt() * 252.0_f64.sqrt() * 100.0)
63 })
64 .collect()
65 }
66}
67
68impl Indicator for ZeroTrendVol {
69 fn source(&self) -> Option<crate::studies::StudySource> {
70 Some(crate::studies::StudySource::Price(
71 crate::model::PriceSource::Close,
72 ))
73 }
74
75 crate::studies::inputs::study_inputs!(self;
76 period: length("Period", 1, usize::MAX)
77 ; {});
78
79 fn value_format(&self) -> crate::scales::StudyFormat {
80 crate::scales::StudyFormat::Percent
81 }
82
83 fn name(&self) -> &str {
84 "Zero-Trend Vol"
85 }
86
87 fn desc(&self) -> &str {
88 "Zero-Trend Close-to-Close Volatility - Annualised percentage assuming daily bars"
89 }
90
91 fn calculate(&mut self, data: &[Bar]) {
92 self.update(data, 0);
93 }
94
95 fn update(&mut self, data: &[Bar], from: usize) {
96 let mut values = std::mem::take(&mut self.values);
97 update_window(
98 &mut values,
99 data,
100 from,
101 usable_behind(self.period, |b| b.close.is_finite()),
102 |data| over_finite_bars(data, |b| b.close.is_finite(), |data| self.compute(data)),
103 );
104 self.values = values;
105 }
106
107 fn is_incremental(&self) -> bool {
108 true
109 }
110
111 fn values(&self) -> &IndicatorValues {
112 &self.values
113 }
114
115 fn colors(&self) -> Vec<Color32> {
116 self.colors.clone()
117 }
118
119 fn set_colors(&mut self, colors: Vec<Color32>) {
120 if !colors.is_empty() {
121 self.colors = colors;
122 }
123 }
124
125 fn is_overlay(&self) -> bool {
126 false
127 }
128
129 fn is_visible(&self) -> bool {
130 self.visible
131 }
132
133 fn set_visible(&mut self, visible: bool) {
134 self.visible = visible;
135 }
136
137 fn clone_box(&self) -> Box<dyn Indicator> {
138 Box::new(self.clone())
139 }
140
141 fn line_names(&self) -> Vec<String> {
142 vec![format!("Zero-Trend Vol({})", self.period)]
143 }
144}