egui_charts/studies/builtin/
chaikin_oscillator.rs1use crate::model::Bar;
27use crate::studies::palette;
28use crate::studies::stepper::Stepper;
29use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
30use egui::Color32;
31
32#[derive(Clone)]
34pub struct ChaikinOscillator {
35 fast_length: usize,
36 slow_length: usize,
37 values: IndicatorValues,
38 stepper: Stepper<(f64, Option<[f64; 2]>)>,
39 color: Color32,
40 visible: bool,
41}
42
43impl ChaikinOscillator {
44 pub fn new(fast_length: usize, slow_length: usize) -> Self {
46 Self {
47 fast_length: fast_length.max(1),
48 slow_length: slow_length.max(1),
49 values: IndicatorValues::new(),
50 stepper: Stepper::default(),
51 color: Color32::from_rgb(236, 64, 122),
52 visible: true,
53 }
54 }
55}
56
57impl Default for ChaikinOscillator {
58 fn default() -> Self {
59 Self::new(3, 10)
60 }
61}
62
63impl Indicator for ChaikinOscillator {
64 crate::studies::inputs::study_inputs!(self;
65 fast_length: length("Fast Length", 1, usize::MAX),
66 slow_length: length("Slow Length", 1, usize::MAX)
67 ; {});
68
69 fn value_format(&self) -> crate::scales::StudyFormat {
70 crate::scales::StudyFormat::Volume
71 }
72
73 fn name(&self) -> &str {
74 "Chaikin Oscillator"
75 }
76
77 fn desc(&self) -> &str {
78 "Chaikin Oscillator - Momentum of the Accumulation/Distribution line"
79 }
80
81 fn calculate(&mut self, data: &[Bar]) {
82 self.update(data, 0);
83 }
84
85 fn update(&mut self, data: &[Bar], from: usize) {
86 self.stepper
87 .run(data, from, &mut self.values, |state, bar| {
88 if ![bar.high, bar.low, bar.close, bar.volume]
89 .iter()
90 .all(|x| x.is_finite())
91 {
92 return IndicatorValue::None;
93 }
94 let range = bar.high - bar.low;
95 if range > 0.0 {
96 let multiplier = (2.0 * bar.close - bar.high - bar.low) / range;
97 state.0 += multiplier * bar.volume;
98 }
99 let adl = if state.0.is_finite() {
100 state.0
101 } else {
102 f64::NAN
103 };
104 let ema = state.1.get_or_insert([adl; 2]);
105 ema[0] += (adl - ema[0]) * (2.0 / (self.fast_length as f64 + 1.0));
106 ema[1] += (adl - ema[1]) * (2.0 / (self.slow_length as f64 + 1.0));
107 IndicatorValue::Single(ema[0] - ema[1])
108 });
109 }
110
111 fn is_incremental(&self) -> bool {
112 true
113 }
114
115 fn values(&self) -> &IndicatorValues {
116 &self.values
117 }
118
119 fn colors(&self) -> Vec<Color32> {
120 vec![self.color]
121 }
122
123 fn set_colors(&mut self, colors: Vec<Color32>) {
124 if let Some(&color) = colors.first() {
125 self.color = color;
126 }
127 }
128
129 fn pane(&self) -> crate::studies::StudyPane {
130 crate::studies::StudyPane {
131 levels: vec![(0.0, palette::GRAY, "Zero")],
132 ..Default::default()
133 }
134 }
135
136 fn is_overlay(&self) -> bool {
137 false
138 }
139
140 fn is_visible(&self) -> bool {
141 self.visible
142 }
143
144 fn set_visible(&mut self, visible: bool) {
145 self.visible = visible;
146 }
147
148 fn clone_box(&self) -> Box<dyn Indicator> {
149 Box::new(self.clone())
150 }
151
152 fn line_names(&self) -> Vec<String> {
153 vec![format!(
154 "Chaikin Osc({}, {})",
155 self.fast_length, self.slow_length
156 )]
157 }
158}