egui_charts/studies/builtin/
chaikin_volatility.rs1use super::smoothing::Smoothing;
2use crate::model::Bar;
3use crate::studies::stepper::Stepper;
4use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
5use egui::Color32;
6
7#[derive(Clone)]
11pub struct ChaikinVolatility {
12 ema_period: usize,
14 roc_period: usize,
16 values: IndicatorValues,
17 stepper: Stepper<(usize, Smoothing, std::collections::VecDeque<f64>)>,
18 color: Color32,
19 visible: bool,
20}
21
22impl ChaikinVolatility {
23 pub fn new(ema_period: usize, roc_period: usize) -> Self {
25 Self {
26 ema_period: ema_period.max(1),
27 roc_period: roc_period.max(1),
28 values: IndicatorValues::new(),
29 stepper: Stepper::default(),
30 color: Color32::from_rgb(171, 71, 188),
31 visible: true,
32 }
33 }
34
35 pub fn with_color(mut self, color: Color32) -> Self {
37 self.color = color;
38 self
39 }
40}
41
42impl Default for ChaikinVolatility {
43 fn default() -> Self {
44 Self::new(10, 10)
45 }
46}
47
48impl Indicator for ChaikinVolatility {
49 crate::studies::inputs::study_inputs!(self;
50 ema_period: length("EMA period", 1, usize::MAX),
51 roc_period: length("Roc period", 1, usize::MAX)
52 ; {});
53
54 fn value_format(&self) -> crate::scales::StudyFormat {
55 crate::scales::StudyFormat::Percent
56 }
57
58 fn name(&self) -> &str {
59 "CV"
60 }
61
62 fn desc(&self) -> &str {
63 "Chaikin Volatility - Rate of change of trading range"
64 }
65
66 fn calculate(&mut self, data: &[Bar]) {
67 self.update(data, 0);
68 }
69
70 fn update(&mut self, data: &[Bar], from: usize) {
71 self.stepper
72 .run(data, from, &mut self.values, |state, bar| {
73 if !bar.high.is_finite() || !bar.low.is_finite() {
74 return IndicatorValue::None;
75 }
76 state.0 += 1;
77 let ema = state
78 .1
79 .next(bar.high - bar.low, 1, 2.0 / (self.ema_period as f64 + 1.0));
80 state.2.push_back(ema);
81 let past = if state.2.len() > self.roc_period {
82 state.2.pop_front().unwrap()
83 } else {
84 f64::NAN
85 };
86 if state.0 < self.ema_period.saturating_add(self.roc_period) {
87 IndicatorValue::None
88 } else {
89 IndicatorValue::Single(if past != 0.0 {
90 (ema - past) / past * 100.0
91 } else {
92 0.0
93 })
94 }
95 });
96 }
97
98 fn is_incremental(&self) -> bool {
99 true
100 }
101
102 fn values(&self) -> &IndicatorValues {
103 &self.values
104 }
105
106 fn colors(&self) -> Vec<Color32> {
107 vec![self.color]
108 }
109
110 fn set_colors(&mut self, colors: Vec<Color32>) {
111 if !colors.is_empty() {
112 self.color = colors[0];
113 }
114 }
115
116 fn is_overlay(&self) -> bool {
117 false
118 }
119
120 fn is_visible(&self) -> bool {
121 self.visible
122 }
123
124 fn set_visible(&mut self, visible: bool) {
125 self.visible = visible;
126 }
127
128 fn clone_box(&self) -> Box<dyn Indicator> {
129 Box::new(self.clone())
130 }
131
132 fn line_names(&self) -> Vec<String> {
133 vec![format!("CV({},{})", self.ema_period, self.roc_period)]
134 }
135}