finance_query/backtesting/refs/mod.rs
1//! Indicator reference system for building strategy conditions.
2//!
3//! This module provides a type-safe way to reference indicator values
4//! that can be used to build trading conditions.
5//!
6//! # Example
7//!
8//! ```ignore
9//! use finance_query::backtesting::refs::*;
10//!
11//! // Reference RSI indicator
12//! let rsi_ref = rsi(14);
13//!
14//! // Build conditions
15//! let oversold = rsi_ref.below(30.0);
16//! let overbought = rsi_ref.above(70.0);
17//!
18//! // Compose conditions
19//! let entry = rsi(14).crosses_below(30.0).and(price().above_ref(sma(200)));
20//! ```
21
22mod computed;
23mod price;
24
25pub use computed::*;
26pub use price::*;
27
28use crate::indicators::Indicator;
29
30use super::strategy::StrategyContext;
31
32/// A reference to a value that can be compared in conditions.
33///
34/// This is the building block for creating conditions. Each indicator
35/// reference knows:
36/// - Its unique key for storing computed values
37/// - What indicators it requires
38/// - How to retrieve its value from the strategy context
39///
40/// # Implementing Custom References
41///
42/// ```ignore
43/// use finance_query::backtesting::refs::IndicatorRef;
44///
45/// #[derive(Clone)]
46/// struct MyCustomRef {
47/// period: usize,
48/// }
49///
50/// impl IndicatorRef for MyCustomRef {
51/// fn key(&self) -> String {
52/// format!("my_custom_{}", self.period)
53/// }
54///
55/// fn required_indicators(&self) -> Vec<(String, Indicator)> {
56/// vec![(self.key(), Indicator::Sma(self.period))]
57/// }
58///
59/// fn value(&self, ctx: &StrategyContext) -> Option<f64> {
60/// ctx.indicator(&self.key())
61/// }
62///
63/// fn prev_value(&self, ctx: &StrategyContext) -> Option<f64> {
64/// ctx.indicator_prev(&self.key())
65/// }
66/// }
67/// ```
68pub trait IndicatorRef: Clone + Send + Sync + 'static {
69 /// Unique key for storing computed values in the context.
70 ///
71 /// This key is used to look up pre-computed indicator values
72 /// in the `StrategyContext::indicators` map.
73 fn key(&self) -> String;
74
75 /// Required indicators to compute this reference.
76 ///
77 /// Returns a list of (key, Indicator) pairs that must be
78 /// pre-computed by the backtest engine before the strategy runs.
79 fn required_indicators(&self) -> Vec<(String, Indicator)>;
80
81 /// Get the value at current candle index from context.
82 fn value(&self, ctx: &StrategyContext) -> Option<f64>;
83
84 /// Get the value at the previous candle index.
85 fn prev_value(&self, ctx: &StrategyContext) -> Option<f64>;
86}
87
88/// Extension trait that adds condition-building methods to all indicator references.
89///
90/// This trait provides a fluent API for building conditions from indicator values.
91/// It is automatically implemented for all types that implement `IndicatorRef`.
92///
93/// # Example
94///
95/// ```ignore
96/// use finance_query::backtesting::refs::*;
97///
98/// // All these methods are available on any IndicatorRef
99/// let cond1 = rsi(14).above(70.0);
100/// let cond2 = rsi(14).below(30.0);
101/// let cond3 = rsi(14).crosses_above(30.0);
102/// let cond4 = rsi(14).crosses_below(70.0);
103/// let cond5 = rsi(14).between(30.0, 70.0);
104/// let cond6 = sma(10).above_ref(sma(20));
105/// let cond7 = sma(10).crosses_above_ref(sma(20));
106/// ```
107pub trait IndicatorRefExt: IndicatorRef + Sized {
108 /// Create a condition that checks if this indicator is above a threshold.
109 ///
110 /// # Example
111 ///
112 /// ```ignore
113 /// let overbought = rsi(14).above(70.0);
114 /// ```
115 fn above(self, threshold: f64) -> super::condition::Above<Self> {
116 super::condition::Above::new(self, threshold)
117 }
118
119 /// Create a condition that checks if this indicator is above another indicator.
120 ///
121 /// # Example
122 ///
123 /// ```ignore
124 /// let uptrend = price().above_ref(sma(200));
125 /// ```
126 fn above_ref<R: IndicatorRef>(self, other: R) -> super::condition::AboveRef<Self, R> {
127 super::condition::AboveRef::new(self, other)
128 }
129
130 /// Create a condition that checks if this indicator is below a threshold.
131 ///
132 /// # Example
133 ///
134 /// ```ignore
135 /// let oversold = rsi(14).below(30.0);
136 /// ```
137 fn below(self, threshold: f64) -> super::condition::Below<Self> {
138 super::condition::Below::new(self, threshold)
139 }
140
141 /// Create a condition that checks if this indicator is below another indicator.
142 ///
143 /// # Example
144 ///
145 /// ```ignore
146 /// let downtrend = price().below_ref(sma(200));
147 /// ```
148 fn below_ref<R: IndicatorRef>(self, other: R) -> super::condition::BelowRef<Self, R> {
149 super::condition::BelowRef::new(self, other)
150 }
151
152 /// Create a condition that checks if this indicator crosses above a threshold.
153 ///
154 /// A crossover occurs when the previous value was at or below the threshold
155 /// and the current value is above it.
156 ///
157 /// # Example
158 ///
159 /// ```ignore
160 /// let rsi_exit_oversold = rsi(14).crosses_above(30.0);
161 /// ```
162 fn crosses_above(self, threshold: f64) -> super::condition::CrossesAbove<Self> {
163 super::condition::CrossesAbove::new(self, threshold)
164 }
165
166 /// Create a condition that checks if this indicator crosses above another indicator.
167 ///
168 /// # Example
169 ///
170 /// ```ignore
171 /// let golden_cross = sma(50).crosses_above_ref(sma(200));
172 /// ```
173 fn crosses_above_ref<R: IndicatorRef>(
174 self,
175 other: R,
176 ) -> super::condition::CrossesAboveRef<Self, R> {
177 super::condition::CrossesAboveRef::new(self, other)
178 }
179
180 /// Create a condition that checks if this indicator crosses below a threshold.
181 ///
182 /// A crossover occurs when the previous value was at or above the threshold
183 /// and the current value is below it.
184 ///
185 /// # Example
186 ///
187 /// ```ignore
188 /// let rsi_enter_overbought = rsi(14).crosses_below(70.0);
189 /// ```
190 fn crosses_below(self, threshold: f64) -> super::condition::CrossesBelow<Self> {
191 super::condition::CrossesBelow::new(self, threshold)
192 }
193
194 /// Create a condition that checks if this indicator crosses below another indicator.
195 ///
196 /// # Example
197 ///
198 /// ```ignore
199 /// let death_cross = sma(50).crosses_below_ref(sma(200));
200 /// ```
201 fn crosses_below_ref<R: IndicatorRef>(
202 self,
203 other: R,
204 ) -> super::condition::CrossesBelowRef<Self, R> {
205 super::condition::CrossesBelowRef::new(self, other)
206 }
207
208 /// Create a condition that checks if this indicator is between two thresholds.
209 ///
210 /// Returns true when `low < value < high`.
211 ///
212 /// # Example
213 ///
214 /// ```ignore
215 /// let neutral_rsi = rsi(14).between(40.0, 60.0);
216 /// ```
217 fn between(self, low: f64, high: f64) -> super::condition::Between<Self> {
218 super::condition::Between::new(self, low, high)
219 }
220
221 /// Create a condition that checks if this indicator equals a value (within tolerance).
222 ///
223 /// # Example
224 ///
225 /// ```ignore
226 /// let at_zero = macd(12, 26, 9).histogram().equals(0.0, 0.001);
227 /// ```
228 fn equals(self, value: f64, tolerance: f64) -> super::condition::Equals<Self> {
229 super::condition::Equals::new(self, value, tolerance)
230 }
231}
232
233// Auto-implement IndicatorRefExt for all types that implement IndicatorRef
234impl<T: IndicatorRef + Sized> IndicatorRefExt for T {}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn test_indicator_ref_ext_methods_exist() {
242 // This test just verifies the trait methods compile
243 let _sma = sma(20);
244 let _ema = ema(12);
245 let _rsi = rsi(14);
246
247 // Verify key() works
248 assert_eq!(_sma.key(), "sma_20");
249 assert_eq!(_ema.key(), "ema_12");
250 assert_eq!(_rsi.key(), "rsi_14");
251 }
252}