use crate::Result;
pub trait PriceProvider {
fn spot<'a>(
&'a self,
ticker: &'a str,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<f64>> + Send + 'a>>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Concept, DivEvent, DividendSnapshot};
struct Fixed(f64);
impl PriceProvider for Fixed {
fn spot<'a>(
&'a self,
_t: &'a str,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = crate::Result<f64>> + Send + 'a>>
{
let v = self.0;
Box::pin(async move { Ok(v) })
}
}
#[tokio::test]
async fn yield_with_uses_provider_price() {
let today = chrono::Utc::now().date_naive();
let mk = |days_ago: i64, amt: f64| {
let d = today - chrono::Duration::days(days_ago);
DivEvent {
period_start: d,
period_end: d,
amount: amt,
concept: Concept::Declared,
accn: "x".into(),
form: None,
}
};
let snap = DividendSnapshot::from_events(
"KO".into(),
21344,
vec![
mk(280, 0.485),
mk(190, 0.485),
mk(100, 0.485),
mk(10, 0.485),
],
);
let expected = 1.94 / 50.0;
let y = snap.yield_on(50.0);
assert!((y - expected).abs() < 1e-9, "yield_on mismatch: {y}");
let y_via_provider = snap.yield_with(&Fixed(50.0)).await.unwrap();
assert!(
(y_via_provider - expected).abs() < 1e-9,
"yield_with mismatch: {y_via_provider}"
);
}
}