use std::fmt;
use anyhow::Error;
use derive_getters::Getters;
use rust_decimal::Decimal;
use strum_macros::Display;
use crate::trade_log;
use super::market::{mexc_base::mexc_enums, MarketKind};
#[derive(Debug, Clone, Copy, Display, serde::Serialize, serde::Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub enum Crypto {
BTC,
DOGE,
ETH,
SOL,
USDC,
USDT,
XEL,
}
#[derive(Debug, Clone, Copy, Display, serde::Serialize, serde::Deserialize, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub enum OrderStatus {
Open,
PartiallyFilled,
PartiallyCanceled,
Filled,
Canceled,
Tested,
Failed,
}
pub type Amount = Decimal;
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Display)]
pub enum Quantity {
Asset(Amount), Quote(Amount), }
impl Quantity {
pub fn get_amount(&self) -> Amount {
match self {
Quantity::Asset(amount) => *amount,
Quantity::Quote(amount) => *amount,
}
}
pub fn get_amounts(&self) -> (Option<Amount>, Option<Amount>) {
match self {
Quantity::Asset(amount) => (Some(*amount), None),
Quantity::Quote(amount) => (None, Some(*amount)),
}
}
}
#[derive(Debug, Clone, Getters, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct Order {
#[getter(skip)]
_id: String, market: MarketKind,
market_id: Option<String>,
asset: Crypto,
quote: Crypto,
side: mexc_enums::OrderSide,
order_type: mexc_enums::OrderType,
status: OrderStatus,
quantity: Quantity,
price: Option<Amount>,
executed_qty: Amount,
cummulative_quote_qty: Amount,
}
impl Order {
pub fn new(
buy: Crypto,
with: Crypto,
side: mexc_enums::OrderSide,
order_type: mexc_enums::OrderType,
quantity: Quantity,
price: Option<Amount>,
) -> Result<Self, Error> {
match order_type {
mexc_enums::OrderType::Limit => {
if price.is_none() {
return Err(Error::msg("Limit order should have a price"));
}
}
mexc_enums::OrderType::Market => {
if price.is_some() {
return Err(Error::msg("Market order should not have a price"));
}
}
mexc_enums::OrderType::LimitMaker
| mexc_enums::OrderType::ImmediateOrCancel
| mexc_enums::OrderType::FillOrKill => {
return Err(Error::msg("Unimplemented order type"))
}
}
if quantity.get_amount() <= Amount::from(0) {
return Err(Error::msg("Amount should be greater than 0"));
}
if price.is_some() && price.unwrap() <= Amount::from(0) {
return Err(Error::msg("Price should be greater than 0"));
}
Ok(Order {
asset: buy,
quote: with,
side,
order_type,
quantity,
price,
market: MarketKind::None,
status: OrderStatus::Open,
_id: uuid::Uuid::new_v4().to_string(),
market_id: None,
executed_qty: Amount::from(0),
cummulative_quote_qty: Amount::from(0),
})
}
pub fn id(&self) -> &String {
&self._id
}
pub fn set_status(&mut self, status: OrderStatus) {
self.status = status;
trade_log!("Update on order {}: status now {}", self._id, status);
}
pub fn set_executed(&mut self, executed_qty: Amount, cummulative_quote_qty: Amount) {
self.executed_qty = executed_qty;
self.cummulative_quote_qty = cummulative_quote_qty;
tracing::debug!(
"Order {} executed: {} - {}",
self._id,
executed_qty,
cummulative_quote_qty
);
}
pub fn set_asset_quantity(&mut self, asset_quantity: Amount) -> Result<(), Error> {
if self.order_type != mexc_enums::OrderType::Market
&& self.side != mexc_enums::OrderSide::Buy
{
return Err(Error::msg(
"Asset quantity can only be modified for buy market orders",
));
}
self.quantity = Quantity::Asset(asset_quantity);
Ok(())
}
pub fn set_market_id(&mut self, id: String) {
self.market_id = Some(id);
}
pub fn quote_quantity(&self) -> Result<Amount, Error> {
match self.quantity {
Quantity::Asset(amount) => {
Ok(self.price.ok_or_else(|| Error::msg("Price not set"))? * amount)
}
Quantity::Quote(amount) => Ok(amount),
}
}
pub fn is_done(&self) -> bool {
matches!(
self.status,
OrderStatus::Filled
| OrderStatus::Canceled
| OrderStatus::PartiallyCanceled
| OrderStatus::Tested
| OrderStatus::Failed
)
}
pub fn set_price(&mut self, price: Amount) -> Result<(), Error> {
if self.order_type != mexc_enums::OrderType::Market && self.status != OrderStatus::Filled {
return Err(Error::msg(
"Price can only be modified except for filled market orders",
));
}
self.price = Some(price);
Ok(())
}
}
impl fmt::Display for Order {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Buy: {}, With: {}, Side: {:?}, Type: {:?},Amount: {}, Price: {:?}, Market: {}, Status: {}",
self.asset, self.quote, self.side, self.order_type, self.quantity, self.price, self.market, self.status
)
}
}
#[derive(Debug, Clone, Copy)]
pub struct OrderDraft {
pub asset: Crypto,
pub currency: Crypto,
pub side: mexc_enums::OrderSide,
pub order_type: mexc_enums::OrderType,
pub amount: Quantity,
pub price: Option<Amount>,
}