use crate::models::{Market, MarketStatus};
use chrono::{DateTime, Utc};
pub fn pick_active_market(markets: &[Market], now: DateTime<Utc>) -> Option<&Market> {
markets
.iter()
.filter(|m| {
matches!(m.status, MarketStatus::Active) && m.close_time.is_none_or(|t| t > now)
})
.min_by_key(|m| m.close_time.unwrap_or(DateTime::<Utc>::MAX_UTC))
}
pub fn pick_active_and_next(
markets: &[Market],
now: DateTime<Utc>,
) -> (Option<&Market>, Option<&Market>) {
let mut active: Vec<&Market> = markets
.iter()
.filter(|m| {
matches!(m.status, MarketStatus::Active) && m.close_time.is_none_or(|t| t > now)
})
.collect();
active.sort_by_key(|m| m.close_time.unwrap_or(DateTime::<Utc>::MAX_UTC));
let mut iter = active.into_iter();
(iter.next(), iter.next())
}
#[cfg(test)]
mod tests {
use super::*;
fn fake_market(ticker: &str, status: MarketStatus, close_minutes: Option<i64>) -> Market {
Market {
ticker: ticker.into(),
status,
close_time: close_minutes.map(|m| Utc::now() + chrono::Duration::minutes(m)),
..Default::default()
}
}
#[test]
fn picks_soonest_active() {
let now = Utc::now();
let markets = vec![
fake_market("a", MarketStatus::Active, Some(30)),
fake_market("b", MarketStatus::Active, Some(5)),
fake_market("c", MarketStatus::Active, Some(20)),
];
let pick = pick_active_market(&markets, now).unwrap();
assert_eq!(pick.ticker, "b");
}
#[test]
fn skips_closed() {
let now = Utc::now();
let markets = vec![
fake_market("a", MarketStatus::Closed, Some(5)),
fake_market("b", MarketStatus::Active, Some(20)),
];
let pick = pick_active_market(&markets, now).unwrap();
assert_eq!(pick.ticker, "b");
}
#[test]
fn skips_already_past_close_time() {
let now = Utc::now();
let markets = vec![
fake_market("a", MarketStatus::Active, Some(-1)),
fake_market("b", MarketStatus::Active, Some(10)),
];
let pick = pick_active_market(&markets, now).unwrap();
assert_eq!(pick.ticker, "b");
}
#[test]
fn pick_active_and_next_orders() {
let now = Utc::now();
let markets = vec![
fake_market("third", MarketStatus::Active, Some(45)),
fake_market("first", MarketStatus::Active, Some(5)),
fake_market("second", MarketStatus::Active, Some(20)),
];
let (a, b) = pick_active_and_next(&markets, now);
assert_eq!(a.unwrap().ticker, "first");
assert_eq!(b.unwrap().ticker, "second");
}
#[test]
fn empty_when_none_active() {
let now = Utc::now();
let markets = vec![fake_market("a", MarketStatus::Resolved, Some(5))];
assert!(pick_active_market(&markets, now).is_none());
}
}