egui_charts/studies/builtin/
wma.rs1use crate::model::Bar;
19use crate::studies::palette;
20use crate::studies::stepper::update_window;
21use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
22use egui::Color32;
23
24#[derive(Clone)]
26pub struct WMA {
27 period: usize,
28 values: IndicatorValues,
29 color: Color32,
30 visible: bool,
31}
32
33impl WMA {
34 pub fn new(period: usize) -> Self {
39 Self {
40 period: period.max(1),
41 values: IndicatorValues::new(),
42 color: palette::LIGHT_BLUE,
43 visible: true,
44 }
45 }
46
47 pub fn with_color(mut self, color: Color32) -> Self {
49 self.color = color;
50 self
51 }
52}
53
54impl Default for WMA {
56 fn default() -> Self {
57 Self::new(9)
58 }
59}
60
61impl WMA {
62 fn compute(&self, data: &[Bar]) -> IndicatorValues {
63 let mut values = IndicatorValues::new();
64
65 if data.is_empty() {
66 return values;
67 }
68
69 let weight_sum = self.period as f64 * (self.period as f64 + 1.0) / 2.0;
71
72 for i in 0..data.len() {
73 if i < self.period - 1 {
74 values.push(IndicatorValue::None);
75 } else {
76 let mut weighted_sum = 0.0;
77 for j in 0..self.period {
78 let weight = (self.period - j) as f64;
79 weighted_sum += data[i - j].close * weight;
80 }
81 let wma = weighted_sum / weight_sum;
82 values.push(IndicatorValue::Single(wma));
83 }
84 }
85 values
86 }
87}
88
89impl Indicator for WMA {
90 fn source(&self) -> Option<crate::studies::StudySource> {
91 Some(crate::studies::StudySource::Price(
92 crate::model::PriceSource::Close,
93 ))
94 }
95
96 crate::studies::inputs::study_inputs!(self;
97 period: length("Period", 1, usize::MAX)
98 ; {});
99
100 fn name(&self) -> &str {
101 "WMA"
102 }
103
104 fn desc(&self) -> &str {
105 "Weighted Moving Avg - Gives more weight to recent prices"
106 }
107
108 fn calculate(&mut self, data: &[Bar]) {
109 self.update(data, 0);
110 }
111
112 fn update(&mut self, data: &[Bar], from: usize) {
113 let mut values = std::mem::take(&mut self.values);
114 update_window(
115 &mut values,
116 data,
117 from,
118 |_data, from| from.saturating_sub(self.period - 1),
119 |data| self.compute(data),
120 );
121 self.values = values;
122 }
123
124 fn is_incremental(&self) -> bool {
125 true
126 }
127
128 fn values(&self) -> &IndicatorValues {
129 &self.values
130 }
131
132 fn colors(&self) -> Vec<Color32> {
133 vec![self.color]
134 }
135
136 fn set_colors(&mut self, colors: Vec<Color32>) {
137 if !colors.is_empty() {
138 self.color = colors[0];
139 }
140 }
141
142 fn is_overlay(&self) -> bool {
143 true
144 }
145
146 fn line_count(&self) -> usize {
147 1
148 }
149
150 fn is_visible(&self) -> bool {
151 self.visible
152 }
153
154 fn set_visible(&mut self, visible: bool) {
155 self.visible = visible;
156 }
157
158 fn clone_box(&self) -> Box<dyn Indicator> {
159 Box::new(self.clone())
160 }
161
162 fn line_names(&self) -> Vec<String> {
163 vec![format!("WMA({})", self.period)]
164 }
165}