egui_charts/studies/builtin/
tema.rs1use super::smoothing::Smoothing;
23use crate::model::Bar;
24use crate::studies::palette;
25use crate::studies::stepper::Stepper;
26use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
27use egui::Color32;
28
29#[derive(Clone)]
31pub struct TEMA {
32 period: usize,
33 values: IndicatorValues,
34 stepper: Stepper<[Smoothing; 3]>,
35 color: Color32,
36 visible: bool,
37}
38
39impl TEMA {
40 pub fn new(period: usize) -> Self {
45 Self {
46 period: period.max(1),
47 values: IndicatorValues::new(),
48 stepper: Stepper::default(),
49 color: palette::BLUE,
50 visible: true,
51 }
52 }
53
54 pub fn with_color(mut self, color: Color32) -> Self {
56 self.color = color;
57 self
58 }
59}
60
61impl Default for TEMA {
63 fn default() -> Self {
64 Self::new(9)
65 }
66}
67
68impl Indicator for TEMA {
69 fn source(&self) -> Option<crate::studies::StudySource> {
70 Some(crate::studies::StudySource::Price(
71 crate::model::PriceSource::Close,
72 ))
73 }
74
75 crate::studies::inputs::study_inputs!(self;
76 period: length("Period", 1, usize::MAX)
77 ; {});
78
79 fn name(&self) -> &str {
80 "TEMA"
81 }
82
83 fn desc(&self) -> &str {
84 "Triple Exponential Moving Avg - Minimal lag moving avg"
85 }
86
87 fn calculate(&mut self, data: &[Bar]) {
88 self.update(data, 0);
89 }
90
91 fn update(&mut self, data: &[Bar], from: usize) {
92 self.stepper
93 .run(data, from, &mut self.values, |state, bar| {
94 if !bar.close.is_finite() {
95 return IndicatorValue::None;
96 }
97 let alpha = 2.0 / (self.period as f64 + 1.0);
98 let ema1 = state[0].next(bar.close, self.period, alpha);
99 let ema2 = state[1].next(ema1, self.period, alpha);
100 let ema3 = state[2].next(ema2, self.period, alpha);
101 IndicatorValue::Single(3.0 * ema1 - 3.0 * ema2 + ema3)
102 });
103 }
104
105 fn is_incremental(&self) -> bool {
106 true
107 }
108
109 fn values(&self) -> &IndicatorValues {
110 &self.values
111 }
112
113 fn colors(&self) -> Vec<Color32> {
114 vec![self.color]
115 }
116
117 fn set_colors(&mut self, colors: Vec<Color32>) {
118 if !colors.is_empty() {
119 self.color = colors[0];
120 }
121 }
122
123 fn is_overlay(&self) -> bool {
124 true
125 }
126
127 fn line_count(&self) -> usize {
128 1
129 }
130
131 fn is_visible(&self) -> bool {
132 self.visible
133 }
134
135 fn set_visible(&mut self, visible: bool) {
136 self.visible = visible;
137 }
138
139 fn clone_box(&self) -> Box<dyn Indicator> {
140 Box::new(self.clone())
141 }
142
143 fn line_names(&self) -> Vec<String> {
144 vec![format!("TEMA({})", self.period)]
145 }
146}