Skip to main content

egui_charts/studies/builtin/
ma_channel.rs

1use crate::model::Bar;
2use crate::studies::palette;
3use crate::studies::stepper::update_window;
4use crate::studies::{Indicator, IndicatorValues};
5use egui::Color32;
6
7/// Moving Average Channel: the simple average of the highs as the upper line and of the lows as
8/// the lower line, each over its own length and drawn its own number of bars later (earlier for
9/// a negative offset), with the band between them filled. A window with a non-finite high (low)
10/// has no upper (lower) value.
11#[derive(Clone)]
12pub struct MAChannel {
13    upper_length: usize,
14    lower_length: usize,
15    upper_offset: i32,
16    lower_offset: i32,
17    values: IndicatorValues,
18    colors: Vec<Color32>,
19    visible: bool,
20}
21
22impl MAChannel {
23    /// A channel over `upper_length` highs and `lower_length` lows, each at least 1, drawn on
24    /// the bars they end at.
25    pub fn new(upper_length: usize, lower_length: usize) -> Self {
26        Self {
27            upper_length: upper_length.max(1),
28            lower_length: lower_length.max(1),
29            upper_offset: 0,
30            lower_offset: 0,
31            values: IndicatorValues::new(),
32            colors: vec![
33                palette::LIGHT_BLUE,                       // Upper
34                palette::ORANGE,                           // Lower
35                palette::LIGHT_BLUE.gamma_multiply_u8(13), // Fill
36            ],
37            visible: true,
38        }
39    }
40
41    /// Draw the upper line `upper` and the lower line `lower` bars later; negative values draw
42    /// earlier.
43    pub fn with_offsets(mut self, upper: i32, lower: i32) -> Self {
44        self.upper_offset = upper;
45        self.lower_offset = lower;
46        self
47    }
48}
49
50impl Default for MAChannel {
51    fn default() -> Self {
52        Self::new(20, 20)
53    }
54}
55
56impl MAChannel {
57    fn compute(&self, data: &[Bar]) -> IndicatorValues {
58        // Each window is summed afresh, so an update equals a full calculation bit for bit.
59        let mean = |i: usize, n: usize, price: fn(&Bar) -> f64| {
60            if i + 1 < n {
61                f64::NAN
62            } else {
63                data[i + 1 - n..=i].iter().map(price).sum::<f64>() / n as f64
64            }
65        };
66        let mut values = IndicatorValues::new();
67        for i in 0..data.len() {
68            values.push_lines(&[
69                mean(i, self.upper_length, |b| b.high),
70                mean(i, self.lower_length, |b| b.low),
71            ]);
72        }
73        values
74    }
75}
76
77impl Indicator for MAChannel {
78    crate::studies::inputs::study_inputs!(self;
79        upper_length: length("Upper length", 1, usize::MAX),
80        upper_offset: integer("Upper offset", i32::MIN, i32::MAX),
81        lower_length: length("Lower length", 1, usize::MAX),
82        lower_offset: integer("Lower offset", i32::MIN, i32::MAX)
83    ; {});
84
85    fn plot_offset(&self, line: usize) -> isize {
86        match line {
87            0 => self.upper_offset as isize,
88            1 => self.lower_offset as isize,
89            _ => 0,
90        }
91    }
92
93    fn fills(&self) -> Vec<crate::studies::StudyFill> {
94        vec![crate::studies::StudyFill {
95            first: crate::studies::FillBoundary::Plot(0),
96            second: crate::studies::FillBoundary::Plot(1),
97            color: self.colors[2],
98            inverted_color: None,
99        }]
100    }
101
102    fn name(&self) -> &str {
103        "MA Channel"
104    }
105
106    fn desc(&self) -> &str {
107        "Moving Average Channel - Simple averages of the highs and of the lows"
108    }
109
110    fn calculate(&mut self, data: &[Bar]) {
111        self.update(data, 0);
112    }
113
114    fn update(&mut self, data: &[Bar], from: usize) {
115        let mut values = std::mem::take(&mut self.values);
116        let window = self.upper_length.max(self.lower_length);
117        update_window(
118            &mut values,
119            data,
120            from,
121            |_data, from| from.saturating_sub(window - 1),
122            |data| self.compute(data),
123        );
124        self.values = values;
125    }
126
127    fn is_incremental(&self) -> bool {
128        true
129    }
130
131    fn values(&self) -> &IndicatorValues {
132        &self.values
133    }
134
135    fn colors(&self) -> Vec<Color32> {
136        self.colors.clone()
137    }
138
139    fn set_colors(&mut self, colors: Vec<Color32>) {
140        if colors.len() >= 2 {
141            let n = colors.len().min(self.colors.len());
142            self.colors[..n].copy_from_slice(&colors[..n]);
143        }
144    }
145
146    fn is_overlay(&self) -> bool {
147        true
148    }
149
150    fn line_count(&self) -> usize {
151        2
152    }
153
154    fn is_visible(&self) -> bool {
155        self.visible
156    }
157
158    fn set_visible(&mut self, visible: bool) {
159        self.visible = visible;
160    }
161
162    fn clone_box(&self) -> Box<dyn Indicator> {
163        Box::new(self.clone())
164    }
165
166    fn line_names(&self) -> Vec<String> {
167        vec![
168            format!("Upper({})", self.upper_length),
169            format!("Lower({})", self.lower_length),
170        ]
171    }
172}