Skip to main content

vecdb/variants/eager/
lookback.rs

1use crate::{AnyVec, Error, Exit, GenericStoredVec, IterableVec, Result, StoredVec, VecValue};
2
3use super::{CheckedSub, EagerVec};
4
5impl<V> EagerVec<V>
6where
7    V: StoredVec,
8    V::I: CheckedSub,
9{
10    fn compute_with_lookback<A, F>(
11        &mut self,
12        max_from: V::I,
13        source: &impl IterableVec<V::I, A>,
14        lookback_len: usize,
15        exit: &Exit,
16        transform: F,
17    ) -> Result<()>
18    where
19        A: VecValue + Default,
20        F: Fn(usize, A, A) -> V::T,
21    {
22        self.validate_computed_version_or_reset(source.version())?;
23
24        self.truncate_if_needed(max_from)?;
25
26        self.repeat_until_complete(exit, |this| {
27            let skip = this.len();
28            let mut lookback = source.create_lookback(skip, lookback_len, 0);
29
30            for (i, current) in source.iter().enumerate().skip(skip) {
31                let previous = lookback.get_and_push(i, current.clone(), A::default());
32                let result = transform(i, current, previous);
33                this.checked_push_at(i, result)?;
34
35                if this.batch_limit_reached() {
36                    break;
37                }
38            }
39
40            Ok(())
41        })
42    }
43
44    pub fn compute_previous_value<A>(
45        &mut self,
46        max_from: V::I,
47        source: &impl IterableVec<V::I, A>,
48        len: usize,
49        exit: &Exit,
50    ) -> Result<()>
51    where
52        A: VecValue + Default,
53        f32: From<A>,
54        V::T: From<f32>,
55    {
56        self.compute_with_lookback(max_from, source, len, exit, |i, _, previous| {
57            // If there's no previous value (i < len), return NaN
58            if i < len {
59                V::T::from(f32::NAN)
60            } else {
61                V::T::from(f32::from(previous))
62            }
63        })
64    }
65
66    /// Compute N-period change. Converts source values to output type before subtraction
67    /// to properly handle negative changes (e.g., unsigned source to signed output).
68    pub fn compute_change<A>(
69        &mut self,
70        max_from: V::I,
71        source: &impl IterableVec<V::I, A>,
72        len: usize,
73        exit: &Exit,
74    ) -> Result<()>
75    where
76        A: VecValue + Default + Into<V::T>,
77        V::T: CheckedSub + Default,
78    {
79        self.compute_with_lookback(max_from, source, len, exit, |i, current, previous| {
80            if i < len {
81                V::T::default()
82            } else {
83                let current: V::T = current.into();
84                let previous: V::T = previous.into();
85                current.checked_sub(previous).unwrap()
86            }
87        })
88    }
89
90    pub fn compute_percentage_change<A>(
91        &mut self,
92        max_from: V::I,
93        source: &impl IterableVec<V::I, A>,
94        len: usize,
95        exit: &Exit,
96    ) -> Result<()>
97    where
98        A: VecValue + Default,
99        f32: From<A>,
100        V::T: From<f32>,
101    {
102        self.compute_with_lookback(max_from, source, len, exit, |i, current, previous| {
103            // If there's no previous value (i < len), return NaN
104            if i < len {
105                V::T::from(f32::NAN)
106            } else {
107                let current_f32 = f32::from(current);
108                let previous_f32 = f32::from(previous);
109                V::T::from(((current_f32 / previous_f32) - 1.0) * 100.0)
110            }
111        })
112    }
113
114    pub fn compute_cagr<A>(
115        &mut self,
116        max_from: V::I,
117        percentage_returns: &impl IterableVec<V::I, A>,
118        days: usize,
119        exit: &Exit,
120    ) -> Result<()>
121    where
122        A: VecValue + Default,
123        f32: From<A>,
124        V::T: From<f32>,
125    {
126        if days == 0 || !days.is_multiple_of(365) {
127            return Err(Error::InvalidArgument(
128                "days must be non-zero and a multiple of 365",
129            ));
130        }
131
132        let years = days / 365;
133
134        self.compute_transform(
135            max_from,
136            percentage_returns,
137            |(i, percentage, ..)| {
138                let cagr = (((f32::from(percentage) / 100.0 + 1.0).powf(1.0 / years as f32)) - 1.0)
139                    * 100.0;
140                (i, V::T::from(cagr))
141            },
142            exit,
143        )
144    }
145}