scuriolus 0.1.0

Scuriolus is a modular trading bot platform. It can apply different strategies to various markets, as described below.
Documentation
mod running_cheat_clock;
mod stable_cheat_clock;
mod true_clock;

use std::{fmt, future::Future, pin::Pin, time::Duration};

use anyhow::Error;
use chrono::{DateTime, TimeDelta, Utc};
use tokio::{select, sync::Notify, time::sleep};

pub use {
    running_cheat_clock::{RunningCheatClock, RunningCheatClockFactory, RunningCheatClockRemote},
    stable_cheat_clock::{StableCheatClock, StableCheatClockFactory, StableCheatClockRemote},
    true_clock::TrueClock,
};

/// Trait for something giving time with `now()`.
pub trait ClockBase: Send + Sync {
    fn now(&self) -> DateTime<Utc>;
}

/// A [`Clock`] is something giving time with `now()` but also able to sleep.
pub trait Clock: Send + Sync + fmt::Debug + ClockBase {
    fn sleep(&self, delay: TimeDelta) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
    fn sleep_until(&self, deadline: DateTime<Utc>)
        -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;

    fn clone_box(&self) -> Box<dyn Clock>;
    fn synchronize(&self);
}

/// The refresh rate of the remote clock.
#[cfg(test)]
pub const REMOTE_REFRESH_RATE: Duration = Duration::from_millis(10);
#[cfg(not(test))]
pub const REMOTE_REFRESH_RATE: Duration = Duration::from_secs(3);

/// The refresh rate for clocks that might lost sync with their remote.
pub const SAFETY_REFRESH_DELAY: Duration = Duration::from_secs(120);
pub trait CheatClockRemote: ClockBase {
    #[cfg(test)]
    fn not_blocking(&self);

    fn remote_now(&self) -> DateTime<Utc>;
    fn advance_toward(&self, date: DateTime<Utc>);
    fn notify(&self) -> &Notify;
    fn is_sync(&self) -> bool;

    fn wait_for_the_clock(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
        Box::pin(async move {
            loop {
                if self.is_sync() {
                    break;
                }

                select! {
                    _ = sleep(REMOTE_REFRESH_RATE) => {},
                    _ = self.notify().notified() => {}
                }
            }
        })
    }

    fn clone_box(&self) -> Box<dyn CheatClockRemote>;
}

/// Factory's trait for [`CheatClockRemote`] and [`Clock`].
#[allow(clippy::type_complexity)]
pub trait CheatClockFactory {
    fn get_clock(
        &self,
        date: DateTime<Utc>,
    ) -> Result<(Box<dyn CheatClockRemote>, Box<dyn Clock>), Error>;
}