ux_indicators/indicators/
mfi.rs

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