core/src/models.rs

669 lines
16 KiB
Rust
Raw Normal View History

2021-01-23 16:13:37 +00:00
use std::fmt;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
2021-01-16 11:43:16 +00:00
use dyn_clone::clone_box;
2021-01-24 20:53:33 +00:00
use crate::connectors::Exchange;
use crate::currency::{Symbol, SymbolPair};
use crate::strategy::OrderStrategy;
2021-01-14 12:42:23 +00:00
/***************
* 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<OrderBookEntry>,
}
impl OrderBook {
pub fn new(pair: SymbolPair) -> Self {
OrderBook {
pair,
entries: Vec::new(),
}
}
pub fn with_entries(mut self, entries: Vec<OrderBookEntry>) -> 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)]
2021-01-22 15:37:53 +00:00
pub struct ActiveOrder {
exchange: Exchange,
id: u64,
group_id: Option<u64>,
client_id: Option<u64>,
pair: SymbolPair,
order_form: OrderForm,
creation_timestamp: u64,
update_timestamp: u64,
strategy: Option<Box<dyn OrderStrategy>>,
}
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<u64>) -> Self {
self.group_id = group_id;
self
}
pub fn with_client_id(mut self, client_id: Option<u64>) -> Self {
self.client_id = client_id;
self
}
pub fn with_strategy(mut self, strategy: Option<Box<dyn OrderStrategy>>) -> Self {
self.strategy = strategy;
self
}
pub fn with_leverage(mut self, leverage: Option<f64>) -> 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<u64> {
self.group_id
}
pub fn client_id(&self) -> Option<u64> {
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<Box<dyn OrderStrategy>> {
&self.strategy
}
}
2021-01-22 15:37:53 +00:00
impl Hash for ActiveOrder {
2021-01-16 11:43:16 +00:00
fn hash<H: Hasher>(&self, state: &mut H) {
2021-01-23 16:13:37 +00:00
state.write(&self.id.to_le_bytes());
2021-01-16 11:43:16 +00:00
}
}
2021-01-23 11:44:59 +00:00
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)]
2021-01-23 16:13:37 +00:00
pub enum TradingPlatform {
Exchange,
Derivative,
Funding,
Margin,
}
2021-01-23 16:13:37 +00:00
impl TradingPlatform {
pub fn as_str(&self) -> &'static str {
match self {
TradingPlatform::Exchange => "Exchange",
TradingPlatform::Derivative => "Derivative",
TradingPlatform::Funding => "Funding",
TradingPlatform::Margin => "Margin",
2021-01-15 10:49:44 +00:00
}
}
2021-01-23 16:13:37 +00:00
}
2021-01-15 10:49:44 +00:00
2021-01-23 16:13:37 +00:00
impl Display for TradingPlatform {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
2021-01-15 10:49:44 +00:00
}
2021-01-23 16:13:37 +00:00
}
2021-01-15 10:49:44 +00:00
2021-01-23 16:13:37 +00:00
#[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 },
2021-01-23 16:13:37 +00:00
}
2021-01-24 20:53:33 +00:00
2021-01-23 16:13:37 +00:00
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",
2021-01-15 10:49:44 +00:00
}
}
2021-01-23 16:13:37 +00:00
}
2021-01-15 10:49:44 +00:00
2021-01-23 16:13:37 +00:00
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, )
2021-01-23 16:13:37 +00:00
}
OrderKind::Market => {
write!(f, "[{}]", self.as_str())
2021-01-23 16:13:37 +00:00
}
OrderKind::Stop { price } => {
write!(f, "[{} | Price: {:0.5}]", self.as_str(), price, )
2021-01-23 16:13:37 +00:00
}
OrderKind::StopLimit { stop_price, limit_price } => {
2021-01-23 16:13:37 +00:00
write!(
f,
"[{} | Stop: {:0.5}, Limit: {:0.5}]",
2021-01-23 16:13:37 +00:00
self.as_str(),
stop_price,
2021-01-23 16:13:37 +00:00
limit_price
)
}
OrderKind::TrailingStop { distance } => {
write!(f, "[{} | Distance: {:0.5}]", self.as_str(), distance, )
2021-01-23 16:13:37 +00:00
}
OrderKind::FillOrKill { price } => {
write!(f, "[{} | Price: {:0.5}]", self.as_str(), price, )
2021-01-23 16:13:37 +00:00
}
OrderKind::ImmediateOrCancel { price } => {
write!(f, "[{} | Price: {:0.5}]", self.as_str(), price, )
2021-01-15 10:49:44 +00:00
}
}
}
2021-01-23 16:13:37 +00:00
}
2021-01-15 10:49:44 +00:00
2021-01-23 16:13:37 +00:00
#[derive(Debug, Clone)]
pub struct OrderForm {
pair: SymbolPair,
kind: OrderKind,
platform: TradingPlatform,
amount: f64,
leverage: Option<f64>,
metadata: Option<OrderMetadata>,
2021-01-23 16:13:37 +00:00
}
2021-01-16 11:43:16 +00:00
2021-01-23 16:13:37 +00:00
impl OrderForm {
pub fn new(
pair: SymbolPair,
order_kind: OrderKind,
platform: TradingPlatform,
amount: f64,
) -> Self {
2021-01-23 16:13:37 +00:00
Self {
pair,
kind: order_kind,
platform,
amount,
leverage: None,
metadata: None,
2021-01-23 16:13:37 +00:00
}
2021-01-16 11:43:16 +00:00
}
2021-01-23 16:13:37 +00:00
pub fn with_leverage(mut self, leverage: Option<f64>) -> Self {
self.leverage = leverage;
self
}
pub fn with_metadata(mut self, metadata: Option<OrderMetadata>) -> Self {
self.metadata = metadata;
self
}
2021-01-16 11:43:16 +00:00
pub fn pair(&self) -> &SymbolPair {
&self.pair
}
2021-01-23 16:13:37 +00:00
pub fn kind(&self) -> OrderKind {
self.kind
2021-01-16 11:43:16 +00:00
}
2021-01-23 16:13:37 +00:00
pub fn platform(&self) -> &TradingPlatform {
&self.platform
2021-01-16 11:43:16 +00:00
}
2021-01-23 16:13:37 +00:00
pub fn amount(&self) -> f64 {
self.amount
2021-01-16 11:43:16 +00:00
}
2021-01-23 16:13:37 +00:00
pub fn price(&self) -> Option<f64> {
match self.kind {
OrderKind::Limit { price, .. } => Some(price),
OrderKind::Market { .. } => None,
OrderKind::Stop { price, .. } => Some(price),
OrderKind::StopLimit { stop_price: price, .. } => Some(price),
2021-01-23 16:13:37 +00:00
OrderKind::TrailingStop { .. } => None,
OrderKind::FillOrKill { price, .. } => Some(price),
OrderKind::ImmediateOrCancel { price, .. } => Some(price),
}
2021-01-16 11:43:16 +00:00
}
pub fn leverage(&self) -> Option<f64> {
self.leverage
}
pub fn metadata(&self) -> &Option<OrderMetadata> {
&self.metadata
}
}
#[derive(Debug)]
pub struct OrderMetadata {
position_id: Option<u64>,
strategy: Option<Box<dyn OrderStrategy>>,
}
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<u64>) -> Self {
self.position_id = position_id;
self
}
pub fn with_strategy(mut self, strategy: Option<Box<dyn OrderStrategy>>) -> Self {
self.strategy = strategy;
self
}
pub fn position_id(&self) -> Option<u64> {
self.position_id
}
pub fn cloned_strategy(&self) -> Option<Box<dyn OrderStrategy>> {
match &self.strategy {
None => { None }
Some(strategy) => {
Some(clone_box(&**strategy))
}
}
}
2021-01-15 10:49:44 +00:00
}
2021-01-14 12:42:23 +00:00
/***************
* Positions
***************/
2021-01-16 11:43:16 +00:00
#[derive(Clone, Debug)]
pub struct Position {
pair: SymbolPair,
state: PositionState,
profit_state: Option<PositionProfitState>,
amount: f64,
base_price: f64,
pl: f64,
pl_perc: f64,
price_liq: f64,
position_id: u64,
creation_date: Option<u64>,
creation_update: Option<u64>,
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<u64>) -> Self {
self.creation_date = creation_date;
self
}
pub fn with_creation_update(mut self, creation_update: Option<u64>) -> Self {
self.creation_update = creation_update;
self
}
pub fn with_profit_state(mut self, profit_state: Option<PositionProfitState>) -> Self {
self.profit_state = profit_state;
self
}
pub fn update_profit_loss(&mut self, best_offer: f64, fee_perc: f64) {
2021-01-25 13:16:45 +00:00
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;
2021-01-25 13:16:45 +00:00
(base_price, delta)
} else {
2021-01-25 13:54:25 +00:00
let base_price = self.base_price * (1.0 + fee_perc / 100.0);
2021-01-25 13:16:45 +00:00
let delta = best_offer - base_price;
(base_price, delta)
}
};
2021-01-25 13:16:45 +00:00
let profit_loss = delta * self.amount.abs();
2021-01-25 13:54:25 +00:00
let profit_loss_percentage = delta / base_price * 100.0;
2021-01-25 12:28:01 +00:00
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
}
2021-01-16 11:43:16 +00:00
pub fn id(&self) -> u64 {
self.position_id
}
pub fn profit_state(&self) -> Option<PositionProfitState> {
self.profit_state
}
pub fn creation_date(&self) -> Option<u64> {
self.creation_date
}
pub fn creation_update(&self) -> Option<u64> {
self.creation_update
}
2021-01-16 11:43:16 +00:00
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
}
2021-01-16 11:43:16 +00:00
}
impl Hash for Position {
fn hash<H: Hasher>(&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()
}
}
2021-01-24 20:53:33 +00:00
2021-01-16 11:43:16 +00:00
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,
}
2021-02-12 14:56:50 +00:00
#[derive(Debug)]
pub enum TradingFees {
Maker {
platform: TradingPlatform,
percentage: f64,
},
Taker {
platform: TradingPlatform,
percentage: f64,
},
}