finance_query/indicators/
mfi.rs1use super::{IndicatorError, Result};
4
5pub fn mfi(
29 highs: &[f64],
30 lows: &[f64],
31 closes: &[f64],
32 volumes: &[f64],
33 period: usize,
34) -> Result<Vec<Option<f64>>> {
35 if period == 0 {
36 return Err(IndicatorError::InvalidPeriod(
37 "Period must be greater than 0".to_string(),
38 ));
39 }
40 let len = highs.len();
41 if lows.len() != len || closes.len() != len || volumes.len() != len {
42 return Err(IndicatorError::InvalidPeriod(
43 "Data lengths must match".to_string(),
44 ));
45 }
46 if len < period + 1 {
47 return Err(IndicatorError::InsufficientData {
48 need: period + 1,
49 got: len,
50 });
51 }
52
53 let mut typical_prices = Vec::with_capacity(len);
54 for i in 0..len {
55 typical_prices.push((highs[i] + lows[i] + closes[i]) / 3.0);
56 }
57
58 let mut result = vec![None; len];
59
60 let mut raw_money_flow = Vec::with_capacity(len);
61 raw_money_flow.push(0.0);
62
63 for i in 1..len {
64 raw_money_flow.push(typical_prices[i] * volumes[i]);
65 }
66
67 let mut positive_flow = 0.0;
68 let mut negative_flow = 0.0;
69
70 for i in 1..=period {
71 if typical_prices[i] > typical_prices[i - 1] {
72 positive_flow += raw_money_flow[i];
73 } else if typical_prices[i] < typical_prices[i - 1] {
74 negative_flow += raw_money_flow[i];
75 }
76 }
77
78 if negative_flow == 0.0 {
79 result[period] = Some(100.0);
80 } else {
81 let money_ratio = positive_flow / negative_flow;
82 result[period] = Some(100.0 - (100.0 / (1.0 + money_ratio)));
83 }
84
85 for i in (period + 1)..len {
86 let old_idx = i - period;
87 if typical_prices[old_idx] > typical_prices[old_idx - 1] {
88 positive_flow -= raw_money_flow[old_idx];
89 } else if typical_prices[old_idx] < typical_prices[old_idx - 1] {
90 negative_flow -= raw_money_flow[old_idx];
91 }
92
93 if typical_prices[i] > typical_prices[i - 1] {
94 positive_flow += raw_money_flow[i];
95 } else if typical_prices[i] < typical_prices[i - 1] {
96 negative_flow += raw_money_flow[i];
97 }
98
99 if negative_flow == 0.0 {
100 result[i] = Some(100.0);
101 } else {
102 let money_ratio = positive_flow / negative_flow;
103 result[i] = Some(100.0 - (100.0 / (1.0 + money_ratio)));
104 }
105 }
106
107 Ok(result)
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_mfi() {
116 let highs = vec![10.0, 11.0, 12.0, 11.0, 10.0];
117 let lows = vec![8.0, 9.0, 10.0, 9.0, 8.0];
118 let closes = vec![9.0, 10.0, 11.0, 10.0, 9.0];
119 let volumes = vec![100.0, 200.0, 150.0, 100.0, 50.0];
120 let result = mfi(&highs, &lows, &closes, &volumes, 3).unwrap();
121
122 assert_eq!(result.len(), 5);
123 assert!(result[2].is_none());
124 assert!(result[3].is_some());
125 }
126}