use chrono::{DateTime, Utc};
use snafu::{ensure, OptionExt};
use crate::{error, yahoo, Bar, Interval, Result};
fn aggregate_bars(data: yahoo::Data) -> Result<Vec<Bar>> {
let mut result = Vec::new();
let timestamps = &data.timestamps;
let quotes = &data.indicators.quotes;
if timestamps.is_empty() && quotes.is_empty() { return Ok(result); }
ensure!(!timestamps.is_empty(), error::MissingData { reason: "no timestamps for OHLCV data" });
ensure!(!quotes.is_empty(), error::MissingData { reason: "no OHLCV data" });
let quote = "es[0];
ensure!(timestamps.len() == quote.volumes.len(), error::MissingData { reason: "timestamps do not line up with OHLCV data" });
ensure!(timestamps.len() == quote.opens.len(), error::MissingData { reason: "'open' values do not line up the timestamps" });
ensure!(timestamps.len() == quote.highs.len(), error::MissingData { reason: "'high' values do not line up the timestamps" });
ensure!(timestamps.len() == quote.lows.len(), error::MissingData { reason: "'low' values do not line up the timestamps" });
ensure!(timestamps.len() == quote.closes.len(), error::MissingData { reason: "'close' values do not line up the timestamps" });
#[allow(clippy::needless_range_loop)]
for i in 0..timestamps.len() {
if quote.opens[i].is_none() || quote.highs[i].is_none() || quote.lows[i].is_none() || quote.closes[i].is_none() {
continue;
}
result.push(Bar {
timestamp: timestamps[i] * 1000,
open: quote.opens[i].context(error::InternalLogic{ reason: "missing open not caught" })?,
high: quote.highs[i].context(error::InternalLogic{ reason: "missing high not caught" })?,
low: quote.lows[i].context(error::InternalLogic{ reason: "missing low not caught" })?,
close: quote.closes[i].context(error::InternalLogic{ reason: "missing close not caught" })?,
volume: quote.volumes[i],
})
}
Ok(result)
}
pub async fn retrieve(symbol: &str) -> Result<Vec<Bar>> {
aggregate_bars(yahoo::load_daily(symbol, Interval::_6mo).await?)
}
pub async fn retrieve_interval(symbol: &str, interval: Interval) -> Result<Vec<Bar>> {
ensure!(!interval.is_intraday(), error::NoIntraday { interval });
aggregate_bars(yahoo::load_daily(symbol, interval).await?)
}
pub async fn retrieve_range(symbol: &str, start: DateTime<Utc>, end: Option<DateTime<Utc>>) -> Result<Vec<Bar>> {
let _end = end.unwrap_or_else(Utc::now);
ensure!(_end.signed_duration_since(start).num_seconds() > 0, error::InvalidStartDate);
aggregate_bars(yahoo::load_daily_range(symbol, start.timestamp(), _end.timestamp()).await?)
}