finance_query/indicators/
tema.rs1use super::{IndicatorError, Result, ema::ema};
4
5pub fn tema(data: &[f64], period: usize) -> Result<Vec<Option<f64>>> {
28 if period == 0 {
29 return Err(IndicatorError::InvalidPeriod(
30 "Period must be greater than 0".to_string(),
31 ));
32 }
33 if data.len() < period {
34 return Err(IndicatorError::InsufficientData {
35 need: period,
36 got: data.len(),
37 });
38 }
39
40 let ema1 = ema(data, period);
41
42 let valid_ema1: Vec<f64> = ema1.iter().filter_map(|&x| x).collect();
43 if valid_ema1.len() < period {
44 return Err(IndicatorError::InsufficientData {
45 need: 2 * period - 1,
46 got: data.len(),
47 });
48 }
49
50 let ema2 = ema(&valid_ema1, period);
51
52 let valid_ema2: Vec<f64> = ema2.iter().filter_map(|&x| x).collect();
53 if valid_ema2.len() < period {
54 return Err(IndicatorError::InsufficientData {
55 need: 3 * period - 2,
56 got: data.len(),
57 });
58 }
59
60 let ema3 = ema(&valid_ema2, period);
61
62 let mut result = vec![None; data.len()];
63
64 for i in 0..data.len() {
65 let offset1 = period - 1;
66 let offset2 = 2 * (period - 1);
67
68 if i >= offset2 {
69 let j = i - offset1;
70 let k = i - offset2;
71
72 if j < ema2.len()
73 && k < ema3.len()
74 && let (Some(e1), Some(e2), Some(e3)) = (ema1[i], ema2[j], ema3[k])
75 {
76 result[i] = Some(3.0 * e1 - 3.0 * e2 + e3);
77 }
78 }
79 }
80
81 Ok(result)
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_tema() {
90 let prices = vec![10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0];
91 let result = tema(&prices, 3).unwrap();
92
93 assert_eq!(result.len(), prices.len());
94 assert!(result[0].is_none());
96 assert!(result[5].is_none());
97 assert!(result[6].is_some());
98 }
99}