74 lines
1.8 KiB
Rust
74 lines
1.8 KiB
Rust
use std::{
|
|
borrow::Borrow,
|
|
iter::{Peekable, Scan},
|
|
};
|
|
|
|
pub struct ObvState {
|
|
last: f64,
|
|
obv: f64,
|
|
}
|
|
|
|
#[allow(clippy::type_complexity)]
|
|
pub trait Obv<T>: Iterator + Sized {
|
|
fn obv(self) -> Scan<Peekable<Self>, ObvState, fn(&mut ObvState, T) -> Option<f64>>;
|
|
}
|
|
|
|
impl<I, T> Obv<T> for I
|
|
where
|
|
I: Iterator<Item = T>,
|
|
T: Borrow<(f64, f64)>,
|
|
{
|
|
fn obv(self) -> Scan<Peekable<Self>, ObvState, fn(&mut ObvState, T) -> Option<f64>> {
|
|
let mut iter = self.peekable();
|
|
let first = iter.peek().map(|value| *value.borrow()).unwrap_or_default();
|
|
|
|
iter.scan(
|
|
ObvState {
|
|
last: first.0,
|
|
obv: 0.0,
|
|
},
|
|
|state: &mut ObvState, value: T| {
|
|
let (close, volume) = *value.borrow();
|
|
|
|
if close > state.last {
|
|
state.obv += volume;
|
|
} else if close < state.last {
|
|
state.obv -= volume;
|
|
}
|
|
state.last = close;
|
|
|
|
Some(state.obv)
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_obv() {
|
|
let data = vec![(1.0, 1.0), (2.0, 2.0), (3.0, 3.0), (2.0, 4.0), (1.0, 5.0)];
|
|
let obv = data.into_iter().obv().collect::<Vec<_>>();
|
|
|
|
assert_eq!(obv, vec![0.0, 2.0, 5.0, 1.0, -4.0]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_obv_empty() {
|
|
let data = Vec::<(f64, f64)>::new();
|
|
let obv = data.into_iter().obv().collect::<Vec<_>>();
|
|
|
|
assert_eq!(obv, Vec::<f64>::new());
|
|
}
|
|
|
|
#[test]
|
|
fn test_obv_borrow() {
|
|
let data = [(1.0, 1.0), (2.0, 2.0), (3.0, 3.0), (2.0, 4.0), (1.0, 5.0)];
|
|
let obv = data.iter().obv().collect::<Vec<_>>();
|
|
|
|
assert_eq!(obv, vec![0.0, 2.0, 5.0, 1.0, -4.0]);
|
|
}
|
|
}
|