egui_charts/studies/builtin/
balance_of_power.rs1use super::over_finite_bars;
2use crate::model::Bar;
3use crate::studies::palette;
4use crate::studies::stepper::{update_window, usable_behind};
5use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
6use egui::Color32;
7
8#[derive(Clone)]
18pub struct BalanceOfPower {
19 smooth_period: usize,
21 values: IndicatorValues,
22 color: Color32,
23 visible: bool,
24}
25
26impl BalanceOfPower {
27 pub fn new(smooth_period: usize) -> Self {
29 Self {
30 smooth_period: smooth_period.max(1),
31 values: IndicatorValues::new(),
32 color: palette::RED, visible: true,
34 }
35 }
36
37 pub fn with_color(mut self, color: Color32) -> Self {
39 self.color = color;
40 self
41 }
42
43 fn compute(&self, data: &[Bar]) -> IndicatorValues {
45 let raw: Vec<f64> = data
46 .iter()
47 .map(|bar| {
48 let range = bar.high - bar.low;
49 if range == 0.0 {
50 0.0
51 } else {
52 (bar.close - bar.open) / range
53 }
54 })
55 .collect();
56 (0..raw.len())
57 .map(|i| match i + 1 {
58 n if n < self.smooth_period => IndicatorValue::None,
59 n => IndicatorValue::Single(
60 raw[n - self.smooth_period..n].iter().sum::<f64>() / self.smooth_period as f64,
61 ),
62 })
63 .collect()
64 }
65}
66
67impl Default for BalanceOfPower {
69 fn default() -> Self {
70 Self::new(14)
71 }
72}
73
74impl Indicator for BalanceOfPower {
75 crate::studies::inputs::study_inputs!(self;
76 smooth_period: length("Smooth period", 1, usize::MAX)
77 ; {});
78
79 fn name(&self) -> &str {
80 "BoP"
81 }
82
83 fn desc(&self) -> &str {
84 "Balance of Power - Measures buyer/seller strength"
85 }
86
87 fn calculate(&mut self, data: &[Bar]) {
88 self.update(data, 0);
89 }
90
91 fn update(&mut self, data: &[Bar], from: usize) {
92 let mut values = std::mem::take(&mut self.values);
93 update_window(
94 &mut values,
95 data,
96 from,
97 usable_behind(self.smooth_period - 1, |b| {
98 [b.open, b.high, b.low, b.close]
99 .iter()
100 .all(|p| p.is_finite())
101 }),
102 |data| {
103 over_finite_bars(
104 data,
105 |b| {
106 [b.open, b.high, b.low, b.close]
107 .iter()
108 .all(|p| p.is_finite())
109 },
110 |data| self.compute(data),
111 )
112 },
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 vec![self.color]
127 }
128
129 fn set_colors(&mut self, colors: Vec<Color32>) {
130 if !colors.is_empty() {
131 self.color = colors[0];
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 if self.smooth_period > 1 {
153 vec![format!("BoP({})", self.smooth_period)]
154 } else {
155 vec!["BoP".to_string()]
156 }
157 }
158}