egui_charts/studies/builtin/
ma_channel.rs1use crate::model::Bar;
2use crate::studies::palette;
3use crate::studies::stepper::update_window;
4use crate::studies::{Indicator, IndicatorValues};
5use egui::Color32;
6
7#[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 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, palette::ORANGE, palette::LIGHT_BLUE.gamma_multiply_u8(13), ],
37 visible: true,
38 }
39 }
40
41 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 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}