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