egui_charts/studies/builtin/
force_index.rs1use super::smoothing::Smoothing;
2use crate::model::Bar;
3use crate::studies::stepper::Stepper;
4use crate::studies::{Indicator, IndicatorValue, IndicatorValues};
5use egui::Color32;
6
7#[derive(Clone)]
11pub struct ForceIndex {
12 period: usize,
14 values: IndicatorValues,
15 stepper: Stepper<(Option<f64>, Smoothing)>,
16 color: Color32,
17 visible: bool,
18}
19
20impl ForceIndex {
21 pub fn new(period: usize) -> Self {
23 Self {
24 period: period.max(1),
25 values: IndicatorValues::new(),
26 stepper: Stepper::default(),
27 color: crate::studies::palette::BRIGHT_RED,
28 visible: true,
29 }
30 }
31
32 pub fn with_color(mut self, color: Color32) -> Self {
34 self.color = color;
35 self
36 }
37}
38
39impl Default for ForceIndex {
41 fn default() -> Self {
42 Self::new(13)
43 }
44}
45
46impl Indicator for ForceIndex {
47 fn source(&self) -> Option<crate::studies::StudySource> {
48 Some(crate::studies::StudySource::Price(
49 crate::model::PriceSource::Close,
50 ))
51 }
52
53 crate::studies::inputs::study_inputs!(self;
54 period: length("Period", 1, usize::MAX)
55 ; {});
56
57 fn name(&self) -> &str {
58 "Force Index"
59 }
60
61 fn desc(&self) -> &str {
62 "Force Index - Measures force behind price movements"
63 }
64
65 fn calculate(&mut self, data: &[Bar]) {
66 self.update(data, 0);
67 }
68
69 fn update(&mut self, data: &[Bar], from: usize) {
70 self.stepper
71 .run(data, from, &mut self.values, |state, bar| {
72 if !bar.close.is_finite() || !bar.volume.is_finite() {
73 return IndicatorValue::None;
74 }
75 let raw = state
76 .0
77 .map_or(f64::NAN, |previous| (bar.close - previous) * bar.volume);
78 state.0 = Some(bar.close);
79 IndicatorValue::Single(state.1.next(
80 raw,
81 self.period,
82 2.0 / (self.period as f64 + 1.0),
83 ))
84 });
85 }
86
87 fn is_incremental(&self) -> bool {
88 true
89 }
90
91 fn values(&self) -> &IndicatorValues {
92 &self.values
93 }
94
95 fn colors(&self) -> Vec<Color32> {
96 vec![self.color]
97 }
98
99 fn set_colors(&mut self, colors: Vec<Color32>) {
100 if !colors.is_empty() {
101 self.color = colors[0];
102 }
103 }
104
105 fn is_overlay(&self) -> bool {
106 false }
108
109 fn is_visible(&self) -> bool {
110 self.visible
111 }
112
113 fn set_visible(&mut self, visible: bool) {
114 self.visible = visible;
115 }
116
117 fn clone_box(&self) -> Box<dyn Indicator> {
118 Box::new(self.clone())
119 }
120
121 fn line_names(&self) -> Vec<String> {
122 vec![format!("Force({})", self.period)]
123 }
124}