use super::{ChartSeries, check_close};
use crate::errors::QlResult;
use crate::require;
use crate::types::Real;
fn check_volume(prices: &[Real], volume: &[Real]) -> QlResult<()> {
require!(
prices.len() == volume.len(),
"chart price/volume lengths differ"
);
check_close(prices)?;
for (index, value) in volume.iter().enumerate() {
require!(value.is_finite(), "nonfinite volume at index {index}");
require!(
value.partial_cmp(&0.0) != Some(std::cmp::Ordering::Less),
"negative volume at index {index}"
);
}
Ok(())
}
pub fn vwap(price: &[Real], volume: &[Real]) -> QlResult<ChartSeries> {
check_volume(price, volume)?;
let mut result = ChartSeries::zeroed(price.len(), price.len())?;
let mut total = 0.0;
let mut mean: Real = 0.0;
for (index, (&value, &weight)) in price.iter().zip(volume).enumerate() {
if weight > 0.0 {
let next_total = total + weight;
require!(
next_total.is_finite(),
"nonfinite cumulative volume at index {index}"
);
if total == 0.0 {
mean = value;
result.first_valid = index;
} else {
let fraction = weight / next_total;
let previous_fraction = total / next_total;
let (retained, incoming) = if mean.is_sign_positive() == value.is_sign_positive() {
if fraction <= 0.5 {
(mean, (value - mean) * fraction)
} else {
(value, (mean - value) * previous_fraction)
}
} else {
(mean * previous_fraction, value * fraction)
};
require!(
retained.is_finite() && incoming.is_finite(),
"nonfinite VWAP arithmetic at index {index}"
);
mean = retained + incoming;
require!(mean.is_finite(), "nonfinite VWAP at index {index}");
}
total = next_total;
}
if index >= result.first_valid {
result.values[index] = mean;
}
}
Ok(result)
}
pub fn obv(close: &[Real], volume: &[Real]) -> QlResult<ChartSeries> {
check_volume(close, volume)?;
let mut result = ChartSeries::zeroed(close.len(), 0)?;
let mut value = 0.0;
for index in 1..close.len() {
if close[index] > close[index - 1] {
value += volume[index];
} else if close[index] < close[index - 1] {
value -= volume[index];
}
require!(value.is_finite(), "nonfinite OBV at index {index}");
result.values[index] = value;
}
Ok(result)
}
#[cfg(test)]
#[path = "vwap_obv_tests.rs"]
mod tests;