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