scuriolus 0.1.0

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

pub use mexc::Mexc;
pub use mexc_rs::spot::v3::{enums as mexc_enums, klines::Kline};
pub use test_with_mexc::TestWithMexc;

use super::super::order::{Amount, Crypto, Order, OrderStatus, Quantity};
use super::{KlinesParams, Market, MarketError};

use anyhow::Error;
use mexc_rs::spot::{
    v3::{
        klines::{KlinesEndpoint, KlinesParams as MexcKlinesParams},
        ApiError,
    },
    MexcSpotApiClient,
};

/// Max number of klines to fetch. The Mexc API anounces 1000 but it is actually 500 from testing.
pub const KLINES_LIMIT: u32 = 500; // Mexc Documentation is wrong, it is not 1000

trait MexcBase: Market {
    fn client(&self) -> &MexcSpotApiClient;

    //TODO stock klines locally in the DB to avoir querying too much
    fn base_klines(
        &self,
        params: &KlinesParams,
    ) -> impl std::future::Future<Output = Result<Vec<Kline>, MarketError>> + Send {
        async move {
            Ok(self
                .client()
                .klines(params.into())
                .await
                .map_err(|e| Error::msg(format!("Failed to get klines {:#?} : {}", params, e)))?
                .klines)
        }
    }
}

impl From<OrderStatus> for mexc_enums::OrderStatus {
    fn from(status: OrderStatus) -> Self {
        match status {
            OrderStatus::Open => mexc_enums::OrderStatus::New,
            OrderStatus::Filled => mexc_enums::OrderStatus::Filled,
            OrderStatus::Canceled => mexc_enums::OrderStatus::Canceled,
            OrderStatus::PartiallyFilled => mexc_enums::OrderStatus::PartiallyFilled,
            OrderStatus::PartiallyCanceled => mexc_enums::OrderStatus::PartiallyCanceled,
            OrderStatus::Tested => mexc_enums::OrderStatus::Canceled,
            OrderStatus::Failed => mexc_enums::OrderStatus::Canceled,
        }
    }
}

impl From<mexc_enums::OrderStatus> for OrderStatus {
    fn from(status: mexc_enums::OrderStatus) -> Self {
        match status {
            mexc_enums::OrderStatus::New => OrderStatus::Open,
            mexc_enums::OrderStatus::Filled => OrderStatus::Filled,
            mexc_enums::OrderStatus::Canceled => OrderStatus::Canceled,
            mexc_enums::OrderStatus::PartiallyFilled => OrderStatus::PartiallyFilled,
            mexc_enums::OrderStatus::PartiallyCanceled => OrderStatus::PartiallyCanceled,
        }
    }
}

trait MexcSymbol {
    fn symbol(&self) -> String;
}

impl MexcSymbol for Order {
    fn symbol(&self) -> String {
        format!("{}{}", self.asset(), self.quote())
    }
}

impl<'a> From<&'a KlinesParams> for MexcKlinesParams<'a> {
    fn from(params: &'a KlinesParams) -> Self {
        MexcKlinesParams {
            symbol: params.symbol.as_str(),
            interval: params.interval,
            start_time: params.start_time,
            end_time: params.end_time,
            limit: params.limit,
        }
    }
}

impl From<ApiError> for MarketError {
    fn from(value: ApiError) -> Self {
        anyhow::Error::from(value).into()
    }
}