Skip to main content

quantwave_core/indicators/
vwap.rs

1use crate::indicators::metadata::IndicatorMetadata;
2use crate::traits::Next;
3
4#[derive(Debug, Clone)]
5pub struct AnchoredVWAP {
6    cumulative_tp_v: f64,
7    cumulative_v: f64,
8}
9
10impl AnchoredVWAP {
11    pub fn new() -> Self {
12        Self {
13            cumulative_tp_v: 0.0,
14            cumulative_v: 0.0,
15        }
16    }
17}
18
19impl Next<(f64, f64, bool)> for AnchoredVWAP {
20    type Output = f64;
21
22    fn next(&mut self, (price, volume, anchor): (f64, f64, bool)) -> Self::Output {
23        if anchor {
24            self.cumulative_tp_v = price * volume;
25            self.cumulative_v = volume;
26        } else {
27            self.cumulative_tp_v += price * volume;
28            self.cumulative_v += volume;
29        }
30
31        if self.cumulative_v == 0.0 {
32            0.0
33        } else {
34            self.cumulative_tp_v / self.cumulative_v
35        }
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn test_basic_vwap_no_reset() {
45        let mut vwap = AnchoredVWAP::new();
46        // bar 1: price 10, vol 100 -> vwap 10
47        // bar 2: price 12, vol 200 -> vwap (10*100 + 12*200) / 300 = (1000 + 2400) / 300 = 3400 / 300 = 11.333333
48
49        let result1 = vwap.next((10.0, 100.0, false));
50        approx::assert_relative_eq!(result1, 10.0);
51
52        let result2 = vwap.next((12.0, 200.0, false));
53        approx::assert_relative_eq!(result2, 11.3333333333, epsilon = 1e-6);
54    }
55
56    #[test]
57    fn test_anchored_vwap_reset() {
58        let mut vwap = AnchoredVWAP::new();
59
60        vwap.next((10.0, 100.0, false));
61        vwap.next((12.0, 200.0, false));
62
63        // bar 3: price 15, vol 100, anchor=true -> vwap should reset to 15
64        let result3 = vwap.next((15.0, 100.0, true));
65        approx::assert_relative_eq!(result3, 15.0);
66
67        // bar 4: price 16, vol 100, anchor=false -> vwap (15*100 + 16*100) / 200 = 15.5
68        let result4 = vwap.next((16.0, 100.0, false));
69        approx::assert_relative_eq!(result4, 15.5);
70    }
71}
72
73pub const VWAP_METADATA: IndicatorMetadata = IndicatorMetadata {
74    name: "Anchored VWAP",
75    description: "Volume Weighted Average Price anchored to a specific starting point.",
76    params: &[],
77    formula_source: "https://www.investopedia.com/terms/v/vwap.asp",
78    formula_latex: r#"
79\[
80VWAP = \frac{\sum (Price \times Volume)}{\sum Volume}
81\]
82"#,
83    gold_standard_file: "vwap.json",
84    category: "Classic",
85};