use std::fmt; use std::fmt::{Display, Formatter}; use std::hash::{Hash, Hasher}; use dyn_clone::clone_box; use crate::connectors::Exchange; use crate::currency::{Symbol, SymbolPair}; use crate::strategy::OrderStrategy; /*************** * Prices ***************/ #[derive(Copy, Clone, Debug)] pub struct PriceTicker { pub bid: f64, pub bid_size: f64, pub ask: f64, pub ask_size: f64, pub daily_change: f64, pub daily_change_perc: f64, pub last_price: f64, pub volume: f64, pub high: f64, pub low: f64, } /*************** * Orders ***************/ #[derive(Debug)] pub enum OrderBookEntry { Trading { price: f64, count: u64, amount: f64, }, Funding { rate: f64, period: u64, count: u64, amount: f64, }, } #[derive(Debug)] pub struct OrderBook { pair: SymbolPair, entries: Vec, } impl OrderBook { pub fn new(pair: SymbolPair) -> Self { OrderBook { pair, entries: Vec::new(), } } pub fn with_entries(mut self, entries: Vec) -> Self { self.entries = entries; self } // TODO: distinguish between trading and funding pub fn bids(&self) -> Vec<&OrderBookEntry> { self.entries .iter() .filter(|x| match x { OrderBookEntry::Trading { amount, .. } => amount > &0.0, OrderBookEntry::Funding { amount, .. } => amount < &0.0, }) .collect() } // TODO: distinguish between trading and funding pub fn asks(&self) -> Vec<&OrderBookEntry> { self.entries .iter() .filter(|x| match x { OrderBookEntry::Trading { amount, .. } => amount < &0.0, OrderBookEntry::Funding { amount, .. } => amount > &0.0, }) .collect() } pub fn highest_bid(&self) -> f64 { self.bids() .iter() .map(|x| match x { OrderBookEntry::Trading { price, .. } => price, OrderBookEntry::Funding { rate, .. } => rate, }) .fold(f64::NEG_INFINITY, |a, &b| a.max(b)) } pub fn lowest_ask(&self) -> f64 { self.asks() .iter() .map(|x| match x { OrderBookEntry::Trading { price, .. } => price, OrderBookEntry::Funding { rate, .. } => rate, }) .fold(f64::INFINITY, |a, &b| a.min(b)) } } #[derive(Debug)] pub enum OrderFee { Maker(f64), Taker(f64), } #[derive(Debug)] pub struct OrderDetails { exchange: Exchange, pair: SymbolPair, platform: TradingPlatform, kind: OrderKind, execution_timestamp: u64, id: u64, } impl OrderDetails { pub fn new( exchange: Exchange, id: u64, pair: SymbolPair, platform: TradingPlatform, kind: OrderKind, execution_timestamp: u64, ) -> Self { OrderDetails { exchange, pair, platform, kind, execution_timestamp, id, } } pub fn id(&self) -> u64 { self.id } pub fn pair(&self) -> &SymbolPair { &self.pair } } #[derive(Debug)] pub struct ActiveOrder { exchange: Exchange, id: u64, group_id: Option, client_id: Option, pair: SymbolPair, order_form: OrderForm, creation_timestamp: u64, update_timestamp: u64, strategy: Option>, } impl ActiveOrder { pub fn new( exchange: Exchange, id: u64, pair: SymbolPair, order_form: OrderForm, creation_timestamp: u64, update_timestamp: u64, ) -> Self { Self { exchange, id, group_id: None, client_id: None, pair, order_form, creation_timestamp, update_timestamp, strategy: None, } } pub fn with_group_id(mut self, group_id: Option) -> Self { self.group_id = group_id; self } pub fn with_client_id(mut self, client_id: Option) -> Self { self.client_id = client_id; self } pub fn with_strategy(mut self, strategy: Option>) -> Self { self.strategy = strategy; self } pub fn with_leverage(mut self, leverage: Option) -> Self { self.order_form = self.order_form.with_leverage(leverage); self } pub fn exchange(&self) -> Exchange { self.exchange } pub fn id(&self) -> u64 { self.id } pub fn group_id(&self) -> Option { self.group_id } pub fn client_id(&self) -> Option { self.client_id } pub fn pair(&self) -> &SymbolPair { &self.pair } pub fn order_form(&self) -> &OrderForm { &self.order_form } pub fn creation_timestamp(&self) -> u64 { self.creation_timestamp } pub fn update_timestamp(&self) -> u64 { self.update_timestamp } pub fn strategy(&self) -> &Option> { &self.strategy } } impl Hash for ActiveOrder { fn hash(&self, state: &mut H) { state.write(&self.id.to_le_bytes()); } } impl PartialEq for ActiveOrder { fn eq(&self, other: &Self) -> bool { self.id == other.id && self.client_id == other.client_id && self.group_id == other.group_id } } impl Eq for ActiveOrder {} impl Clone for ActiveOrder { fn clone(&self) -> Self { Self { exchange: self.exchange, id: self.id, group_id: self.group_id, client_id: self.client_id, pair: self.pair.clone(), order_form: self.order_form.clone(), creation_timestamp: self.creation_timestamp, update_timestamp: self.update_timestamp, strategy: self.strategy.as_ref().map(|x| clone_box(&**x)) } } } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum TradingPlatform { Exchange, Derivative, Funding, Margin, } impl TradingPlatform { pub fn as_str(&self) -> &'static str { match self { TradingPlatform::Exchange => "Exchange", TradingPlatform::Derivative => "Derivative", TradingPlatform::Funding => "Funding", TradingPlatform::Margin => "Margin", } } } impl Display for TradingPlatform { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) } } #[derive(Copy, Clone, Debug)] pub enum OrderKind { Limit { price: f64 }, Market, Stop { price: f64 }, StopLimit { stop_price: f64, limit_price: f64 }, TrailingStop { distance: f64 }, FillOrKill { price: f64 }, ImmediateOrCancel { price: f64 }, } impl OrderKind { pub fn as_str(&self) -> &'static str { match self { OrderKind::Limit { .. } => "Limit", OrderKind::Market { .. } => "Market", OrderKind::Stop { .. } => "Stop", OrderKind::StopLimit { .. } => "Stop Limit", OrderKind::TrailingStop { .. } => "Trailing Stop", OrderKind::FillOrKill { .. } => "Fill or Kill", OrderKind::ImmediateOrCancel { .. } => "Immediate or Cancel", } } } impl Display for OrderKind { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { OrderKind::Limit { price } => { write!(f, "[{} | Price: {:0.5}]", self.as_str(), price, ) } OrderKind::Market => { write!(f, "[{}]", self.as_str()) } OrderKind::Stop { price } => { write!(f, "[{} | Price: {:0.5}]", self.as_str(), price, ) } OrderKind::StopLimit { stop_price, limit_price } => { write!( f, "[{} | Stop: {:0.5}, Limit: {:0.5}]", self.as_str(), stop_price, limit_price ) } OrderKind::TrailingStop { distance } => { write!(f, "[{} | Distance: {:0.5}]", self.as_str(), distance, ) } OrderKind::FillOrKill { price } => { write!(f, "[{} | Price: {:0.5}]", self.as_str(), price, ) } OrderKind::ImmediateOrCancel { price } => { write!(f, "[{} | Price: {:0.5}]", self.as_str(), price, ) } } } } #[derive(Debug, Clone)] pub struct OrderForm { pair: SymbolPair, kind: OrderKind, platform: TradingPlatform, amount: f64, leverage: Option, metadata: Option, } impl OrderForm { pub fn new( pair: SymbolPair, order_kind: OrderKind, platform: TradingPlatform, amount: f64, ) -> Self { Self { pair, kind: order_kind, platform, amount, leverage: None, metadata: None, } } pub fn with_leverage(mut self, leverage: Option) -> Self { self.leverage = leverage; self } pub fn with_metadata(mut self, metadata: Option) -> Self { self.metadata = metadata; self } pub fn pair(&self) -> &SymbolPair { &self.pair } pub fn kind(&self) -> OrderKind { self.kind } pub fn platform(&self) -> &TradingPlatform { &self.platform } pub fn amount(&self) -> f64 { self.amount } pub fn price(&self) -> Option { match self.kind { OrderKind::Limit { price, .. } => Some(price), OrderKind::Market { .. } => None, OrderKind::Stop { price, .. } => Some(price), OrderKind::StopLimit { stop_price: price, .. } => Some(price), OrderKind::TrailingStop { .. } => None, OrderKind::FillOrKill { price, .. } => Some(price), OrderKind::ImmediateOrCancel { price, .. } => Some(price), } } pub fn leverage(&self) -> Option { self.leverage } pub fn metadata(&self) -> &Option { &self.metadata } } #[derive(Debug)] pub struct OrderMetadata { position_id: Option, strategy: Option>, } impl Clone for OrderMetadata { fn clone(&self) -> Self { Self { position_id: self.position_id.clone(), strategy: self.strategy.as_ref().map(|x| clone_box(&**x)), } } } impl OrderMetadata { pub fn new() -> Self { Self { position_id: None, strategy: None, } } pub fn with_position_id(mut self, position_id: Option) -> Self { self.position_id = position_id; self } pub fn with_strategy(mut self, strategy: Option>) -> Self { self.strategy = strategy; self } pub fn position_id(&self) -> Option { self.position_id } pub fn cloned_strategy(&self) -> Option> { match &self.strategy { None => { None } Some(strategy) => { Some(clone_box(&**strategy)) } } } } /*************** * Positions ***************/ #[derive(Clone, Debug)] pub struct Position { pair: SymbolPair, state: PositionState, profit_state: Option, amount: f64, base_price: f64, pl: f64, pl_perc: f64, price_liq: f64, position_id: u64, creation_date: Option, creation_update: Option, platform: TradingPlatform, leverage: f64, } impl Position { pub fn new( pair: SymbolPair, state: PositionState, amount: f64, base_price: f64, pl: f64, pl_perc: f64, price_liq: f64, position_id: u64, platform: TradingPlatform, leverage: f64, ) -> Self { Position { pair, state, amount, base_price, pl, pl_perc, price_liq, position_id, creation_date: None, creation_update: None, profit_state: None, platform, leverage, } } pub fn with_creation_date(mut self, creation_date: Option) -> Self { self.creation_date = creation_date; self } pub fn with_creation_update(mut self, creation_update: Option) -> Self { self.creation_update = creation_update; self } pub fn with_profit_state(mut self, profit_state: Option) -> Self { self.profit_state = profit_state; self } pub fn update_profit_loss(&mut self, best_offer: f64, fee_perc: f64) { let (base_price, delta) = { if self.is_short() { let base_price = self.base_price * (1.0 - fee_perc / 100.0); let delta = base_price - best_offer; (base_price, delta) } else { let base_price = self.base_price * (1.0 + fee_perc / 100.0); let delta = best_offer - base_price; (base_price, delta) } }; let profit_loss = delta * self.amount.abs(); let profit_loss_percentage = delta / base_price * 100.0; self.pl = profit_loss; self.pl_perc = profit_loss_percentage; } pub fn with_profit_loss(mut self, profit_loss: f64) -> Self { self.pl = profit_loss; self } pub fn pair(&self) -> &SymbolPair { &self.pair } pub fn state(&self) -> PositionState { self.state } pub fn amount(&self) -> f64 { self.amount } pub fn base_price(&self) -> f64 { self.base_price } pub fn pl(&self) -> f64 { self.pl } pub fn pl_perc(&self) -> f64 { self.pl_perc } pub fn price_liq(&self) -> f64 { self.price_liq } pub fn id(&self) -> u64 { self.position_id } pub fn profit_state(&self) -> Option { self.profit_state } pub fn creation_date(&self) -> Option { self.creation_date } pub fn creation_update(&self) -> Option { self.creation_update } pub fn is_short(&self) -> bool { self.amount.is_sign_negative() } pub fn is_long(&self) -> bool { self.amount.is_sign_positive() } pub fn platform(&self) -> TradingPlatform { self.platform } pub fn leverage(&self) -> f64 { self.leverage } } impl Hash for Position { fn hash(&self, state: &mut H) { state.write(&self.id().to_le_bytes()) } } impl PartialEq for Position { fn eq(&self, other: &Self) -> bool { self.id() == other.id() } } impl Eq for Position {} #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub enum PositionProfitState { Critical, Loss, BreakEven, MinimumProfit, Profit, } #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] pub enum PositionState { Closed, Open, } pub enum WalletKind { Exchange, Margin, Funding, } #[derive(Debug)] pub struct Trade { pub trade_id: u64, pub pair: SymbolPair, pub execution_timestamp: u64, pub price: f64, pub amount: f64, pub fee: OrderFee, pub fee_currency: Symbol, } #[derive(Debug)] pub enum TradingFees { Maker { platform: TradingPlatform, percentage: f64, }, Taker { platform: TradingPlatform, percentage: f64, }, }