egui_charts/studies/builtin/
bull_bear_power.rs1use super::smoothing::Smoothing;
29use crate::model::Bar;
30use crate::studies::palette;
31use crate::studies::stepper::Stepper;
32use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
33use egui::Color32;
34
35#[derive(Clone)]
37pub struct BullBearPower {
38 period: usize,
39 values: IndicatorValues,
40 stepper: Stepper<Smoothing>,
41 positive_color: Color32,
42 negative_color: Color32,
43 visible: bool,
44}
45
46impl BullBearPower {
47 pub fn new(period: usize) -> Self {
49 Self {
50 period: period.max(1),
51 values: IndicatorValues::new(),
52 stepper: Stepper::default(),
53 positive_color: palette::TEAL,
54 negative_color: palette::RED,
55 visible: true,
56 }
57 }
58}
59
60impl Default for BullBearPower {
61 fn default() -> Self {
62 Self::new(13)
63 }
64}
65
66impl Indicator for BullBearPower {
67 crate::studies::inputs::study_inputs!(self;
68 period: length("Length", 1, usize::MAX)
69 ; {});
70
71 fn plot_style(&self, _line: usize) -> crate::studies::PlotStyle {
72 crate::studies::PlotStyle {
73 color: self.positive_color,
74 width: 1.0,
75 pattern: crate::model::LineStyle::Solid,
76 plot_type: crate::studies::PlotType::Columns,
77 visible: true,
78 }
79 }
80
81 fn plot_color(&self, _line: usize, bar: usize) -> Option<Color32> {
82 let value = self.values.value(0, bar)?;
83 Some(if value < 0.0 {
84 self.negative_color
85 } else {
86 self.positive_color
87 })
88 }
89
90 fn value_format(&self) -> crate::scales::StudyFormat {
91 crate::scales::StudyFormat::Price
92 }
93
94 fn name(&self) -> &str {
95 "Bull Bear Power"
96 }
97
98 fn desc(&self) -> &str {
99 "Bull Bear Power - High and low against the EMA of the close"
100 }
101
102 fn calculate(&mut self, data: &[Bar]) {
103 self.update(data, 0);
104 }
105
106 fn update(&mut self, data: &[Bar], from: usize) {
107 self.stepper
108 .run(data, from, &mut self.values, |state, bar| {
109 if ![bar.high, bar.low, bar.close].iter().all(|x| x.is_finite()) {
110 return IndicatorValue::None;
111 }
112 let ema = state.next(bar.close, self.period, 2.0 / (self.period as f64 + 1.0));
113 let (bull, bear) = (bar.high - ema, bar.low - ema);
114 if bull.is_finite() && bear.is_finite() {
115 IndicatorValue::Single(bull + bear)
116 } else {
117 IndicatorValue::None
118 }
119 });
120 }
121
122 fn is_incremental(&self) -> bool {
123 true
124 }
125
126 fn values(&self) -> &IndicatorValues {
127 &self.values
128 }
129
130 fn colors(&self) -> Vec<Color32> {
131 vec![self.positive_color, self.negative_color]
132 }
133
134 fn set_colors(&mut self, colors: Vec<Color32>) {
135 if let Some(&color) = colors.first() {
136 self.positive_color = color;
137 }
138 if let Some(&color) = colors.get(1) {
139 self.negative_color = color;
140 }
141 }
142
143 fn is_overlay(&self) -> bool {
144 false
145 }
146
147 fn line_count(&self) -> usize {
148 1
149 }
150
151 fn is_visible(&self) -> bool {
152 self.visible
153 }
154
155 fn set_visible(&mut self, visible: bool) {
156 self.visible = visible;
157 }
158
159 fn clone_box(&self) -> Box<dyn Indicator> {
160 Box::new(self.clone())
161 }
162
163 fn line_names(&self) -> Vec<String> {
164 vec![format!("BBP({})", self.period)]
165 }
166}