use std::collections::BTreeMap; use brk_error::{Error, Result}; use brk_types::{Cents, Close, Date, Dollars, High, Low, OHLCCents, Open, Timestamp}; /// Parse OHLC value from a JSON array element at given index pub fn parse_cents(array: &[serde_json::Value], index: usize) -> Cents { let value = array .get(index) .and_then(|v| v.as_str()) .and_then(|s| s.parse::().ok()) .unwrap_or_else(|| { tracing::warn!( "Failed to parse price at index {index}: {:?}", array.get(index) ); 0.0 }); Cents::from(Dollars::from(value)) } /// Build OHLCCentsUnsigned from array indices 1-4 (open, high, low, close) pub fn ohlc_from_array(array: &[serde_json::Value]) -> OHLCCents { OHLCCents::from(( Open::new(parse_cents(array, 1)), High::new(parse_cents(array, 2)), Low::new(parse_cents(array, 3)), Close::new(parse_cents(array, 4)), )) } /// Compute OHLC for a block from a time series of minute data. /// Aggregates all candles between previous_timestamp and timestamp. pub fn compute_ohlc_from_range( tree: &BTreeMap, timestamp: Timestamp, previous_timestamp: Option, source_name: &str, ) -> Result { let previous_ohlc = previous_timestamp.map_or(Some(OHLCCents::default()), |t| tree.get(&t).cloned()); let last_ohlc = tree.get(×tamp); if previous_ohlc.is_none() || last_ohlc.is_none() { return Err(Error::NotFound(format!( "Couldn't find timestamp in {source_name}" ))); } let previous_ohlc = previous_ohlc.unwrap(); let mut result = OHLCCents::from(previous_ohlc.close); let start = previous_timestamp.unwrap_or(Timestamp::new(0)); let end = timestamp; // Skip if re-org (start >= end) if start < end { for (_, ohlc) in tree.range(start..=end).skip(1) { if ohlc.high > result.high { result.high = ohlc.high; } if ohlc.low < result.low { result.low = ohlc.low; } result.close = ohlc.close; } } Ok(result) } /// Parse timestamp from milliseconds (Binance format) pub fn timestamp_from_ms(ms: u64) -> Timestamp { Timestamp::from((ms / 1_000) as u32) } /// Parse timestamp from seconds (Kraken format) pub fn timestamp_from_secs(secs: u64) -> Timestamp { Timestamp::from(secs as u32) } /// Convert timestamp to date pub fn date_from_timestamp(timestamp: Timestamp) -> Date { Date::from(timestamp) }