finance_query/backtesting/condition/
composite.rs1use crate::backtesting::strategy::StrategyContext;
6use crate::indicators::Indicator;
7
8use super::{Condition, HtfIndicatorSpec};
9
10#[derive(Clone)]
12pub struct And<C1: Condition, C2: Condition> {
13 left: C1,
14 right: C2,
15}
16
17impl<C1: Condition, C2: Condition> And<C1, C2> {
18 pub fn new(left: C1, right: C2) -> Self {
20 Self { left, right }
21 }
22}
23
24impl<C1: Condition, C2: Condition> Condition for And<C1, C2> {
25 fn evaluate(&self, ctx: &StrategyContext) -> bool {
26 self.left.evaluate(ctx) && self.right.evaluate(ctx)
27 }
28
29 fn required_indicators(&self) -> Vec<(String, Indicator)> {
30 let mut indicators = self.left.required_indicators();
31 indicators.extend(self.right.required_indicators());
32 indicators.sort_by(|a, b| a.0.cmp(&b.0));
34 indicators.dedup_by(|a, b| a.0 == b.0);
35 indicators
36 }
37
38 fn htf_requirements(&self) -> Vec<HtfIndicatorSpec> {
39 let mut reqs = self.left.htf_requirements();
40 reqs.extend(self.right.htf_requirements());
41 reqs.sort_by(|a, b| a.htf_key.cmp(&b.htf_key));
42 reqs.dedup_by(|a, b| a.htf_key == b.htf_key);
43 reqs
44 }
45
46 fn tracks_position_extremes(&self) -> bool {
47 self.left.tracks_position_extremes() || self.right.tracks_position_extremes()
48 }
49
50 fn description(&self) -> String {
51 format!(
52 "({} AND {})",
53 self.left.description(),
54 self.right.description()
55 )
56 }
57}
58
59#[derive(Clone)]
61pub struct Or<C1: Condition, C2: Condition> {
62 left: C1,
63 right: C2,
64}
65
66impl<C1: Condition, C2: Condition> Or<C1, C2> {
67 pub fn new(left: C1, right: C2) -> Self {
69 Self { left, right }
70 }
71}
72
73impl<C1: Condition, C2: Condition> Condition for Or<C1, C2> {
74 fn evaluate(&self, ctx: &StrategyContext) -> bool {
75 self.left.evaluate(ctx) || self.right.evaluate(ctx)
76 }
77
78 fn required_indicators(&self) -> Vec<(String, Indicator)> {
79 let mut indicators = self.left.required_indicators();
80 indicators.extend(self.right.required_indicators());
81 indicators.sort_by(|a, b| a.0.cmp(&b.0));
83 indicators.dedup_by(|a, b| a.0 == b.0);
84 indicators
85 }
86
87 fn htf_requirements(&self) -> Vec<HtfIndicatorSpec> {
88 let mut reqs = self.left.htf_requirements();
89 reqs.extend(self.right.htf_requirements());
90 reqs.sort_by(|a, b| a.htf_key.cmp(&b.htf_key));
91 reqs.dedup_by(|a, b| a.htf_key == b.htf_key);
92 reqs
93 }
94
95 fn tracks_position_extremes(&self) -> bool {
96 self.left.tracks_position_extremes() || self.right.tracks_position_extremes()
97 }
98
99 fn description(&self) -> String {
100 format!(
101 "({} OR {})",
102 self.left.description(),
103 self.right.description()
104 )
105 }
106}
107
108#[derive(Clone)]
110pub struct Not<C: Condition> {
111 inner: C,
112}
113
114impl<C: Condition> Not<C> {
115 pub fn new(inner: C) -> Self {
117 Self { inner }
118 }
119}
120
121impl<C: Condition> Condition for Not<C> {
122 fn evaluate(&self, ctx: &StrategyContext) -> bool {
123 !self.inner.evaluate(ctx)
124 }
125
126 fn required_indicators(&self) -> Vec<(String, Indicator)> {
127 self.inner.required_indicators()
128 }
129
130 fn htf_requirements(&self) -> Vec<HtfIndicatorSpec> {
131 self.inner.htf_requirements()
132 }
133
134 fn tracks_position_extremes(&self) -> bool {
135 self.inner.tracks_position_extremes()
136 }
137
138 fn description(&self) -> String {
139 format!("NOT ({})", self.inner.description())
140 }
141}
142
143#[derive(Clone)]
164pub struct ConditionBuilder<C: Condition> {
165 conditions: Vec<C>,
166}
167
168impl<C: Condition> Default for ConditionBuilder<C> {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174impl<C: Condition> ConditionBuilder<C> {
175 pub fn new() -> Self {
177 Self {
178 conditions: Vec::new(),
179 }
180 }
181
182 pub fn with_condition(mut self, condition: C) -> Self {
184 self.conditions.push(condition);
185 self
186 }
187}
188
189#[derive(Clone)]
191pub struct All<C: Condition> {
192 conditions: Vec<C>,
193}
194
195impl<C: Condition> Condition for All<C> {
196 fn evaluate(&self, ctx: &StrategyContext) -> bool {
197 self.conditions.iter().all(|c| c.evaluate(ctx))
198 }
199
200 fn required_indicators(&self) -> Vec<(String, Indicator)> {
201 let mut indicators = Vec::new();
202 for c in &self.conditions {
203 indicators.extend(c.required_indicators());
204 }
205 indicators.sort_by(|a, b| a.0.cmp(&b.0));
207 indicators.dedup_by(|a, b| a.0 == b.0);
208 indicators
209 }
210
211 fn description(&self) -> String {
212 let descs: Vec<_> = self.conditions.iter().map(|c| c.description()).collect();
213 format!("ALL({})", descs.join(" AND "))
214 }
215}
216
217#[derive(Clone)]
219pub struct Any<C: Condition> {
220 conditions: Vec<C>,
221}
222
223impl<C: Condition> Condition for Any<C> {
224 fn evaluate(&self, ctx: &StrategyContext) -> bool {
225 self.conditions.iter().any(|c| c.evaluate(ctx))
226 }
227
228 fn required_indicators(&self) -> Vec<(String, Indicator)> {
229 let mut indicators = Vec::new();
230 for c in &self.conditions {
231 indicators.extend(c.required_indicators());
232 }
233 indicators.sort_by(|a, b| a.0.cmp(&b.0));
235 indicators.dedup_by(|a, b| a.0 == b.0);
236 indicators
237 }
238
239 fn description(&self) -> String {
240 let descs: Vec<_> = self.conditions.iter().map(|c| c.description()).collect();
241 format!("ANY({})", descs.join(" OR "))
242 }
243}
244
245impl<C: Condition> ConditionBuilder<C> {
246 pub fn all(self) -> All<C> {
248 All {
249 conditions: self.conditions,
250 }
251 }
252
253 pub fn any(self) -> Any<C> {
255 Any {
256 conditions: self.conditions,
257 }
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264 use crate::backtesting::condition::{always_false, always_true};
265
266 #[test]
267 fn test_and_description() {
268 let cond = And::new(always_true(), always_false());
269 assert_eq!(cond.description(), "(always true AND always false)");
270 }
271
272 #[test]
273 fn test_or_description() {
274 let cond = Or::new(always_true(), always_false());
275 assert_eq!(cond.description(), "(always true OR always false)");
276 }
277
278 #[test]
279 fn test_not_description() {
280 let cond = Not::new(always_true());
281 assert_eq!(cond.description(), "NOT (always true)");
282 }
283
284 #[test]
285 fn test_all_description() {
286 let all = ConditionBuilder::new()
287 .with_condition(always_true())
288 .with_condition(always_false())
289 .all();
290 assert_eq!(all.description(), "ALL(always true AND always false)");
291 }
292
293 #[test]
294 fn test_any_description() {
295 let any = ConditionBuilder::new()
296 .with_condition(always_true())
297 .with_condition(always_false())
298 .any();
299 assert_eq!(any.description(), "ANY(always true OR always false)");
300 }
301}