egui_charts/studies/builtin/
williams_alligator.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 WilliamsAlligator {
38 jaw_length: usize,
39 teeth_length: usize,
40 lips_length: usize,
41 jaw_offset: usize,
42 teeth_offset: usize,
43 lips_offset: usize,
44 values: IndicatorValues,
45 stepper: Stepper<[Smoothing; 3]>,
46 colors: [Color32; 3],
47 visible: bool,
48}
49
50impl WilliamsAlligator {
51 pub fn new(jaw_length: usize, teeth_length: usize, lips_length: usize) -> Self {
54 Self {
55 jaw_length: jaw_length.max(1),
56 teeth_length: teeth_length.max(1),
57 lips_length: lips_length.max(1),
58 jaw_offset: 8,
59 teeth_offset: 5,
60 lips_offset: 3,
61 values: IndicatorValues::new(),
62 stepper: Stepper::default(),
63 colors: [
64 palette::ACCENT,
65 palette::PINK,
66 Color32::from_rgb(102, 187, 106),
67 ],
68 visible: true,
69 }
70 }
71
72 pub fn with_offsets(mut self, jaw: usize, teeth: usize, lips: usize) -> Self {
74 (self.jaw_offset, self.teeth_offset, self.lips_offset) = (jaw, teeth, lips);
75 self
76 }
77}
78
79impl Default for WilliamsAlligator {
81 fn default() -> Self {
82 Self::new(13, 8, 5)
83 }
84}
85
86impl Indicator for WilliamsAlligator {
87 fn source(&self) -> Option<crate::studies::StudySource> {
88 Some(crate::studies::StudySource::Price(
89 crate::model::PriceSource::HL2,
90 ))
91 }
92
93 crate::studies::inputs::study_inputs!(self;
94 jaw_length: length("Jaw length", 1, usize::MAX),
95 teeth_length: length("Teeth length", 1, usize::MAX),
96 lips_length: length("Lips length", 1, usize::MAX),
97 jaw_offset: length("Jaw offset", 0, isize::MAX as usize),
98 teeth_offset: length("Teeth offset", 0, isize::MAX as usize),
99 lips_offset: length("Lips offset", 0, isize::MAX as usize)
100 ; {});
101
102 fn name(&self) -> &str {
103 "Williams Alligator"
104 }
105
106 fn desc(&self) -> &str {
107 "Williams Alligator - Displaced smoothed averages of the median price"
108 }
109
110 fn calculate(&mut self, data: &[Bar]) {
111 self.update(data, 0);
112 }
113
114 fn update(&mut self, data: &[Bar], from: usize) {
115 self.stepper
116 .run(data, from, &mut self.values, |state, bar| {
117 if !bar.high.is_finite() || !bar.low.is_finite() {
118 return IndicatorValue::None;
119 }
120 let median = (bar.high + bar.low) / 2.0;
121 let values = state
122 .iter_mut()
123 .zip([self.jaw_length, self.teeth_length, self.lips_length])
124 .map(|(average, n)| average.next(median, n, 1.0 / n as f64))
125 .collect();
126 IndicatorValue::Multiple(values)
127 });
128 }
129
130 fn is_incremental(&self) -> bool {
131 true
132 }
133
134 fn plot_offset(&self, line: usize) -> isize {
135 let offset = [self.jaw_offset, self.teeth_offset, self.lips_offset]
136 .get(line)
137 .copied()
138 .unwrap_or(0);
139 offset.min(isize::MAX as usize) as isize
140 }
141
142 fn values(&self) -> &IndicatorValues {
143 &self.values
144 }
145
146 fn colors(&self) -> Vec<Color32> {
147 self.colors.to_vec()
148 }
149
150 fn set_colors(&mut self, colors: Vec<Color32>) {
151 for (old, new) in self.colors.iter_mut().zip(colors) {
152 *old = new;
153 }
154 }
155
156 fn is_visible(&self) -> bool {
157 self.visible
158 }
159
160 fn set_visible(&mut self, visible: bool) {
161 self.visible = visible;
162 }
163
164 fn clone_box(&self) -> Box<dyn Indicator> {
165 Box::new(self.clone())
166 }
167
168 fn line_names(&self) -> Vec<String> {
169 vec!["Jaw".into(), "Teeth".into(), "Lips".into()]
170 }
171}