Skip to main content

wickra_core/indicators/
avg_price.rs

1//! Average Price (AVGPRICE).
2
3use crate::ohlcv::Candle;
4use crate::traits::Indicator;
5
6/// Average Price (`AVGPRICE`) — the bar's `(open + high + low + close) / 4`.
7///
8/// A per-bar price aggregate that, unlike [`TypicalPrice`](crate::TypicalPrice)
9/// and [`WeightedClose`](crate::WeightedClose), folds in the open as well as the
10/// high, low and close. As a stateless transform it emits a value from the very
11/// first candle.
12///
13/// # Example
14///
15/// ```
16/// use wickra_core::{Candle, Indicator, AvgPrice};
17///
18/// let mut indicator = AvgPrice::new();
19/// let mut last = None;
20/// for i in 0..80 {
21///     let base = 100.0 + f64::from(i);
22///     let candle =
23///         Candle::new(base, base + 2.0, base - 2.0, base + 1.0, 10.0, i64::from(i)).unwrap();
24///     last = indicator.update(candle);
25/// }
26/// assert!(last.is_some());
27/// ```
28#[derive(Debug, Clone, Default)]
29pub struct AvgPrice {
30    has_emitted: bool,
31}
32
33impl AvgPrice {
34    /// Construct a new Average Price transform.
35    pub const fn new() -> Self {
36        Self { has_emitted: false }
37    }
38}
39
40impl Indicator for AvgPrice {
41    type Input = Candle;
42    type Output = f64;
43
44    fn update(&mut self, candle: Candle) -> Option<f64> {
45        self.has_emitted = true;
46        Some(candle.avg_price())
47    }
48
49    fn reset(&mut self) {
50        self.has_emitted = false;
51    }
52
53    fn warmup_period(&self) -> usize {
54        1
55    }
56
57    fn is_ready(&self) -> bool {
58        self.has_emitted
59    }
60
61    fn name(&self) -> &'static str {
62        "AVGPRICE"
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use approx::assert_relative_eq;
70
71    #[test]
72    fn averages_the_four_prices() {
73        // (open + high + low + close) / 4 = (10 + 14 + 6 + 12) / 4 = 10.5.
74        let candle = Candle::new(10.0, 14.0, 6.0, 12.0, 1.0, 0).unwrap();
75        let mut ap = AvgPrice::new();
76        assert!(!ap.is_ready());
77        assert_relative_eq!(ap.update(candle).unwrap(), 10.5, epsilon = 1e-12);
78        assert!(ap.is_ready());
79    }
80
81    #[test]
82    fn accessors_and_reset() {
83        let mut ap = AvgPrice::new();
84        assert_eq!(ap.name(), "AVGPRICE");
85        assert_eq!(ap.warmup_period(), 1);
86        let candle = Candle::new(10.0, 14.0, 6.0, 12.0, 1.0, 0).unwrap();
87        let _ = ap.update(candle);
88        assert!(ap.is_ready());
89        ap.reset();
90        assert!(!ap.is_ready());
91    }
92}