finance_query/indicators/
aroon.rs1use std::collections::VecDeque;
4
5use super::{IndicatorError, Result};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct AroonResult {
11 pub aroon_up: Vec<Option<f64>>,
13 pub aroon_down: Vec<Option<f64>>,
15}
16
17pub fn aroon(highs: &[f64], lows: &[f64], period: usize) -> Result<AroonResult> {
38 if period == 0 {
39 return Err(IndicatorError::InvalidPeriod(
40 "Period must be greater than 0".to_string(),
41 ));
42 }
43 let len = highs.len();
44 if lows.len() != len {
45 return Err(IndicatorError::InvalidPeriod(
46 "Data lengths must match".to_string(),
47 ));
48 }
49 if len < period {
50 return Err(IndicatorError::InsufficientData {
51 need: period,
52 got: len,
53 });
54 }
55
56 let mut aroon_up = vec![None; len];
57 let mut aroon_down = vec![None; len];
58
59 let mut max_deque: VecDeque<usize> = VecDeque::new();
62 let mut min_deque: VecDeque<usize> = VecDeque::new();
63 let period_f = period as f64;
64
65 for i in 0..len {
66 while max_deque.front().is_some_and(|&j| j + period <= i) {
67 max_deque.pop_front();
68 }
69 while min_deque.front().is_some_and(|&j| j + period <= i) {
70 min_deque.pop_front();
71 }
72 while max_deque.back().is_some_and(|&j| highs[j] <= highs[i]) {
73 max_deque.pop_back();
74 }
75 while min_deque.back().is_some_and(|&j| lows[j] >= lows[i]) {
76 min_deque.pop_back();
77 }
78 max_deque.push_back(i);
79 min_deque.push_back(i);
80
81 if i + 1 >= period {
82 let high_idx = *max_deque.front().unwrap();
83 let low_idx = *min_deque.front().unwrap();
84 let periods_since_high = i - high_idx;
85 let periods_since_low = i - low_idx;
86 aroon_up[i] = Some(((period_f - periods_since_high as f64) / period_f) * 100.0);
87 aroon_down[i] = Some(((period_f - periods_since_low as f64) / period_f) * 100.0);
88 }
89 }
90
91 Ok(AroonResult {
92 aroon_up,
93 aroon_down,
94 })
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_aroon() {
103 let highs = vec![10.0, 11.0, 12.0, 11.0, 10.0];
104 let lows = vec![8.0, 9.0, 10.0, 9.0, 8.0];
105 let result = aroon(&highs, &lows, 3).unwrap();
106
107 assert_eq!(result.aroon_up.len(), 5);
108 assert!(result.aroon_up[0].is_none());
109 assert!(result.aroon_up[1].is_none());
110 assert!(result.aroon_up[2].is_some());
111 }
112}