Skip to main content

quantwave_core/indicators/
vwap.rs

1use crate::indicators::metadata::IndicatorMetadata;
2use crate::traits::Next;
3
4#[derive(Debug, Clone, Default)]
5pub struct AnchoredVWAP {
6    cumulative_tp_v: f64,
7    cumulative_v: f64,
8}
9
10impl AnchoredVWAP {
11    pub fn new() -> Self {
12        Self::default()
13    }
14}
15
16impl Next<(f64, f64, bool)> for AnchoredVWAP {
17    type Output = f64;
18
19    fn next(&mut self, (price, volume, anchor): (f64, f64, bool)) -> Self::Output {
20        if anchor {
21            self.cumulative_tp_v = price * volume;
22            self.cumulative_v = volume;
23        } else {
24            self.cumulative_tp_v += price * volume;
25            self.cumulative_v += volume;
26        }
27
28        if self.cumulative_v == 0.0 {
29            0.0
30        } else {
31            self.cumulative_tp_v / self.cumulative_v
32        }
33    }
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn test_basic_vwap_no_reset() {
42        let mut vwap = AnchoredVWAP::new();
43        // bar 1: price 10, vol 100 -> vwap 10
44        // bar 2: price 12, vol 200 -> vwap (10*100 + 12*200) / 300 = (1000 + 2400) / 300 = 3400 / 300 = 11.333333
45
46        let result1 = vwap.next((10.0, 100.0, false));
47        approx::assert_relative_eq!(result1, 10.0);
48
49        let result2 = vwap.next((12.0, 200.0, false));
50        approx::assert_relative_eq!(result2, 11.3333333333, epsilon = 1e-6);
51    }
52
53    #[test]
54    fn test_anchored_vwap_reset() {
55        let mut vwap = AnchoredVWAP::new();
56
57        vwap.next((10.0, 100.0, false));
58        vwap.next((12.0, 200.0, false));
59
60        // bar 3: price 15, vol 100, anchor=true -> vwap should reset to 15
61        let result3 = vwap.next((15.0, 100.0, true));
62        approx::assert_relative_eq!(result3, 15.0);
63
64        // bar 4: price 16, vol 100, anchor=false -> vwap (15*100 + 16*100) / 200 = 15.5
65        let result4 = vwap.next((16.0, 100.0, false));
66        approx::assert_relative_eq!(result4, 15.5);
67    }
68}
69
70pub const VWAP_METADATA: IndicatorMetadata = IndicatorMetadata {
71    name: "Anchored VWAP",
72    description: "Volume Weighted Average Price anchored to a specific starting point.",
73    usage: "Use as an intraday fair value benchmark. Institutional traders buy below VWAP and sell above it; breakouts above VWAP on heavy volume signal bullish institutional interest.",
74    keywords: &["trend", "volume", "classic", "support-resistance"],
75    ehlers_summary: "Volume Weighted Average Price calculates the average price weighted by volume transacted at each level throughout the trading session. It serves as the primary execution benchmark for institutional orders — TWAP and VWAP algorithms are the two most common order execution strategies in equity markets. — Investopedia",
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};