crypto_exc_all 0.1.1

Unified cryptocurrency exchange SDK facade
Documentation
use crate::adapters::ExchangeClient;
use crate::error::Result;
use crate::exchange::ExchangeId;
use crate::instrument::Instrument;
use crate::margin::MarginMode;
use serde::{Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum OrderSide {
    Buy,
    Sell,
}

impl OrderSide {
    pub(crate) fn lower(self) -> &'static str {
        match self {
            Self::Buy => "buy",
            Self::Sell => "sell",
        }
    }

    pub(crate) fn upper(self) -> &'static str {
        match self {
            Self::Buy => "BUY",
            Self::Sell => "SELL",
        }
    }
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum OrderType {
    Limit,
    Market,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum TimeInForce {
    Gtc,
    Ioc,
    Fok,
    PostOnly,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PlaceOrderRequest {
    pub instrument: Instrument,
    pub side: OrderSide,
    pub order_type: OrderType,
    pub size: String,
    pub price: Option<String>,
    pub margin_mode: Option<MarginMode>,
    pub margin_coin: Option<String>,
    pub position_side: Option<String>,
    pub trade_side: Option<String>,
    pub client_order_id: Option<String>,
    pub reduce_only: Option<bool>,
    pub time_in_force: Option<TimeInForce>,
}

impl PlaceOrderRequest {
    pub fn new(
        instrument: Instrument,
        side: OrderSide,
        order_type: OrderType,
        size: impl Into<String>,
    ) -> Self {
        Self {
            instrument,
            side,
            order_type,
            size: size.into(),
            price: None,
            margin_mode: None,
            margin_coin: None,
            position_side: None,
            trade_side: None,
            client_order_id: None,
            reduce_only: None,
            time_in_force: None,
        }
    }

    pub fn limit(
        instrument: Instrument,
        side: OrderSide,
        size: impl Into<String>,
        price: impl Into<String>,
    ) -> Self {
        Self::new(instrument, side, OrderType::Limit, size).with_price(price)
    }

    pub fn market(instrument: Instrument, side: OrderSide, size: impl Into<String>) -> Self {
        Self::new(instrument, side, OrderType::Market, size)
    }

    pub fn with_price(mut self, value: impl Into<String>) -> Self {
        self.price = Some(value.into());
        self
    }

    pub fn with_margin_mode(mut self, value: impl Into<MarginMode>) -> Self {
        self.margin_mode = Some(value.into());
        self
    }

    pub fn with_margin_coin(mut self, value: impl Into<String>) -> Self {
        self.margin_coin = Some(value.into());
        self
    }

    pub fn with_position_side(mut self, value: impl Into<String>) -> Self {
        self.position_side = Some(value.into());
        self
    }

    pub fn with_trade_side(mut self, value: impl Into<String>) -> Self {
        self.trade_side = Some(value.into());
        self
    }

    pub fn with_client_order_id(mut self, value: impl Into<String>) -> Self {
        self.client_order_id = Some(value.into());
        self
    }

    pub fn with_reduce_only(mut self, value: bool) -> Self {
        self.reduce_only = Some(value);
        self
    }

    pub fn with_time_in_force(mut self, value: TimeInForce) -> Self {
        self.time_in_force = Some(value);
        self
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CancelOrderRequest {
    pub instrument: Instrument,
    pub order_id: Option<String>,
    pub client_order_id: Option<String>,
    pub margin_coin: Option<String>,
}

impl CancelOrderRequest {
    pub fn new(instrument: Instrument) -> Self {
        Self {
            instrument,
            order_id: None,
            client_order_id: None,
            margin_coin: None,
        }
    }

    pub fn by_order_id(instrument: Instrument, order_id: impl Into<String>) -> Self {
        Self::new(instrument).with_order_id(order_id)
    }

    pub fn by_client_order_id(instrument: Instrument, client_order_id: impl Into<String>) -> Self {
        Self::new(instrument).with_client_order_id(client_order_id)
    }

    pub fn with_order_id(mut self, value: impl Into<String>) -> Self {
        self.order_id = Some(value.into());
        self
    }

    pub fn with_client_order_id(mut self, value: impl Into<String>) -> Self {
        self.client_order_id = Some(value.into());
        self
    }

    pub fn with_margin_coin(mut self, value: impl Into<String>) -> Self {
        self.margin_coin = Some(value.into());
        self
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct OrderAck {
    pub exchange: ExchangeId,
    pub instrument: Instrument,
    pub exchange_symbol: String,
    pub order_id: Option<String>,
    pub client_order_id: Option<String>,
    pub status: Option<String>,
    pub raw: Value,
}

pub struct TradeFacade<'a> {
    pub(crate) client: &'a ExchangeClient,
}

impl<'a> TradeFacade<'a> {
    pub(crate) fn new(client: &'a ExchangeClient) -> Self {
        Self { client }
    }

    pub async fn place_order(&self, request: PlaceOrderRequest) -> Result<OrderAck> {
        self.client.place_order(request).await
    }

    pub async fn cancel_order(&self, request: CancelOrderRequest) -> Result<OrderAck> {
        self.client.cancel_order(request).await
    }
}