ux_indicators/indicators/
obv.rs

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