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    #[inline]
45    fn update(&mut self, candle: Candle) -> Option<f64> {
46        self.has_emitted = true;
47        Some(candle.avg_price())
48    }
49
50    fn reset(&mut self) {
51        self.has_emitted = false;
52    }
53
54    #[inline]
55    fn warmup_period(&self) -> usize {
56        1
57    }
58
59    #[inline]
60    fn is_ready(&self) -> bool {
61        self.has_emitted
62    }
63
64    #[inline]
65    fn name(&self) -> &'static str {
66        "AVGPRICE"
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use approx::assert_relative_eq;
74
75    #[test]
76    fn averages_the_four_prices() {
77        // (open + high + low + close) / 4 = (10 + 14 + 6 + 12) / 4 = 10.5.
78        let candle = Candle::new(10.0, 14.0, 6.0, 12.0, 1.0, 0).unwrap();
79        let mut ap = AvgPrice::new();
80        assert!(!ap.is_ready());
81        assert_relative_eq!(ap.update(candle).unwrap(), 10.5, epsilon = 1e-12);
82        assert!(ap.is_ready());
83    }
84
85    #[test]
86    fn accessors_and_reset() {
87        let mut ap = AvgPrice::new();
88        assert_eq!(ap.name(), "AVGPRICE");
89        assert_eq!(ap.warmup_period(), 1);
90        let candle = Candle::new(10.0, 14.0, 6.0, 12.0, 1.0, 0).unwrap();
91        let _ = ap.update(candle);
92        assert!(ap.is_ready());
93        ap.reset();
94        assert!(!ap.is_ready());
95    }
96}