finance_query/indicators/
tema.rs1use super::{IndicatorError, Result, ema::ema_raw};
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_raw(data, period); if ema1.len() < period {
43 return Err(IndicatorError::InsufficientData {
44 need: 2 * period - 1,
45 got: data.len(),
46 });
47 }
48 let ema2 = ema_raw(&ema1, period); if ema2.len() < period {
50 return Err(IndicatorError::InsufficientData {
51 need: 3 * period - 2,
52 got: data.len(),
53 });
54 }
55 let ema3 = ema_raw(&ema2, period); let mut result = vec![None; data.len()];
58 let off = period - 1;
59
60 for (k3, &e3) in ema3.iter().enumerate() {
63 let orig_idx = k3 + 3 * off;
64 let e1 = ema1[k3 + 2 * off];
65 let e2 = ema2[k3 + off];
66 result[orig_idx] = Some(3.0 * e1 - 3.0 * e2 + e3);
67 }
68
69 Ok(result)
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test_tema() {
78 let prices = vec![10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0];
79 let result = tema(&prices, 3).unwrap();
80
81 assert_eq!(result.len(), prices.len());
82 assert!(result[0].is_none());
84 assert!(result[5].is_none());
85 assert!(result[6].is_some());
86 }
87}