egui_charts/studies/builtin/
twap.rs1use super::anchor_period::{AnchorPeriod, ExchangeDays};
22use crate::model::{Bar, Instrument, PriceSource};
23use crate::studies::stepper::Stepper;
24use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
25use egui::Color32;
26
27#[derive(Clone)]
34pub struct TimeWeightedAvgPrice {
35 anchor: AnchorPeriod,
36 source: PriceSource,
37 days: ExchangeDays,
38 values: IndicatorValues,
39 stepper: Stepper<State>,
40 intraday: bool,
41 colors: Vec<Color32>,
42 visible: bool,
43}
44
45#[derive(Clone, Default)]
46struct State {
47 previous_date: Option<chrono::NaiveDate>,
48 intraday: bool,
49 period: Option<(i32, u32)>,
50 sum: f64,
51 count: f64,
52}
53
54impl TimeWeightedAvgPrice {
55 pub fn new() -> Self {
57 Self {
58 anchor: AnchorPeriod::Session,
59 source: PriceSource::OHLC4,
60 days: ExchangeDays::default(),
61 values: IndicatorValues::new(),
62 stepper: Stepper::default(),
63 intraday: false,
64 colors: vec![Color32::from_rgb(221, 122, 40)],
65 visible: true,
66 }
67 }
68
69 pub fn with_anchor(mut self, anchor: AnchorPeriod) -> Self {
71 self.anchor = anchor;
72 self
73 }
74
75 pub fn with_source(mut self, source: PriceSource) -> Self {
77 self.source = source;
78 self
79 }
80}
81
82impl Default for TimeWeightedAvgPrice {
83 fn default() -> Self {
84 Self::new()
85 }
86}
87
88impl Indicator for TimeWeightedAvgPrice {
89 fn source(&self) -> Option<crate::studies::StudySource> {
90 Some(crate::studies::StudySource::Price(self.source))
91 }
92
93 crate::studies::inputs::study_inputs!(self;
94 anchor: choice("Anchor Period", [AnchorPeriod::Session, AnchorPeriod::Week, AnchorPeriod::Month, AnchorPeriod::Quarter, AnchorPeriod::Year, AnchorPeriod::Decade, AnchorPeriod::Century]),
95 source: choice("Source", PriceSource::all())
96 ; {});
97
98 fn name(&self) -> &str {
99 "Time Weighted Average Price"
100 }
101
102 fn desc(&self) -> &str {
103 "Time Weighted Average Price - Mean price since the start of each anchor period"
104 }
105
106 fn calculate(&mut self, data: &[Bar]) {
107 self.update(data, 0);
108 }
109
110 fn update(&mut self, data: &[Bar], mut from: usize) {
111 loop {
112 self.stepper
113 .run(data, from, &mut self.values, |state, bar| {
114 let price = self.source.compute(bar.open, bar.high, bar.low, bar.close);
115 if !price.is_finite() {
116 return IndicatorValue::None;
117 }
118 let date = bar.time.date_naive();
119 state.intraday |= state.previous_date == Some(date);
120 state.previous_date = Some(date);
121 let key = Some(self.anchor.key(self.days.day(bar, !self.intraday)));
122 if key != state.period {
123 (state.period, state.sum, state.count) = (key, 0.0, 0.0);
124 }
125 state.sum += price;
126 state.count += 1.0;
127 IndicatorValue::Single(state.sum / state.count)
128 });
129 if self.intraday == self.stepper.state().intraday {
130 break;
131 }
132 self.intraday = self.stepper.state().intraday;
134 from = 0;
135 }
136 }
137
138 fn is_incremental(&self) -> bool {
139 true
140 }
141
142 fn set_instrument(&mut self, instrument: Option<&Instrument>) -> bool {
143 self.days.set(instrument)
144 }
145
146 fn values(&self) -> &IndicatorValues {
147 &self.values
148 }
149
150 fn colors(&self) -> Vec<Color32> {
151 self.colors.clone()
152 }
153
154 fn set_colors(&mut self, colors: Vec<Color32>) {
155 if let Some(color) = colors.first() {
156 self.colors = vec![*color];
157 }
158 }
159
160 fn is_overlay(&self) -> bool {
161 true
162 }
163
164 fn is_visible(&self) -> bool {
165 self.visible
166 }
167
168 fn set_visible(&mut self, visible: bool) {
169 self.visible = visible;
170 }
171
172 fn clone_box(&self) -> Box<dyn Indicator> {
173 Box::new(self.clone())
174 }
175
176 fn line_names(&self) -> Vec<String> {
177 vec!["TWAP".to_string()]
178 }
179}