Skip to main content

egui_charts/studies/builtin/
anchored_vwap_study.rs

1use crate::model::Bar;
2use crate::studies::stepper::Stepper;
3use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
4use egui::Color32;
5
6/// Anchored VWAP - VWAP calculated from a specific anchor bar index forward.
7/// typical_price = (high + low + close) / 3
8/// VWAP = cumsum(volume * typical_price) / cumsum(volume) from anchor_bar onward.
9#[derive(Clone)]
10pub struct AnchoredVWAP {
11    anchor_bar: usize,
12    values: IndicatorValues,
13    stepper: Stepper<(usize, f64, f64)>,
14    colors: Vec<Color32>,
15    visible: bool,
16}
17
18impl AnchoredVWAP {
19    /// Create with a zero-based anchor bar index for volume-weighted averaging.
20    pub fn new(anchor_bar: usize) -> Self {
21        Self {
22            anchor_bar,
23            values: IndicatorValues::new(),
24            stepper: Stepper::default(),
25            colors: vec![Color32::from_rgb(30, 136, 229)],
26            visible: true,
27        }
28    }
29}
30
31impl Default for AnchoredVWAP {
32    fn default() -> Self {
33        Self::new(0)
34    }
35}
36
37impl Indicator for AnchoredVWAP {
38    fn source(&self) -> Option<crate::studies::StudySource> {
39        Some(crate::studies::StudySource::Price(
40            crate::model::PriceSource::HLC3,
41        ))
42    }
43
44    crate::studies::inputs::study_inputs!(self;
45        anchor_bar: length("Anchor bar", 0, usize::MAX)
46    ; {});
47
48    fn name(&self) -> &str {
49        "AVWAP"
50    }
51
52    fn desc(&self) -> &str {
53        "Anchored VWAP - Volume weighted average price from anchor bar"
54    }
55
56    fn calculate(&mut self, data: &[Bar]) {
57        self.update(data, 0);
58    }
59
60    fn update(&mut self, data: &[Bar], from: usize) {
61        self.stepper
62            .run(data, from, &mut self.values, |state, bar| {
63                let index = state.0;
64                state.0 += 1;
65                if index < self.anchor_bar
66                    || ![bar.high, bar.low, bar.close, bar.volume]
67                        .iter()
68                        .all(|x| x.is_finite())
69                {
70                    return IndicatorValue::None;
71                }
72                let price = bar.typical_price();
73                state.1 += price * bar.volume;
74                state.2 += bar.volume;
75                IndicatorValue::Single(if state.2 == 0.0 {
76                    price
77                } else {
78                    state.1 / state.2
79                })
80            });
81    }
82
83    fn is_incremental(&self) -> bool {
84        true
85    }
86
87    fn values(&self) -> &IndicatorValues {
88        &self.values
89    }
90
91    fn colors(&self) -> Vec<Color32> {
92        self.colors.clone()
93    }
94
95    fn set_colors(&mut self, colors: Vec<Color32>) {
96        if !colors.is_empty() {
97            self.colors = colors;
98        }
99    }
100
101    fn is_overlay(&self) -> bool {
102        true
103    }
104
105    fn is_visible(&self) -> bool {
106        self.visible
107    }
108
109    fn set_visible(&mut self, visible: bool) {
110        self.visible = visible;
111    }
112
113    fn clone_box(&self) -> Box<dyn Indicator> {
114        Box::new(self.clone())
115    }
116
117    fn line_names(&self) -> Vec<String> {
118        vec![format!("AVWAP({})", self.anchor_bar)]
119    }
120}