use crate::bar_indicators::indicator_value::IndicatorValue;
#[derive(Clone, Copy, Debug)]
pub enum AvwapAnchorMode {
Monthly,
}
#[derive(Clone, Copy, Debug)]
pub struct AnchoredVwapParams {
pub mode: AvwapAnchorMode,
}
impl Default for AnchoredVwapParams {
fn default() -> Self {
Self {
mode: AvwapAnchorMode::Monthly,
}
}
}
#[derive(Clone)]
pub struct AnchoredVwap {
#[allow(dead_code)]
params: AnchoredVwapParams,
cum_pv: f64,
cum_v: f64,
value: f64,
last_month_key: i32,
is_ready: bool,
}
impl AnchoredVwap {
pub fn new(params: AnchoredVwapParams) -> Self {
Self {
params,
cum_pv: 0.0,
cum_v: 0.0,
value: 0.0,
last_month_key: i32::MIN,
is_ready: false,
}
}
#[inline]
pub fn reset(&mut self) {
self.cum_pv = 0.0;
self.cum_v = 0.0;
self.value = 0.0;
self.last_month_key = i32::MIN;
self.is_ready = false;
}
pub fn update_bar(
&mut self,
_open: f64,
high: f64,
low: f64,
close: f64,
volume: f64,
unix_time_secs: i64,
) -> f64 {
let month_key = Self::calc_month_key(unix_time_secs);
if self.last_month_key != month_key {
self.cum_pv = 0.0;
self.cum_v = 0.0;
self.is_ready = false;
self.last_month_key = month_key;
}
let tp = (high + low + close) / 3.0;
self.cum_pv += tp * volume.max(0.0);
self.cum_v += volume.max(0.0);
if self.cum_v > 0.0 {
self.value = self.cum_pv / self.cum_v;
self.is_ready = true;
}
self.value
}
#[inline]
pub fn value(&self) -> IndicatorValue {
IndicatorValue::Single(self.value)
}
#[inline]
pub fn is_ready(&self) -> bool {
self.is_ready
}
#[inline]
fn calc_month_key(unix_time_secs: i64) -> i32 {
let days = unix_time_secs / 86_400;
let approx_months_since_epoch = (days as f64 / 30.436875) as i64;
approx_months_since_epoch as i32
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_anchored_vwap_creation() {
let avwap = AnchoredVwap::new(AnchoredVwapParams::default());
assert!(!avwap.is_ready());
assert_eq!(avwap.value().main(), 0.0);
}
#[test]
fn test_anchored_vwap_update() {
let mut avwap = AnchoredVwap::new(AnchoredVwapParams::default());
let ts = 1700000000_i64; let value = avwap.update_bar(100.0, 102.0, 98.0, 101.0, 1000.0, ts);
assert!(avwap.is_ready());
assert!(value > 0.0);
}
#[test]
fn test_anchored_vwap_accumulation() {
let mut avwap = AnchoredVwap::new(AnchoredVwapParams::default());
let ts = 1700000000_i64;
for i in 0..10 {
let price = 100.0 + i as f64;
avwap.update_bar(price, price + 1.0, price - 1.0, price, 1000.0, ts + i * 86400);
}
assert!(avwap.is_ready());
let v = avwap.value().main();
assert!(v > 100.0 && v < 110.0, "VWAP should be within price range");
}
#[test]
fn test_anchored_vwap_reset() {
let mut avwap = AnchoredVwap::new(AnchoredVwapParams::default());
avwap.update_bar(100.0, 102.0, 98.0, 101.0, 1000.0, 1700000000);
avwap.reset();
assert!(!avwap.is_ready());
assert_eq!(avwap.value().main(), 0.0);
}
}