egui_charts/studies/builtin/
guppy_ma.rs1use crate::model::Bar;
2use crate::studies::stepper::Stepper;
3use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
4use egui::Color32;
5
6const SHORT_PERIODS: [usize; 6] = [3, 5, 8, 10, 12, 15];
9const LONG_PERIODS: [usize; 6] = [30, 35, 40, 45, 50, 60];
10
11#[derive(Clone)]
13pub struct GuppyMA {
14 values: IndicatorValues,
15 stepper: Stepper<Option<[f64; 12]>>,
16 colors: Vec<Color32>,
17 visible: bool,
18}
19
20impl GuppyMA {
21 pub fn new() -> Self {
23 Self {
24 values: IndicatorValues::new(),
25 stepper: Stepper::default(),
26 colors: [217, 224, 232, 240, 247, 255]
27 .into_iter()
28 .map(|alpha| Color32::from_rgba_unmultiplied(0, 255, 255, alpha))
29 .chain(
30 [217, 224, 232, 240, 247, 255]
31 .into_iter()
32 .map(|alpha| Color32::from_rgba_unmultiplied(255, 0, 0, alpha)),
33 )
34 .collect(),
35 visible: true,
36 }
37 }
38}
39
40impl Default for GuppyMA {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl Indicator for GuppyMA {
47 fn source(&self) -> Option<crate::studies::StudySource> {
48 Some(crate::studies::StudySource::Price(
49 crate::model::PriceSource::Close,
50 ))
51 }
52
53 fn name(&self) -> &str {
54 "GMMA"
55 }
56
57 fn desc(&self) -> &str {
58 "Guppy Multiple Moving Average - 12 EMAs in short and long groups"
59 }
60
61 fn calculate(&mut self, data: &[Bar]) {
62 self.update(data, 0);
63 }
64
65 fn update(&mut self, data: &[Bar], from: usize) {
66 self.stepper
67 .run(data, from, &mut self.values, |state, bar| {
68 if !bar.close.is_finite() {
69 return IndicatorValue::None;
70 }
71 let averages = state.get_or_insert([bar.close; 12]);
72 for (average, period) in averages
73 .iter_mut()
74 .zip(SHORT_PERIODS.into_iter().chain(LONG_PERIODS))
75 {
76 *average = (bar.close - *average) * (2.0 / (period as f64 + 1.0)) + *average;
77 }
78 IndicatorValue::Multiple(averages.to_vec())
79 });
80 }
81
82 fn is_incremental(&self) -> bool {
83 true
84 }
85
86 fn values(&self) -> &IndicatorValues {
87 &self.values
88 }
89
90 fn colors(&self) -> Vec<Color32> {
91 self.colors.clone()
92 }
93
94 fn set_colors(&mut self, colors: Vec<Color32>) {
95 if colors.len() >= 12 {
96 self.colors = colors;
97 }
98 }
99
100 fn is_overlay(&self) -> bool {
101 true
102 }
103
104 fn line_count(&self) -> usize {
105 12
106 }
107
108 fn is_visible(&self) -> bool {
109 self.visible
110 }
111
112 fn set_visible(&mut self, visible: bool) {
113 self.visible = visible;
114 }
115
116 fn clone_box(&self) -> Box<dyn Indicator> {
117 Box::new(self.clone())
118 }
119
120 fn line_names(&self) -> Vec<String> {
121 let mut names = Vec::with_capacity(12);
122 for &p in &SHORT_PERIODS {
123 names.push(format!("EMA({p})"));
124 }
125 for &p in &LONG_PERIODS {
126 names.push(format!("EMA({p})"));
127 }
128 names
129 }
130}