egui_charts/studies/builtin/
advance_decline_ratio_bars.rs1use 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#[derive(Clone)]
34pub struct AdvanceDeclineRatioBars {
35 period: usize,
36 values: IndicatorValues,
37 color: Color32,
38 visible: bool,
39}
40
41impl AdvanceDeclineRatioBars {
42 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 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}