use crate::{ config::{Config, ALPACA_API_BASE}, database, }; use log::info; use qrust::{ alpaca, types::{self, Calendar}, utils::{backoff, duration_until}, }; use std::sync::Arc; use time::OffsetDateTime; use tokio::{join, sync::mpsc, time::sleep}; pub enum Status { Open, Closed, } pub struct Message { pub status: Status, pub next_switch: OffsetDateTime, } impl From for Message { fn from(clock: types::alpaca::api::incoming::clock::Clock) -> Self { if clock.is_open { Self { status: Status::Open, next_switch: clock.next_close, } } else { Self { status: Status::Closed, next_switch: clock.next_open, } } } } pub async fn run(config: Arc, sender: mpsc::Sender) { loop { let clock_future = async { alpaca::clock::get( &config.alpaca_client, &config.alpaca_rate_limiter, Some(backoff::infinite()), &ALPACA_API_BASE, ) .await .unwrap() }; let calendar_future = async { alpaca::calendar::get( &config.alpaca_client, &config.alpaca_rate_limiter, &types::alpaca::api::outgoing::calendar::Calendar::default(), Some(backoff::infinite()), &ALPACA_API_BASE, ) .await .unwrap() .into_iter() .map(Calendar::from) .collect::>() }; let (clock, calendar) = join!(clock_future, calendar_future); let sleep_until = duration_until(if clock.is_open { info!("Market is open, will close at {}.", clock.next_close); clock.next_close } else { info!("Market is closed, will reopen at {}.", clock.next_open); clock.next_open }); let sleep_future = sleep(sleep_until); let calendar_future = async { database::calendar::upsert_batch_and_delete( &config.clickhouse_client, &config.clickhouse_concurrency_limiter, &calendar, ) .await .unwrap(); }; join!(sleep_future, calendar_future); sender.send(clock.into()).await.unwrap(); } }