kestrel_chartkit/indicator/
source_mapped.rs1use super::{Indicator, IndicatorAlert, IndicatorOutput};
18use crate::model::{Bar, BarValidationError, Source};
19
20pub struct SourceMapped<I: Indicator> {
23 inner: I,
24 source: Source,
25}
26
27impl<I: Indicator> SourceMapped<I> {
28 pub fn new(inner: I, source: Source) -> Self {
29 Self { inner, source }
30 }
31
32 fn map_bar(&self, bar: &Bar) -> Bar {
33 let value = self.source.extract(bar);
34 Bar::new(bar.timestamp, value, value, value, value, bar.volume)
35 }
36}
37
38impl<I: Indicator> Indicator for SourceMapped<I> {
39 fn name(&self) -> &str {
40 self.inner.name()
41 }
42 fn warmup_period(&self) -> usize {
43 self.inner.warmup_period()
44 }
45 fn reset(&mut self) {
46 self.inner.reset()
47 }
48 fn on_bar(&mut self, bar: &Bar) -> Option<IndicatorOutput> {
49 let mapped = self.map_bar(bar);
50 self.inner.on_bar(&mapped)
51 }
52 fn on_checked_bar(&mut self, bar: &Bar) -> Result<Option<IndicatorOutput>, BarValidationError> {
53 bar.validate()?;
54 Ok(self.on_bar(bar))
55 }
56 fn alerts(&self) -> Vec<IndicatorAlert> {
57 self.inner.alerts()
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use crate::indicator::moving_averages::SmaEngine;
65
66 #[test]
67 fn test_source_mapped_uses_selected_source_not_close() {
68 let mut close_sma = SmaEngine::new(2);
69 let mut open_sma = SourceMapped::new(SmaEngine::new(2), Source::Open);
70
71 let bars = [
73 Bar::new(0, 10.0, 12.0, 8.0, 11.0, 100.0),
74 Bar::new(60, 20.0, 22.0, 18.0, 21.0, 100.0),
75 ];
76
77 let mut close_out = None;
78 let mut open_out = None;
79 for bar in &bars {
80 close_out = close_sma.on_bar(bar);
81 open_out = open_sma.on_bar(bar);
82 }
83
84 assert_eq!(close_out.unwrap().value, (11.0 + 21.0) / 2.0);
85 assert_eq!(open_out.unwrap().value, (10.0 + 20.0) / 2.0);
86 }
87
88 #[test]
89 fn test_source_mapped_preserves_volume_and_delegates_metadata() {
90 let mut hl2_sma = SourceMapped::new(SmaEngine::new(1), Source::Hl2);
91 let bar = Bar::new(0, 10.0, 20.0, 0.0, 15.0, 250.0);
92 let out = hl2_sma.on_bar(&bar).unwrap();
94 assert_eq!(out.value, 10.0);
95 assert_eq!(hl2_sma.name(), "sma");
96 assert_eq!(hl2_sma.warmup_period(), SmaEngine::new(1).warmup_period());
97 }
98}