use std::collections::VecDeque;
use crate::bar_indicators::indicator_value::IndicatorValue;
#[derive(Debug, Clone)]
pub struct Vwma {
period: usize,
pv_buf: VecDeque<f64>,
v_buf: VecDeque<f64>,
sum_pv: f64,
sum_v: f64,
value: f64,
}
impl Vwma {
pub fn new(period: usize) -> Self {
let p = period.max(1);
Self {
period: p,
pv_buf: VecDeque::with_capacity(p),
v_buf: VecDeque::with_capacity(p),
sum_pv: 0.0,
sum_v: 0.0,
value: 0.0,
}
}
pub fn update_bar(&mut self, _o: f64, _h: f64, _l: f64, c: f64, v: f64) -> f64 {
let pv = c * v;
self.pv_buf.push_back(pv);
self.v_buf.push_back(v);
self.sum_pv += pv;
self.sum_v += v;
if self.pv_buf.len() > self.period {
if let Some(x) = self.pv_buf.pop_front() {
self.sum_pv -= x;
}
if let Some(x) = self.v_buf.pop_front() {
self.sum_v -= x;
}
}
if self.sum_v > 0.0 {
self.value = self.sum_pv / self.sum_v;
}
self.value
}
pub fn value(&self) -> IndicatorValue {
IndicatorValue::Single(self.value)
}
pub fn is_ready(&self) -> bool {
self.pv_buf.len() == self.period && self.sum_v > 0.0
}
pub fn reset(&mut self) {
self.pv_buf.clear();
self.v_buf.clear();
self.sum_pv = 0.0;
self.sum_v = 0.0;
self.value = 0.0;
}
pub fn period(&self) -> usize {
self.period
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vwma_basic_calculation() {
let mut vwma = Vwma::new(3);
vwma.update_bar(0.0, 0.0, 0.0, 100.0, 1000.0);
vwma.update_bar(0.0, 0.0, 0.0, 110.0, 2000.0);
let v3 = vwma.update_bar(0.0, 0.0, 0.0, 105.0, 1000.0);
assert!(vwma.is_ready());
assert!((v3 - 106.25).abs() < 0.01);
}
#[test]
fn test_vwma_zero_volume() {
let mut vwma = Vwma::new(2);
vwma.update_bar(0.0, 0.0, 0.0, 100.0, 0.0);
vwma.update_bar(0.0, 0.0, 0.0, 110.0, 0.0);
assert!(!vwma.is_ready());
}
#[test]
fn test_vwma_reset() {
let mut vwma = Vwma::new(3);
vwma.update_bar(0.0, 0.0, 0.0, 100.0, 1000.0);
vwma.update_bar(0.0, 0.0, 0.0, 110.0, 1000.0);
vwma.update_bar(0.0, 0.0, 0.0, 120.0, 1000.0);
assert!(vwma.is_ready());
vwma.reset();
assert!(!vwma.is_ready());
}
}