Skip to main content

finance_query/backtesting/condition/
composite.rs

1//! Composite conditions for combining multiple conditions.
2//!
3//! This module provides AND, OR, and NOT operations for combining conditions.
4
5use crate::backtesting::strategy::StrategyContext;
6use crate::indicators::Indicator;
7
8use super::Condition;
9
10/// Condition: both conditions must be true (AND logic).
11#[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    /// Create a new And condition.
19    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        // Deduplicate by key
33        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 description(&self) -> String {
39        format!(
40            "({} AND {})",
41            self.left.description(),
42            self.right.description()
43        )
44    }
45}
46
47/// Condition: at least one condition must be true (OR logic).
48#[derive(Clone)]
49pub struct Or<C1: Condition, C2: Condition> {
50    left: C1,
51    right: C2,
52}
53
54impl<C1: Condition, C2: Condition> Or<C1, C2> {
55    /// Create a new Or condition.
56    pub fn new(left: C1, right: C2) -> Self {
57        Self { left, right }
58    }
59}
60
61impl<C1: Condition, C2: Condition> Condition for Or<C1, C2> {
62    fn evaluate(&self, ctx: &StrategyContext) -> bool {
63        self.left.evaluate(ctx) || self.right.evaluate(ctx)
64    }
65
66    fn required_indicators(&self) -> Vec<(String, Indicator)> {
67        let mut indicators = self.left.required_indicators();
68        indicators.extend(self.right.required_indicators());
69        // Deduplicate by key
70        indicators.sort_by(|a, b| a.0.cmp(&b.0));
71        indicators.dedup_by(|a, b| a.0 == b.0);
72        indicators
73    }
74
75    fn description(&self) -> String {
76        format!(
77            "({} OR {})",
78            self.left.description(),
79            self.right.description()
80        )
81    }
82}
83
84/// Condition: negation of a condition (NOT logic).
85#[derive(Clone)]
86pub struct Not<C: Condition> {
87    inner: C,
88}
89
90impl<C: Condition> Not<C> {
91    /// Create a new Not condition.
92    pub fn new(inner: C) -> Self {
93        Self { inner }
94    }
95}
96
97impl<C: Condition> Condition for Not<C> {
98    fn evaluate(&self, ctx: &StrategyContext) -> bool {
99        !self.inner.evaluate(ctx)
100    }
101
102    fn required_indicators(&self) -> Vec<(String, Indicator)> {
103        self.inner.required_indicators()
104    }
105
106    fn description(&self) -> String {
107        format!("NOT ({})", self.inner.description())
108    }
109}
110
111/// Builder for creating complex multi-condition combinations.
112///
113/// # Example
114///
115/// ```ignore
116/// use finance_query::backtesting::condition::*;
117/// use finance_query::backtesting::refs::*;
118///
119/// let conditions = ConditionBuilder::new()
120///     .with_condition(rsi(14).below(30.0))
121///     .with_condition(price().above_ref(sma(200)))
122///     .with_condition(adx(14).above(25.0))
123///     .all();  // All conditions must be true
124///
125/// // Or use any() for OR logic
126/// let exit = ConditionBuilder::new()
127///     .with_condition(rsi(14).above(70.0))
128///     .with_condition(stop_loss(0.05))
129///     .any();  // Any condition can be true
130/// ```
131#[derive(Clone)]
132pub struct ConditionBuilder<C: Condition> {
133    conditions: Vec<C>,
134}
135
136impl<C: Condition> Default for ConditionBuilder<C> {
137    fn default() -> Self {
138        Self::new()
139    }
140}
141
142impl<C: Condition> ConditionBuilder<C> {
143    /// Create a new condition builder.
144    pub fn new() -> Self {
145        Self {
146            conditions: Vec::new(),
147        }
148    }
149
150    /// Add a condition to the builder.
151    pub fn with_condition(mut self, condition: C) -> Self {
152        self.conditions.push(condition);
153        self
154    }
155}
156
157/// A condition that evaluates to true when ALL inner conditions are true.
158#[derive(Clone)]
159pub struct All<C: Condition> {
160    conditions: Vec<C>,
161}
162
163impl<C: Condition> Condition for All<C> {
164    fn evaluate(&self, ctx: &StrategyContext) -> bool {
165        self.conditions.iter().all(|c| c.evaluate(ctx))
166    }
167
168    fn required_indicators(&self) -> Vec<(String, Indicator)> {
169        let mut indicators = Vec::new();
170        for c in &self.conditions {
171            indicators.extend(c.required_indicators());
172        }
173        // Deduplicate by key
174        indicators.sort_by(|a, b| a.0.cmp(&b.0));
175        indicators.dedup_by(|a, b| a.0 == b.0);
176        indicators
177    }
178
179    fn description(&self) -> String {
180        let descs: Vec<_> = self.conditions.iter().map(|c| c.description()).collect();
181        format!("ALL({})", descs.join(" AND "))
182    }
183}
184
185/// A condition that evaluates to true when ANY inner condition is true.
186#[derive(Clone)]
187pub struct Any<C: Condition> {
188    conditions: Vec<C>,
189}
190
191impl<C: Condition> Condition for Any<C> {
192    fn evaluate(&self, ctx: &StrategyContext) -> bool {
193        self.conditions.iter().any(|c| c.evaluate(ctx))
194    }
195
196    fn required_indicators(&self) -> Vec<(String, Indicator)> {
197        let mut indicators = Vec::new();
198        for c in &self.conditions {
199            indicators.extend(c.required_indicators());
200        }
201        // Deduplicate by key
202        indicators.sort_by(|a, b| a.0.cmp(&b.0));
203        indicators.dedup_by(|a, b| a.0 == b.0);
204        indicators
205    }
206
207    fn description(&self) -> String {
208        let descs: Vec<_> = self.conditions.iter().map(|c| c.description()).collect();
209        format!("ANY({})", descs.join(" OR "))
210    }
211}
212
213impl<C: Condition> ConditionBuilder<C> {
214    /// Build a condition that requires ALL conditions to be true.
215    pub fn all(self) -> All<C> {
216        All {
217            conditions: self.conditions,
218        }
219    }
220
221    /// Build a condition that requires ANY condition to be true.
222    pub fn any(self) -> Any<C> {
223        Any {
224            conditions: self.conditions,
225        }
226    }
227}
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232    use crate::backtesting::condition::{always_false, always_true};
233
234    #[test]
235    fn test_and_description() {
236        let cond = And::new(always_true(), always_false());
237        assert_eq!(cond.description(), "(always true AND always false)");
238    }
239
240    #[test]
241    fn test_or_description() {
242        let cond = Or::new(always_true(), always_false());
243        assert_eq!(cond.description(), "(always true OR always false)");
244    }
245
246    #[test]
247    fn test_not_description() {
248        let cond = Not::new(always_true());
249        assert_eq!(cond.description(), "NOT (always true)");
250    }
251
252    #[test]
253    fn test_all_description() {
254        let all = ConditionBuilder::new()
255            .with_condition(always_true())
256            .with_condition(always_false())
257            .all();
258        assert_eq!(all.description(), "ALL(always true AND always false)");
259    }
260
261    #[test]
262    fn test_any_description() {
263        let any = ConditionBuilder::new()
264            .with_condition(always_true())
265            .with_condition(always_false())
266            .any();
267        assert_eq!(any.description(), "ANY(always true OR always false)");
268    }
269}