2021-01-16 11:43:16 +00:00
|
|
|
use std::fmt::Display;
|
|
|
|
|
|
|
|
use chrono::{DateTime, TimeZone};
|
|
|
|
|
2021-01-13 08:57:46 +00:00
|
|
|
use crate::currency::SymbolPair;
|
2021-01-15 10:49:44 +00:00
|
|
|
use crate::BoxError;
|
2021-01-16 11:43:16 +00:00
|
|
|
use std::hash::{Hash, Hasher};
|
2021-01-13 08:57:46 +00:00
|
|
|
|
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(Clone, Debug)]
|
2021-01-16 11:43:16 +00:00
|
|
|
pub struct ExecutedOrder {
|
2021-01-13 08:57:46 +00:00
|
|
|
pub id: i64,
|
|
|
|
pub group_id: Option<i32>,
|
|
|
|
pub client_id: i64,
|
|
|
|
pub symbol: String,
|
|
|
|
pub creation_timestamp: i64,
|
|
|
|
pub update_timestamp: i64,
|
|
|
|
pub amount: f64,
|
|
|
|
pub amount_original: f64,
|
|
|
|
pub order_type: String,
|
|
|
|
pub previous_order_type: Option<String>,
|
|
|
|
pub flags: Option<i32>,
|
|
|
|
pub order_status: Option<String>,
|
|
|
|
pub price: f64,
|
|
|
|
pub price_avg: f64,
|
|
|
|
pub price_trailing: Option<f64>,
|
|
|
|
pub price_aux_limit: Option<f64>,
|
|
|
|
pub notify: i32,
|
|
|
|
pub hidden: i32,
|
|
|
|
pub placed_id: Option<i32>,
|
|
|
|
}
|
|
|
|
|
2021-01-16 11:43:16 +00:00
|
|
|
impl Hash for ExecutedOrder {
|
|
|
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
|
|
state.write(&self.id.to_le_bytes())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, Hash)]
|
2021-01-13 08:57:46 +00:00
|
|
|
pub enum OrderKind {
|
|
|
|
Limit,
|
|
|
|
ExchangeLimit,
|
|
|
|
Market,
|
|
|
|
ExchangeMarket,
|
|
|
|
Stop,
|
|
|
|
ExchangeStop,
|
|
|
|
StopLimit,
|
|
|
|
ExchangeStopLimit,
|
|
|
|
TrailingStop,
|
|
|
|
Fok,
|
|
|
|
ExchangeFok,
|
|
|
|
Ioc,
|
|
|
|
ExchangeIoc,
|
|
|
|
}
|
|
|
|
|
2021-01-15 10:51:02 +00:00
|
|
|
#[derive(Clone)]
|
2021-01-15 10:49:44 +00:00
|
|
|
pub struct OrderForm {
|
|
|
|
/// Order Type: LIMIT, EXCHANGE LIMIT, MARKET, EXCHANGE MARKET,
|
|
|
|
/// STOP, EXCHANGE STOP, STOP LIMIT, EXCHANGE STOP LIMIT,
|
|
|
|
/// TRAILING STOP, EXCHANGE TRAILING STOP, FOK,
|
|
|
|
/// EXCHANGE FOK, IOC, EXCHANGE IOC
|
|
|
|
kind: OrderKind,
|
|
|
|
/// Symbol for desired pair
|
2021-01-16 11:43:16 +00:00
|
|
|
pair: SymbolPair,
|
2021-01-15 10:49:44 +00:00
|
|
|
/// Price of order
|
2021-01-16 11:43:16 +00:00
|
|
|
price: f64,
|
2021-01-15 10:49:44 +00:00
|
|
|
/// Amount of order (positive for buy, negative for sell)
|
2021-01-16 11:43:16 +00:00
|
|
|
amount: f64,
|
2021-01-15 10:49:44 +00:00
|
|
|
/// Set the leverage for a derivative order, supported by derivative symbol orders only.
|
|
|
|
/// The value should be between 1 and 100 inclusive.
|
|
|
|
/// The field is optional, if omitted the default leverage value of 10 will be used.
|
|
|
|
leverage: Option<u32>,
|
|
|
|
/// The trailing price for a trailing stop order
|
|
|
|
price_trailing: Option<String>,
|
|
|
|
/// Auxiliary Limit price (for STOP LIMIT)
|
|
|
|
price_aux_limit: Option<String>,
|
|
|
|
/// Time-In-Force: datetime for automatic order cancellation (ie. 2020-01-01 10:45:23) )
|
|
|
|
tif: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl OrderForm {
|
2021-01-16 11:43:16 +00:00
|
|
|
pub fn new(pair: &SymbolPair, price: f64, amount: f64, kind: OrderKind) -> Self {
|
2021-01-15 10:49:44 +00:00
|
|
|
OrderForm {
|
|
|
|
kind,
|
2021-01-16 11:43:16 +00:00
|
|
|
pair: pair.clone(),
|
|
|
|
price,
|
|
|
|
amount,
|
2021-01-15 10:49:44 +00:00
|
|
|
leverage: None,
|
|
|
|
price_trailing: None,
|
|
|
|
price_aux_limit: None,
|
|
|
|
tif: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_leverage(mut self, leverage: u32) -> Self {
|
|
|
|
self.leverage = Some(leverage);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_price_trailing(mut self, trailing: f64) -> Result<Self, BoxError> {
|
|
|
|
match self.kind {
|
|
|
|
OrderKind::TrailingStop => {
|
|
|
|
self.price_trailing = Some(trailing.to_string());
|
|
|
|
Ok(self)
|
|
|
|
}
|
|
|
|
_ => Err("Invalid order type.".into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-16 11:43:16 +00:00
|
|
|
pub fn with_price_aux_limit(mut self, limit: f64) -> Result<Self, BoxError> {
|
2021-01-15 10:49:44 +00:00
|
|
|
match self.kind {
|
|
|
|
OrderKind::StopLimit | OrderKind::ExchangeStopLimit => {
|
|
|
|
self.price_aux_limit = Some(limit.to_string());
|
|
|
|
Ok(self)
|
|
|
|
}
|
|
|
|
_ => Err("Invalid order type.".into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_tif<T: TimeZone>(mut self, tif: DateTime<T>) -> Self
|
|
|
|
where
|
|
|
|
T::Offset: Display,
|
|
|
|
{
|
|
|
|
self.tif = Some(tif.format("%Y-%m-%d %H:%M:%S").to_string());
|
|
|
|
self
|
|
|
|
}
|
2021-01-16 11:43:16 +00:00
|
|
|
|
|
|
|
pub fn kind(&self) -> OrderKind {
|
|
|
|
self.kind
|
|
|
|
}
|
|
|
|
pub fn pair(&self) -> &SymbolPair {
|
|
|
|
&self.pair
|
|
|
|
}
|
|
|
|
pub fn price(&self) -> &f64 {
|
|
|
|
&self.price
|
|
|
|
}
|
|
|
|
pub fn amount(&self) -> &f64 {
|
|
|
|
&self.amount
|
|
|
|
}
|
|
|
|
pub fn leverage(&self) -> Option<u32> {
|
|
|
|
self.leverage
|
|
|
|
}
|
|
|
|
pub fn price_trailing(&self) -> &Option<String> {
|
|
|
|
&self.price_trailing
|
|
|
|
}
|
|
|
|
pub fn price_aux_limit(&self) -> &Option<String> {
|
|
|
|
&self.price_aux_limit
|
|
|
|
}
|
|
|
|
pub fn tif(&self) -> &Option<String> {
|
|
|
|
&self.tif
|
|
|
|
}
|
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)]
|
2021-01-13 08:57:46 +00:00
|
|
|
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>,
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
) -> Self {
|
|
|
|
Position {
|
|
|
|
pair,
|
|
|
|
state,
|
|
|
|
amount,
|
|
|
|
base_price,
|
|
|
|
pl,
|
|
|
|
pl_perc,
|
|
|
|
price_liq,
|
|
|
|
position_id,
|
|
|
|
creation_date: None,
|
|
|
|
creation_update: None,
|
|
|
|
profit_state: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 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 {
|
2021-01-13 08:57:46 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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-13 08:57:46 +00:00
|
|
|
}
|
2021-01-16 11:43:16 +00:00
|
|
|
impl Eq for Position {}
|
2021-01-13 08:57:46 +00:00
|
|
|
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
|
|
|
pub enum PositionProfitState {
|
|
|
|
Critical,
|
|
|
|
Loss,
|
|
|
|
BreakEven,
|
|
|
|
MinimumProfit,
|
|
|
|
Profit,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PositionProfitState {
|
|
|
|
fn color(self) -> String {
|
|
|
|
match self {
|
|
|
|
PositionProfitState::Critical | PositionProfitState::Loss => "red",
|
|
|
|
PositionProfitState::BreakEven => "yellow",
|
|
|
|
PositionProfitState::MinimumProfit | PositionProfitState::Profit => "green",
|
|
|
|
}
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
|
|
|
pub enum PositionState {
|
|
|
|
Closed,
|
|
|
|
Open,
|
|
|
|
}
|