finlib_ta/indicators/
on_balance_volume.rs

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