egui_charts/studies/builtin/
mcginley_dynamic.rs1use crate::model::Bar;
2use crate::studies::palette;
3use crate::studies::stepper::Stepper;
4use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
5use egui::Color32;
6
7#[derive(Clone)]
12pub struct McGinleyDynamic {
13 period: usize,
15 values: IndicatorValues,
16 stepper: Stepper<Option<f64>>,
17 color: Color32,
18 visible: bool,
19}
20
21impl McGinleyDynamic {
22 pub fn new(period: usize) -> Self {
24 Self {
25 period: period.max(1),
26 values: IndicatorValues::new(),
27 stepper: Stepper::default(),
28 color: palette::LIGHT_BLUE,
29 visible: true,
30 }
31 }
32
33 pub fn with_color(mut self, color: Color32) -> Self {
35 self.color = color;
36 self
37 }
38}
39
40impl Default for McGinleyDynamic {
41 fn default() -> Self {
42 Self::new(14)
43 }
44}
45
46impl Indicator for McGinleyDynamic {
47 fn source(&self) -> Option<crate::studies::StudySource> {
48 Some(crate::studies::StudySource::Price(
49 crate::model::PriceSource::Close,
50 ))
51 }
52
53 crate::studies::inputs::study_inputs!(self;
54 period: length("Period", 1, usize::MAX)
55 ; {});
56
57 fn name(&self) -> &str {
58 "MD"
59 }
60
61 fn desc(&self) -> &str {
62 "McGinley Dynamic - Self-adjusting moving avg"
63 }
64
65 fn calculate(&mut self, data: &[Bar]) {
66 self.update(data, 0);
67 }
68
69 fn update(&mut self, data: &[Bar], from: usize) {
70 self.stepper
71 .run(data, from, &mut self.values, |state, bar| {
72 if !bar.close.is_finite() {
73 return IndicatorValue::None;
74 }
75 let md = state.get_or_insert(bar.close);
76 if *md != 0.0 {
77 let ratio = bar.close / *md;
78 let divisor = self.period as f64 * ratio.powi(4);
79 if divisor != 0.0 {
80 *md += (bar.close - *md) / divisor;
81 }
82 }
83 IndicatorValue::Single(*md)
84 });
85 }
86
87 fn is_incremental(&self) -> bool {
88 true
89 }
90
91 fn values(&self) -> &IndicatorValues {
92 &self.values
93 }
94
95 fn colors(&self) -> Vec<Color32> {
96 vec![self.color]
97 }
98
99 fn set_colors(&mut self, colors: Vec<Color32>) {
100 if !colors.is_empty() {
101 self.color = colors[0];
102 }
103 }
104
105 fn is_overlay(&self) -> bool {
106 true }
108
109 fn is_visible(&self) -> bool {
110 self.visible
111 }
112
113 fn set_visible(&mut self, visible: bool) {
114 self.visible = visible;
115 }
116
117 fn clone_box(&self) -> Box<dyn Indicator> {
118 Box::new(self.clone())
119 }
120
121 fn line_names(&self) -> Vec<String> {
122 vec![format!("MD({})", self.period)]
123 }
124}