finlib_ta/indicators/
money_flow_index.rs1use alloc::boxed::Box;
2use alloc::vec;
3use core::fmt;
4
5use crate::errors::{Result, TaError};
6use crate::{Close, High, Low, Next, Period, Reset, Volume};
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11#[doc(alias = "MFI")]
59#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
60#[derive(Debug, Clone)]
61pub struct MoneyFlowIndex {
62 period: usize,
63 index: usize,
64 count: usize,
65 previous_typical_price: f64,
66 total_positive_money_flow: f64,
67 total_negative_money_flow: f64,
68 deque: Box<[f64]>,
69}
70
71impl MoneyFlowIndex {
72 pub fn new(period: usize) -> Result<Self> {
73 match period {
74 0 => Err(TaError::InvalidParameter),
75 _ => Ok(Self {
76 period,
77 index: 0,
78 count: 0,
79 previous_typical_price: 0.0,
80 total_positive_money_flow: 0.0,
81 total_negative_money_flow: 0.0,
82 deque: vec![0.0; period].into_boxed_slice(),
83 }),
84 }
85 }
86}
87
88impl Period for MoneyFlowIndex {
89 fn period(&self) -> usize {
90 self.period
91 }
92}
93
94impl<T: High + Low + Close + Volume> Next<&T> for MoneyFlowIndex {
95 type Output = f64;
96
97 fn next(&mut self, input: &T) -> f64 {
98 let tp = (input.close() + input.high() + input.low()) / 3.0;
99
100 self.index = if self.index + 1 < self.period {
101 self.index + 1
102 } else {
103 0
104 };
105
106 if self.count < self.period {
107 self.count = self.count + 1;
108 if self.count == 1 {
109 self.previous_typical_price = tp;
110 return 50.0;
111 }
112 } else {
113 let popped = self.deque[self.index];
114 if popped.is_sign_positive() {
115 self.total_positive_money_flow -= popped;
116 } else {
117 self.total_negative_money_flow += popped;
118 }
119 }
120
121 if tp > self.previous_typical_price {
122 let raw_money_flow = tp * input.volume();
123 self.total_positive_money_flow += raw_money_flow;
124 self.deque[self.index] = raw_money_flow;
125 } else if tp < self.previous_typical_price {
126 let raw_money_flow = tp * input.volume();
127 self.total_negative_money_flow += raw_money_flow;
128 self.deque[self.index] = -raw_money_flow;
129 } else {
130 self.deque[self.index] = 0.0;
131 }
132 self.previous_typical_price = tp;
133
134 self.total_positive_money_flow
135 / (self.total_positive_money_flow + self.total_negative_money_flow)
136 * 100.0
137 }
138}
139
140impl Default for MoneyFlowIndex {
141 fn default() -> Self {
142 Self::new(14).unwrap()
143 }
144}
145
146impl fmt::Display for MoneyFlowIndex {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 write!(f, "MFI({})", self.period)
149 }
150}
151
152impl Reset for MoneyFlowIndex {
153 fn reset(&mut self) {
154 self.index = 0;
155 self.count = 0;
156 self.previous_typical_price = 0.0;
157 self.total_positive_money_flow = 0.0;
158 self.total_negative_money_flow = 0.0;
159 for i in 0..self.period {
160 self.deque[i] = 0.0;
161 }
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::test_helper::*;
169 use alloc::format;
170
171 #[test]
172 fn test_new() {
173 assert!(MoneyFlowIndex::new(0).is_err());
174 assert!(MoneyFlowIndex::new(1).is_ok());
175 }
176
177 #[test]
178 fn test_next_bar() {
179 let mut mfi = MoneyFlowIndex::new(3).unwrap();
180
181 let bar1 = Bar::new().high(3).low(1).close(2).volume(500.0);
182 assert_eq!(round(mfi.next(&bar1)), 50.0);
183
184 let bar2 = Bar::new().high(2.3).low(2.0).close(2.3).volume(1000.0);
185 assert_eq!(round(mfi.next(&bar2)), 100.0);
186
187 let bar3 = Bar::new().high(9).low(7).close(8).volume(200.0);
188 assert_eq!(round(mfi.next(&bar3)), 100.0);
189
190 let bar4 = Bar::new().high(5).low(3).close(4).volume(500.0);
191 assert_eq!(round(mfi.next(&bar4)), 65.517);
192
193 let bar5 = Bar::new().high(4).low(2).close(3).volume(5000.0);
194 assert_eq!(round(mfi.next(&bar5)), 8.602);
195
196 let bar6 = Bar::new().high(2).low(1).close(1.5).volume(6000.0);
197 assert_eq!(round(mfi.next(&bar6)), 0.0);
198
199 let bar7 = Bar::new().high(2).low(2).close(2).volume(7000.0);
200 assert_eq!(round(mfi.next(&bar7)), 36.842);
201
202 let bar8 = Bar::new().high(2).low(2).close(2).volume(7000.0);
203 assert_eq!(round(mfi.next(&bar8)), 60.87);
204 }
205
206 #[test]
207 fn test_reset() {
208 let mut mfi = MoneyFlowIndex::new(3).unwrap();
209
210 let bar1 = Bar::new().high(3).low(1).close(2).volume(500.0);
211 let bar2 = Bar::new().high(2.3).low(2.0).close(2.3).volume(1000.0);
212
213 assert_eq!(round(mfi.next(&bar1)), 50.0);
214 assert_eq!(round(mfi.next(&bar2)), 100.0);
215
216 mfi.reset();
217
218 assert_eq!(round(mfi.next(&bar1)), 50.0);
219 assert_eq!(round(mfi.next(&bar2)), 100.0);
220 }
221
222 #[test]
223 fn test_default() {
224 MoneyFlowIndex::default();
225 }
226
227 #[test]
228 fn test_display() {
229 let mfi = MoneyFlowIndex::new(10).unwrap();
230 assert_eq!(format!("{}", mfi), "MFI(10)");
231 }
232}