egui_charts/studies/builtin/
majority_rule.rs1use super::over_finite_bars;
2use crate::model::Bar;
3use crate::studies::stepper::{update_window, usable_behind};
4use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
5use egui::Color32;
6
7#[derive(Clone)]
10pub struct MajorityRule {
11 period: usize,
12 values: IndicatorValues,
13 colors: Vec<Color32>,
14 visible: bool,
15}
16
17impl MajorityRule {
18 pub fn new(period: usize) -> Self {
20 Self {
21 period: period.max(1),
22 values: IndicatorValues::new(),
23 colors: vec![Color32::from_rgb(255, 82, 82)],
24 visible: true,
25 }
26 }
27}
28
29impl Default for MajorityRule {
30 fn default() -> Self {
31 Self::new(10)
32 }
33}
34
35impl MajorityRule {
36 fn compute(&self, data: &[Bar]) -> IndicatorValues {
38 let mut values = IndicatorValues::new();
39 if data.is_empty() {
40 return values;
41 }
42
43 values.push(IndicatorValue::None);
45
46 for i in 1..data.len() {
47 if i < self.period {
48 values.push(IndicatorValue::None);
49 continue;
50 }
51
52 let start = i + 1 - self.period;
53 let mut up_count: i32 = 0;
54 let mut down_count: i32 = 0;
55
56 for idx in start..=i {
57 if idx == 0 {
58 continue;
59 }
60 if data[idx].close > data[idx - 1].close {
61 up_count += 1;
62 } else if data[idx].close < data[idx - 1].close {
63 down_count += 1;
64 }
65 }
66
67 let result = if up_count > down_count {
68 1.0
69 } else if down_count > up_count {
70 -1.0
71 } else {
72 0.0
73 };
74
75 values.push(IndicatorValue::Single(result));
76 }
77
78 values
79 }
80}
81
82impl Indicator for MajorityRule {
83 fn source(&self) -> Option<crate::studies::StudySource> {
84 Some(crate::studies::StudySource::Price(
85 crate::model::PriceSource::Close,
86 ))
87 }
88
89 crate::studies::inputs::study_inputs!(self;
90 period: length("Period", 1, usize::MAX)
91 ; {});
92
93 fn name(&self) -> &str {
94 "MajRule"
95 }
96
97 fn desc(&self) -> &str {
98 "Majority Rule - +1 if majority up closes, -1 if majority down, 0 if tie"
99 }
100
101 fn calculate(&mut self, data: &[Bar]) {
102 self.update(data, 0);
103 }
104
105 fn update(&mut self, data: &[Bar], from: usize) {
106 let mut values = std::mem::take(&mut self.values);
107 update_window(
108 &mut values,
109 data,
110 from,
111 usable_behind(self.period, |b| b.close.is_finite()),
112 |data| over_finite_bars(data, |b| b.close.is_finite(), |data| self.compute(data)),
113 );
114 self.values = values;
115 }
116
117 fn is_incremental(&self) -> bool {
118 true
119 }
120
121 fn values(&self) -> &IndicatorValues {
122 &self.values
123 }
124
125 fn colors(&self) -> Vec<Color32> {
126 self.colors.clone()
127 }
128
129 fn set_colors(&mut self, colors: Vec<Color32>) {
130 if !colors.is_empty() {
131 self.colors = colors;
132 }
133 }
134
135 fn is_overlay(&self) -> bool {
136 false
137 }
138
139 fn is_visible(&self) -> bool {
140 self.visible
141 }
142
143 fn set_visible(&mut self, visible: bool) {
144 self.visible = visible;
145 }
146
147 fn clone_box(&self) -> Box<dyn Indicator> {
148 Box::new(self.clone())
149 }
150
151 fn line_names(&self) -> Vec<String> {
152 vec![format!("MajRule({})", self.period)]
153 }
154}