quantaxis_rs/indicators/
money_flow_index.rs

1use std::collections::VecDeque;
2use std::fmt;
3
4use crate::errors::*;
5use crate::{Close, High, Low, Next, Reset, Volume};
6
7/// Money Flow Index (MFI).
8///
9/// The MFI is an volume and price based oscillator which gives moneyflow over n periods.
10/// MFI is used to measure buying and selling pressure.
11/// MFI is also known as volume-weighted RSI.
12///
13/// # Formula
14///
15/// Typical Price(TP) = (High + Low + Close)/3
16///
17/// Money Flow(MF) = Typical Price x Volume
18///
19/// MF is positive when currennt TP is greater that previous period TP and
20/// negative when current TP is less than preivous TP.
21///
22/// Positive money flow (PMF)- calculated by adding the money flow of all the days RMF is positive.
23///
24/// Negative money flow (NMF)- calculated by adding the money flow of all the days RMF is negative.
25///
26/// Money Flow Index(MFI) = PMF / (PMF + NMF) * 100
27///
28///
29/// # Parameters
30///
31/// * _n_ - number of periods, integer greater than 0
32///
33/// # Example
34///
35/// ```
36/// use quantaxis_rs::indicators::MoneyFlowIndex;
37/// use quantaxis_rs::{Next, DataItem};
38///
39/// let mut mfi = MoneyFlowIndex::new(3).unwrap();
40/// let di = DataItem::builder()
41///             .high(3.0)
42///             .low(1.0)
43///             .close(2.0)
44///             .open(1.5)
45///             .volume(1000.0)
46///             .build().unwrap();
47/// mfi.next(&di);
48///
49/// ```
50/// # Links
51/// * [Money Flow Index, Wikipedia](https://en.wikipedia.org/wiki/Money_flow_index)
52/// * [Money Flow Index, stockcharts](https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:money_flow_index_mfi)
53
54#[derive(Debug, Clone)]
55pub struct MoneyFlowIndex {
56    n: u32,
57    money_flows: VecDeque<f64>,
58    prev_typical_price: f64,
59    total_positive_money_flow: f64,
60    total_absolute_money_flow: f64,
61    is_new: bool,
62}
63
64impl MoneyFlowIndex {
65    pub fn new(n: u32) -> Result<Self> {
66        match n {
67            0 => Err(Error::from_kind(ErrorKind::InvalidParameter)),
68            _ => {
69                let indicator = Self {
70                    n: n,
71                    money_flows: VecDeque::with_capacity(n as usize + 1),
72                    prev_typical_price: 0.0,
73                    total_positive_money_flow: 0.0,
74                    total_absolute_money_flow: 0.0,
75                    is_new: true,
76                };
77                Ok(indicator)
78            }
79        }
80    }
81}
82
83impl<'a, T: High + Low + Close + Volume> Next<&'a T> for MoneyFlowIndex {
84    type Output = f64;
85
86    fn next(&mut self, input: &'a T) -> f64 {
87        let typical_price = (input.high() + input.low() + input.close()) / 3.0;
88
89        if self.is_new {
90            // money flow is 0, because without having previous typical_price
91            // it is not possible to determine is it positive or negative.
92            self.money_flows.push_back(0.0);
93            self.prev_typical_price = typical_price;
94            self.is_new = false;
95            return 50.0;
96        } else {
97            let money_flow = typical_price * input.volume();
98
99            let signed_money_flow = if typical_price >= self.prev_typical_price {
100                self.total_positive_money_flow += money_flow;
101                money_flow
102            } else {
103                -money_flow
104            };
105
106            self.total_absolute_money_flow += money_flow;
107
108            if self.money_flows.len() == (self.n as usize) {
109                let old_signed_money_flow = self.money_flows.pop_front().unwrap();
110                if old_signed_money_flow > 0.0 {
111                    self.total_positive_money_flow -= old_signed_money_flow;
112                    self.total_absolute_money_flow -= old_signed_money_flow;
113                } else {
114                    // it is actually subtraction, because old_signed_money_flow is negative
115                    self.total_absolute_money_flow += old_signed_money_flow;
116                }
117            }
118
119            self.money_flows.push_back(signed_money_flow);
120            self.prev_typical_price = typical_price;
121
122            (self.total_positive_money_flow / self.total_absolute_money_flow) * 100.0
123        }
124    }
125}
126
127impl Default for MoneyFlowIndex {
128    fn default() -> Self {
129        Self::new(14).unwrap()
130    }
131}
132
133impl fmt::Display for MoneyFlowIndex {
134    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135        write!(f, "MFI({})", self.n)
136    }
137}
138
139impl Reset for MoneyFlowIndex {
140    fn reset(&mut self) {
141        self.money_flows.clear();
142        self.prev_typical_price = 0.0;
143        self.total_positive_money_flow = 0.0;
144        self.total_absolute_money_flow = 0.0;
145        self.is_new = true;
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152    use crate::test_helper::*;
153    macro_rules! test_indicator {
154        ($i:tt) => {
155            #[test]
156            fn test_indicator() {
157                let bar = Bar::new();
158
159                // ensure Default trait is implemented
160                let mut indicator = $i::default();
161
162                // ensure Next<f64> is implemented
163                let first_output = indicator.next(12.3);
164
165                // ensure next accepts &DataItem as well
166                indicator.next(&bar);
167
168                // ensure Reset is implemented and works correctly
169                indicator.reset();
170                assert_eq!(indicator.next(12.3), first_output);
171
172                // ensure Display is implemented
173                format!("{}", indicator);
174            }
175        };
176    }
177    #[test]
178    fn test_new() {
179        assert!(MoneyFlowIndex::new(0).is_err());
180        assert!(MoneyFlowIndex::new(1).is_ok());
181    }
182
183    #[test]
184    fn test_next_bar() {
185        let mut mfi = MoneyFlowIndex::new(3).unwrap();
186
187        // tp = 2.0
188        let bar1 = Bar::new().high(3).low(1).close(2).volume(500.0);
189        assert_eq!(mfi.next(&bar1), 50.0);
190
191        // tp = 2.2, fm = 2.2*1000 = 2200, abs_total = 2200, pos_total = 2200
192        let bar2 = Bar::new().high(2.3).low(2.0).close(2.3).volume(1000.0);
193        assert_eq!(mfi.next(&bar2), 100.0);
194
195        // tp = 8.0, fm = 8*200 = 1600, abs_total = 3800, pos_total = 3800
196        let bar3 = Bar::new().high(9).low(7).close(8).volume(200.0);
197        assert_eq!(mfi.next(&bar3), 100.0);
198
199        // tp = 4.0, fm = -4.0*500 = -2000, abs_total = 5800 , pos_total = 3800
200        let bar4 = Bar::new().high(5).low(3).close(4).volume(500.0);
201        assert_eq!(mfi.next(&bar4), 3800.0 / 5800.0 * 100.0);
202
203        // tp = 3.0, fm = -3 * 5000 = -15000, abs_total = 5800+15000-2200=18600, pos_total=3800-2200=1600
204        let bar5 = Bar::new().high(4).low(2).close(3).volume(5000.0);
205        assert_eq!(mfi.next(&bar5), 1600.0 / 18600.0 * 100.0);
206
207        // tp = 1.5, fm = -1.5*6000= -9000, abs_total=18600+9000-1600=26000, pos_total=0
208        let bar6 = Bar::new().high(2).low(1).close(1.5).volume(6000.0);
209        assert_eq!(mfi.next(&bar6), 0.0 / 23800.0 * 100.0);
210
211        // tp = 2, fm = 2*7000=14000, abs_total=26000+14000-2000=38000, pos_total=14000
212        let bar7 = Bar::new().high(2).low(2).close(2).volume(7000.0);
213        assert_eq!(mfi.next(&bar7), 14000.0 / 38000.0 * 100.0);
214    }
215
216    #[test]
217    fn test_reset() {
218        let mut mfi = MoneyFlowIndex::new(3).unwrap();
219
220        let bar1 = Bar::new().high(2).low(1).close(1.5).volume(1000.0);
221        let bar2 = Bar::new().high(5).low(3).close(4).volume(2000.0);
222        let bar3 = Bar::new().high(9).low(7).close(8).volume(3000.0);
223        let bar4 = Bar::new().high(5).low(3).close(4).volume(4000.0);
224        let bar5 = Bar::new().high(5).low(3).close(4).volume(5000.0);
225        let bar6 = Bar::new().high(2).low(1).close(1.5).volume(6000.0);
226
227        assert_eq!(mfi.next(&bar1), 50.0);
228        assert_eq!(mfi.next(&bar2), 100.0);
229        assert_eq!(mfi.next(&bar3), 100.0);
230        assert_eq!(round(mfi.next(&bar4)), 66.667);
231        assert_eq!(round(mfi.next(&bar5)), 73.333);
232        assert_eq!(round(mfi.next(&bar6)), 44.444);
233
234        mfi.reset();
235
236        assert_eq!(mfi.next(&bar1), 50.0);
237        assert_eq!(mfi.next(&bar2), 100.0);
238        assert_eq!(mfi.next(&bar3), 100.0);
239        assert_eq!(round(mfi.next(&bar4)), 66.667);
240        assert_eq!(round(mfi.next(&bar5)), 73.333);
241        assert_eq!(round(mfi.next(&bar6)), 44.444);
242    }
243
244    #[test]
245    fn test_default() {
246        MoneyFlowIndex::default();
247    }
248
249    #[test]
250    fn test_display() {
251        let mfi = MoneyFlowIndex::new(10).unwrap();
252        assert_eq!(format!("{}", mfi), "MFI(10)");
253    }
254
255}