stock-trek 0.2.11

Stock Trek time-series analysis
Documentation
use crate::{
    aligned_window::AlignedWindow,
    dto::raw_market_candles::RawMarketCandles,
    market_data::{extract::dec_to_f64, market_candle::MarketCandle, market_ohlcv::MarketOhlcv},
};
use rust_decimal::Decimal;
use std::{collections::HashMap, sync::OnceLock};
use strum::IntoEnumIterator;

#[derive(Debug)]
pub struct MarketAlignedWindow {
    candles: HashMap<AlignedWindow, Vec<MarketCandle>>,
    opens: OnceLock<HashMap<AlignedWindow, OnceLock<Vec<f64>>>>,
    highs: OnceLock<HashMap<AlignedWindow, OnceLock<Vec<f64>>>>,
    lows: OnceLock<HashMap<AlignedWindow, OnceLock<Vec<f64>>>>,
    closes: OnceLock<HashMap<AlignedWindow, OnceLock<Vec<f64>>>>,
    volumes: OnceLock<HashMap<AlignedWindow, OnceLock<Vec<f64>>>>,
    quote_volumes: OnceLock<HashMap<AlignedWindow, OnceLock<Vec<f64>>>>,
    vwaps: OnceLock<HashMap<AlignedWindow, OnceLock<Vec<f64>>>>,
}

impl MarketAlignedWindow {
    pub fn exact(&self) -> &HashMap<AlignedWindow, Vec<MarketCandle>> {
        &self.candles
    }
    pub fn opens(&self, window: AlignedWindow) -> &Vec<f64> {
        self.opens
            .get_or_init(windows_map)
            .get(&window)
            .unwrap()
            .get_or_init(|| self.aligned_values(window, |ohlcv| ohlcv.exact_open()))
    }
    pub fn highs(&self, window: AlignedWindow) -> &Vec<f64> {
        self.highs
            .get_or_init(windows_map)
            .get(&window)
            .unwrap()
            .get_or_init(|| self.aligned_values(window, |ohlcv| ohlcv.exact_high()))
    }
    pub fn lows(&self, window: AlignedWindow) -> &Vec<f64> {
        self.lows
            .get_or_init(windows_map)
            .get(&window)
            .unwrap()
            .get_or_init(|| self.aligned_values(window, |ohlcv| ohlcv.exact_low()))
    }
    pub fn closes(&self, window: AlignedWindow) -> &Vec<f64> {
        self.closes
            .get_or_init(windows_map)
            .get(&window)
            .unwrap()
            .get_or_init(|| self.aligned_values(window, |ohlcv| ohlcv.exact_close()))
    }
    pub fn volumes(&self, window: AlignedWindow) -> &Vec<f64> {
        self.volumes
            .get_or_init(windows_map)
            .get(&window)
            .unwrap()
            .get_or_init(|| self.aligned_values(window, |ohlcv| ohlcv.exact_volume()))
    }
    pub fn quote_volumes(&self, window: AlignedWindow) -> &Vec<f64> {
        self.quote_volumes
            .get_or_init(windows_map)
            .get(&window)
            .unwrap()
            .get_or_init(|| self.aligned_values(window, |ohlcv| ohlcv.exact_quote_volume()))
    }
    pub fn vwaps(&self, window: AlignedWindow) -> &Vec<f64> {
        self.vwaps
            .get_or_init(windows_map)
            .get(&window)
            .unwrap()
            .get_or_init(|| self.aligned_values(window, |ohlcv| ohlcv.exact_vwap()))
    }

    fn aligned_values(
        &self,
        window: AlignedWindow,
        to_value: impl Fn(&MarketOhlcv) -> Decimal,
    ) -> Vec<f64> {
        self.candles
            .get(&window)
            .unwrap()
            .iter()
            .map(|candle| dec_to_f64(to_value(candle.ohlcv())))
            .collect()
    }
}

fn windows_map() -> HashMap<AlignedWindow, OnceLock<Vec<f64>>> {
    AlignedWindow::iter()
        .map(|window| (window, OnceLock::new()))
        .collect()
}

impl TryFrom<HashMap<u8, RawMarketCandles>> for MarketAlignedWindow {
    type Error = String;

    fn try_from(value: HashMap<u8, RawMarketCandles>) -> Result<Self, Self::Error> {
        let mut candles = HashMap::new();
        for (window_u8, raw_candles) in value {
            let window =
                AlignedWindow::from_repr(window_u8).ok_or("Invalid AlignedWindow value")?;
            let window_candles = raw_candles
                .candles
                .into_iter()
                .map(MarketCandle::from)
                .collect();
            candles.insert(window, window_candles);
        }
        Ok(MarketAlignedWindow {
            candles,
            opens: OnceLock::new(),
            highs: OnceLock::new(),
            lows: OnceLock::new(),
            closes: OnceLock::new(),
            volumes: OnceLock::new(),
            quote_volumes: OnceLock::new(),
            vwaps: OnceLock::new(),
        })
    }
}