Skip to main content

egui_charts/studies/builtin/
advance_decline_ratio_bars.rs

1//! Advance/Decline Ratio (Bars)
2//!
3//! The breadth ratio of one instrument's own bars: how many of the last `length` bars closed
4//! up against how many closed down.
5//!
6//! # Formula
7//! ```text
8//! up bar:   close > open
9//! down bar: close < open   (a bar with close == open counts as neither)
10//! ratio = count(up bars, length) / count(down bars, length)
11//! ```
12//! with conventional length 9. A window without a down bar has no value.
13//!
14//! # Example
15//! ```
16//! # let bars: Vec<egui_charts::model::Bar> = (0..60).map(|i| { let p = 100.0 + (i as f64 * 0.3).sin() * 5.0; egui_charts::model::Bar::new(chrono::Utc::now(), p, p + 1.0, p - 1.0, p + if i % 3 == 0 { -0.5 } else { 0.5 }, 1_000.0) }).collect();
17//! use egui_charts::studies::{AdvanceDeclineRatioBars, Indicator};
18//!
19//! let mut ratio = AdvanceDeclineRatioBars::new(9);
20//! ratio.calculate(&bars);
21//! // Six up bars for every three down bars.
22//! assert_eq!(ratio.values().value(0, 8), Some(2.0));
23//! ```
24
25use super::over_finite_bars;
26use crate::model::Bar;
27use crate::studies::palette;
28use crate::studies::stepper::{update_window, usable_behind};
29use crate::studies::{Indicator, IndicatorValues};
30use egui::Color32;
31
32/// Up bars over down bars within a window.
33#[derive(Clone)]
34pub struct AdvanceDeclineRatioBars {
35    period: usize,
36    values: IndicatorValues,
37    color: Color32,
38    visible: bool,
39}
40
41impl AdvanceDeclineRatioBars {
42    /// Create the ratio over the last `period` bars (conventionally 9).
43    pub fn new(period: usize) -> Self {
44        Self {
45            period: period.max(1),
46            values: IndicatorValues::new(),
47            color: palette::BLUE,
48            visible: true,
49        }
50    }
51
52    /// The values over `data`, every bar of which has a finite open and close.
53    fn compute(&self, data: &[Bar]) -> IndicatorValues {
54        let direction = |b: &Bar| (usize::from(b.close > b.open), usize::from(b.close < b.open));
55        let (mut up, mut down) = (0, 0);
56        let mut values = IndicatorValues::new();
57        for (i, bar) in data.iter().enumerate() {
58            let (u, d) = direction(bar);
59            (up, down) = (up + u, down + d);
60            if let Some(gone) = i.checked_sub(self.period) {
61                let (u, d) = direction(&data[gone]);
62                (up, down) = (up - u, down - d);
63            }
64            let full = i + 1 >= self.period;
65            values.push_lines(&[if full && down > 0 {
66                up as f64 / down as f64
67            } else {
68                f64::NAN
69            }]);
70        }
71        values
72    }
73}
74
75impl Default for AdvanceDeclineRatioBars {
76    fn default() -> Self {
77        Self::new(9)
78    }
79}
80
81impl Indicator for AdvanceDeclineRatioBars {
82    crate::studies::inputs::study_inputs!(self;
83        period: length("Length", 1, usize::MAX)
84    ; {});
85
86    fn name(&self) -> &str {
87        "Advance/Decline Ratio (Bars)"
88    }
89
90    fn desc(&self) -> &str {
91        "Advance/Decline Ratio (Bars) - Up bars over down bars in a window"
92    }
93
94    fn calculate(&mut self, data: &[Bar]) {
95        self.update(data, 0);
96    }
97
98    fn update(&mut self, data: &[Bar], from: usize) {
99        let mut values = std::mem::take(&mut self.values);
100        update_window(
101            &mut values,
102            data,
103            from,
104            usable_behind(self.period - 1, |b| {
105                b.open.is_finite() && b.close.is_finite()
106            }),
107            |data| {
108                over_finite_bars(
109                    data,
110                    |b| b.open.is_finite() && b.close.is_finite(),
111                    |data| self.compute(data),
112                )
113            },
114        );
115        self.values = values;
116    }
117
118    fn is_incremental(&self) -> bool {
119        true
120    }
121
122    fn values(&self) -> &IndicatorValues {
123        &self.values
124    }
125
126    fn colors(&self) -> Vec<Color32> {
127        vec![self.color]
128    }
129
130    fn set_colors(&mut self, colors: Vec<Color32>) {
131        if let Some(&color) = colors.first() {
132            self.color = color;
133        }
134    }
135
136    fn is_overlay(&self) -> bool {
137        false
138    }
139
140    fn is_visible(&self) -> bool {
141        self.visible
142    }
143
144    fn set_visible(&mut self, visible: bool) {
145        self.visible = visible;
146    }
147
148    fn clone_box(&self) -> Box<dyn Indicator> {
149        Box::new(self.clone())
150    }
151
152    fn line_names(&self) -> Vec<String> {
153        vec![format!("A/D Ratio({})", self.period)]
154    }
155}