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 description(&self) -> String {
47 format!(
48 "({} AND {})",
49 self.left.description(),
50 self.right.description()
51 )
52 }
53}
54
55#[derive(Clone)]
57pub struct Or<C1: Condition, C2: Condition> {
58 left: C1,
59 right: C2,
60}
61
62impl<C1: Condition, C2: Condition> Or<C1, C2> {
63 pub fn new(left: C1, right: C2) -> Self {
65 Self { left, right }
66 }
67}
68
69impl<C1: Condition, C2: Condition> Condition for Or<C1, C2> {
70 fn evaluate(&self, ctx: &StrategyContext) -> bool {
71 self.left.evaluate(ctx) || self.right.evaluate(ctx)
72 }
73
74 fn required_indicators(&self) -> Vec<(String, Indicator)> {
75 let mut indicators = self.left.required_indicators();
76 indicators.extend(self.right.required_indicators());
77 indicators.sort_by(|a, b| a.0.cmp(&b.0));
79 indicators.dedup_by(|a, b| a.0 == b.0);
80 indicators
81 }
82
83 fn htf_requirements(&self) -> Vec<HtfIndicatorSpec> {
84 let mut reqs = self.left.htf_requirements();
85 reqs.extend(self.right.htf_requirements());
86 reqs.sort_by(|a, b| a.htf_key.cmp(&b.htf_key));
87 reqs.dedup_by(|a, b| a.htf_key == b.htf_key);
88 reqs
89 }
90
91 fn description(&self) -> String {
92 format!(
93 "({} OR {})",
94 self.left.description(),
95 self.right.description()
96 )
97 }
98}
99
100#[derive(Clone)]
102pub struct Not<C: Condition> {
103 inner: C,
104}
105
106impl<C: Condition> Not<C> {
107 pub fn new(inner: C) -> Self {
109 Self { inner }
110 }
111}
112
113impl<C: Condition> Condition for Not<C> {
114 fn evaluate(&self, ctx: &StrategyContext) -> bool {
115 !self.inner.evaluate(ctx)
116 }
117
118 fn required_indicators(&self) -> Vec<(String, Indicator)> {
119 self.inner.required_indicators()
120 }
121
122 fn htf_requirements(&self) -> Vec<HtfIndicatorSpec> {
123 self.inner.htf_requirements()
124 }
125
126 fn description(&self) -> String {
127 format!("NOT ({})", self.inner.description())
128 }
129}
130
131#[derive(Clone)]
152pub struct ConditionBuilder<C: Condition> {
153 conditions: Vec<C>,
154}
155
156impl<C: Condition> Default for ConditionBuilder<C> {
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162impl<C: Condition> ConditionBuilder<C> {
163 pub fn new() -> Self {
165 Self {
166 conditions: Vec::new(),
167 }
168 }
169
170 pub fn with_condition(mut self, condition: C) -> Self {
172 self.conditions.push(condition);
173 self
174 }
175}
176
177#[derive(Clone)]
179pub struct All<C: Condition> {
180 conditions: Vec<C>,
181}
182
183impl<C: Condition> Condition for All<C> {
184 fn evaluate(&self, ctx: &StrategyContext) -> bool {
185 self.conditions.iter().all(|c| c.evaluate(ctx))
186 }
187
188 fn required_indicators(&self) -> Vec<(String, Indicator)> {
189 let mut indicators = Vec::new();
190 for c in &self.conditions {
191 indicators.extend(c.required_indicators());
192 }
193 indicators.sort_by(|a, b| a.0.cmp(&b.0));
195 indicators.dedup_by(|a, b| a.0 == b.0);
196 indicators
197 }
198
199 fn description(&self) -> String {
200 let descs: Vec<_> = self.conditions.iter().map(|c| c.description()).collect();
201 format!("ALL({})", descs.join(" AND "))
202 }
203}
204
205#[derive(Clone)]
207pub struct Any<C: Condition> {
208 conditions: Vec<C>,
209}
210
211impl<C: Condition> Condition for Any<C> {
212 fn evaluate(&self, ctx: &StrategyContext) -> bool {
213 self.conditions.iter().any(|c| c.evaluate(ctx))
214 }
215
216 fn required_indicators(&self) -> Vec<(String, Indicator)> {
217 let mut indicators = Vec::new();
218 for c in &self.conditions {
219 indicators.extend(c.required_indicators());
220 }
221 indicators.sort_by(|a, b| a.0.cmp(&b.0));
223 indicators.dedup_by(|a, b| a.0 == b.0);
224 indicators
225 }
226
227 fn description(&self) -> String {
228 let descs: Vec<_> = self.conditions.iter().map(|c| c.description()).collect();
229 format!("ANY({})", descs.join(" OR "))
230 }
231}
232
233impl<C: Condition> ConditionBuilder<C> {
234 pub fn all(self) -> All<C> {
236 All {
237 conditions: self.conditions,
238 }
239 }
240
241 pub fn any(self) -> Any<C> {
243 Any {
244 conditions: self.conditions,
245 }
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252 use crate::backtesting::condition::{always_false, always_true};
253
254 #[test]
255 fn test_and_description() {
256 let cond = And::new(always_true(), always_false());
257 assert_eq!(cond.description(), "(always true AND always false)");
258 }
259
260 #[test]
261 fn test_or_description() {
262 let cond = Or::new(always_true(), always_false());
263 assert_eq!(cond.description(), "(always true OR always false)");
264 }
265
266 #[test]
267 fn test_not_description() {
268 let cond = Not::new(always_true());
269 assert_eq!(cond.description(), "NOT (always true)");
270 }
271
272 #[test]
273 fn test_all_description() {
274 let all = ConditionBuilder::new()
275 .with_condition(always_true())
276 .with_condition(always_false())
277 .all();
278 assert_eq!(all.description(), "ALL(always true AND always false)");
279 }
280
281 #[test]
282 fn test_any_description() {
283 let any = ConditionBuilder::new()
284 .with_condition(always_true())
285 .with_condition(always_false())
286 .any();
287 assert_eq!(any.description(), "ANY(always true OR always false)");
288 }
289}