quantaxis_rs/indicators/
on_balance_volume.rs

1use std::fmt;
2
3use crate::{Close, Next, Reset, Volume};
4
5/// On Balance Volume (OBV).
6///
7/// The OBV is an volume and price based oscillator which gives cumulative total volumes.
8/// OBV measures buying and selling pressure as a cumulative indicator,
9/// adding volume on up days and subtracting it on down days.
10///
11/// # Formula
12///
13/// If the closing price is above the prior close price then:
14/// Current OBV = Previous OBV + Current Volume
15///
16/// If the closing price is below the prior close price then:
17/// Current OBV = Previous OBV  -  Current Volume
18///
19/// If the closing prices equals the prior close price then:
20/// Current OBV = Previous OBV
21///
22/// Where:
23///
24/// obv - on the balance volume
25///
26/// # Example
27///
28/// ```
29/// use quantaxis_rs::indicators::OnBalanceVolume;
30/// use quantaxis_rs::{Next, DataItem};
31///
32/// let mut obv = OnBalanceVolume::new();
33///
34/// let di1 = DataItem::builder()
35///             .high(3.0)
36///             .low(1.0)
37///             .close(2.0)
38///             .open(1.5)
39///             .volume(1000.0)
40///             .build().unwrap();
41///
42/// let di2 = DataItem::builder()
43///             .high(3.0)
44///             .low(1.0)
45///             .close(1.5)
46///             .open(1.5)
47///             .volume(300.0)
48///             .build().unwrap();
49///
50/// assert_eq!(obv.next(&di1), 1000.0);
51/// assert_eq!(obv.next(&di2), 700.0);
52/// ```
53///
54/// # Links
55///
56/// * [On Balance Volume, Wikipedia](https://en.wikipedia.org/wiki/On-balance_volume)
57/// * [On Balance Volume, stockcharts](https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:on_balance_volume_obv)
58
59#[derive(Debug, Clone)]
60pub struct OnBalanceVolume {
61    obv: f64,
62    prev_close: f64,
63}
64
65impl OnBalanceVolume {
66    pub fn new() -> Self {
67        Self {
68            obv: 0.0,
69            prev_close: 0.0,
70        }
71    }
72}
73
74impl<'a, T: Close + Volume> Next<&'a T> for OnBalanceVolume {
75    type Output = f64;
76
77    fn next(&mut self, input: &'a T) -> f64 {
78        if input.close() > self.prev_close {
79            self.obv = self.obv + input.volume();
80        } else if input.close() < self.prev_close {
81            self.obv = self.obv - input.volume();
82        }
83        self.prev_close = input.close();
84        self.obv
85    }
86}
87
88impl Default for OnBalanceVolume {
89    fn default() -> Self {
90        Self::new()
91    }
92}
93
94impl fmt::Display for OnBalanceVolume {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        write!(f, "OBV")
97    }
98}
99
100impl Reset for OnBalanceVolume {
101    fn reset(&mut self) {
102        self.obv = 0.0;
103        self.prev_close = 0.0;
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110    use crate::test_helper::*;
111
112    #[test]
113    fn test_next_bar() {
114        let mut obv = OnBalanceVolume::new();
115
116        let bar1 = Bar::new().close(1.5).volume(1000.0);
117        let bar2 = Bar::new().close(5).volume(5000.0);
118        let bar3 = Bar::new().close(4).volume(9000.0);
119        let bar4 = Bar::new().close(4).volume(4000.0);
120
121        assert_eq!(obv.next(&bar1), 1000.0);
122
123        //close > prev_close
124        assert_eq!(obv.next(&bar2), 6000.0);
125
126        // close < prev_close
127        assert_eq!(obv.next(&bar3), -3000.0);
128
129        // close == prev_close
130        assert_eq!(obv.next(&bar4), -3000.0);
131    }
132
133    #[test]
134    fn test_reset() {
135        let mut obv = OnBalanceVolume::new();
136
137        let bar1 = Bar::new().close(1.5).volume(1000.0);
138        let bar2 = Bar::new().close(4).volume(2000.0);
139        let bar3 = Bar::new().close(8).volume(3000.0);
140
141        assert_eq!(obv.next(&bar1), 1000.0);
142        assert_eq!(obv.next(&bar2), 3000.0);
143        assert_eq!(obv.next(&bar3), 6000.0);
144
145        obv.reset();
146
147        assert_eq!(obv.next(&bar1), 1000.0);
148        assert_eq!(obv.next(&bar2), 3000.0);
149        assert_eq!(obv.next(&bar3), 6000.0);
150    }
151
152    #[test]
153    fn test_default() {
154        OnBalanceVolume::default();
155    }
156
157    #[test]
158    fn test_display() {
159        let obv = OnBalanceVolume::new();
160        assert_eq!(format!("{}", obv), "OBV");
161    }
162
163}