use crate::{KandError, TAFloat};
pub const fn lookback() -> Result<usize, KandError> {
Ok(0)
}
pub fn obv(
input_close: &[TAFloat],
input_volume: &[TAFloat],
output_obv: &mut [TAFloat],
) -> Result<(), KandError> {
let len = input_close.len();
let lookback = lookback()?;
#[cfg(feature = "check")]
{
if len == 0 {
return Err(KandError::InvalidData);
}
if len <= lookback {
return Err(KandError::InsufficientData);
}
if len != input_volume.len() || len != output_obv.len() {
return Err(KandError::LengthMismatch);
}
}
#[cfg(feature = "deep-check")]
{
for i in 0..len {
if input_close[i].is_nan() || input_volume[i].is_nan() {
return Err(KandError::NaNDetected);
}
}
}
let mut obv = input_volume[lookback];
output_obv[lookback] = obv;
for i in (lookback + 1)..len {
obv = if input_close[i] > input_close[i - 1] {
obv + input_volume[i]
} else if input_close[i] < input_close[i - 1] {
obv - input_volume[i]
} else {
obv
};
output_obv[i] = obv;
}
Ok(())
}
pub fn obv_inc(
input_curr_close: TAFloat,
prev_close: TAFloat,
input_volume: TAFloat,
prev_obv: TAFloat,
) -> Result<TAFloat, KandError> {
#[cfg(feature = "deep-check")]
{
if input_curr_close.is_nan()
|| prev_close.is_nan()
|| input_volume.is_nan()
|| prev_obv.is_nan()
{
return Err(KandError::NaNDetected);
}
}
Ok(if input_curr_close > prev_close {
prev_obv + input_volume
} else if input_curr_close < prev_close {
prev_obv - input_volume
} else {
prev_obv
})
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
#[test]
fn test_obv_calculation() {
let input_close = vec![
35216.1, 35221.4, 35190.7, 35170.0, 35181.5, 35254.6, 35202.8, 35251.9, 35197.6,
35184.7, 35175.1, 35229.9, 35212.5, 35160.7, 35090.3, 35041.2, 34999.3, 35013.4,
35069.0, 35024.6,
];
let input_volume = vec![
1055.365, 756.488, 682.152, 1197.747, 425.97, 859.638, 741.925, 888.477, 1043.333,
467.901, 387.47, 566.099, 672.296, 834.915, 1854.024, 3670.795, 3761.198, 1605.442,
1726.574, 934.713,
];
let mut output_obv = vec![0.0; input_close.len()];
obv(&input_close, &input_volume, &mut output_obv).unwrap();
let expected_values = [
1055.365, 1811.853, 1129.701, -68.046, 357.924, 1217.562, 475.637, 1364.114, 320.781,
-147.12, -534.59, 31.509, -640.787, -1475.702, -3329.726, -7000.521, -10761.719,
-9156.277, -7429.703, -8364.416,
];
for (i, expected) in expected_values.iter().enumerate() {
assert_relative_eq!(output_obv[i], *expected, epsilon = 0.00001);
}
let mut prev_obv = output_obv[0];
for i in 1..input_close.len() {
let result = obv_inc(
input_close[i],
input_close[i - 1],
input_volume[i],
prev_obv,
)
.unwrap();
assert_relative_eq!(result, output_obv[i], epsilon = 0.00001);
prev_obv = result;
}
}
}