egui_charts/strategies/library/
mod.rs1use std::collections::HashMap;
4
5use super::Strategy;
6use crate::studies::{InputKind, InputValue, StudyInput};
7
8mod price_action;
9#[cfg(feature = "studies")]
10mod studies;
11#[cfg(test)]
12mod tests;
13
14type StrategyConstructor = Box<dyn Fn() -> Box<dyn Strategy> + Send + Sync>;
15
16pub struct StrategyFactory {
34 constructors: HashMap<String, StrategyConstructor>,
35}
36
37impl StrategyFactory {
38 pub fn new() -> Self {
40 let mut factory = Self {
41 constructors: HashMap::new(),
42 };
43 price_action::register(&mut factory);
44 #[cfg(feature = "studies")]
45 studies::register(&mut factory);
46 factory
47 }
48
49 pub fn register<F>(&mut self, name: impl Into<String>, constructor: F)
51 where
52 F: Fn() -> Box<dyn Strategy> + Send + Sync + 'static,
53 {
54 self.constructors.insert(name.into(), Box::new(constructor));
55 }
56
57 pub fn create(&self, name: &str) -> Option<Box<dyn Strategy>> {
59 self.constructors.get(name).map(|constructor| constructor())
60 }
61
62 pub fn list(&self) -> Vec<&str> {
64 let mut names: Vec<_> = self.constructors.keys().map(String::as_str).collect();
65 names.sort_unstable();
66 names
67 }
68}
69
70impl Default for StrategyFactory {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76fn length_input(id: &str, label: &str, value: usize) -> StudyInput {
77 StudyInput::new(
78 id,
79 label,
80 InputValue::Length(value),
81 InputKind::Length(1, usize::MAX),
82 )
83}
84
85fn number_input(id: &str, label: &str, value: f64, min: f64, max: f64) -> StudyInput {
86 StudyInput::new(
87 id,
88 label,
89 InputValue::Number(value),
90 InputKind::Number(min, max),
91 )
92}
93
94fn set_input(inputs: &mut [StudyInput], id: &str, value: InputValue) -> Result<(), &'static str> {
95 let input = inputs
96 .iter_mut()
97 .find(|input| input.id == id)
98 .ok_or("unknown strategy input")?;
99 if !input.accepts(&value) {
100 return Err("strategy input is outside its accepted values");
101 }
102 input.value = value;
103 Ok(())
104}
105
106fn length(inputs: &[StudyInput], id: &str) -> usize {
107 match inputs
108 .iter()
109 .find(|input| input.id == id)
110 .map(|input| &input.value)
111 {
112 Some(InputValue::Length(value)) => *value,
113 _ => unreachable!("declared length input"),
114 }
115}
116
117fn number(inputs: &[StudyInput], id: &str) -> f64 {
118 match inputs
119 .iter()
120 .find(|input| input.id == id)
121 .map(|input| &input.value)
122 {
123 Some(InputValue::Number(value)) => *value,
124 _ => unreachable!("declared numeric input"),
125 }
126}