implemented orderbook structs, modified algorithm to calculate best price when closing orders

This commit is contained in:
Giulio De Pasquale 2021-01-19 21:30:01 +00:00
parent 945f5f63c1
commit e6cb512a17
4 changed files with 202 additions and 51 deletions

10
rustybot/Cargo.lock generated
View File

@ -301,6 +301,15 @@ dependencies = [
"log 0.4.11", "log 0.4.11",
] ]
[[package]]
name = "float-cmp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -1132,6 +1141,7 @@ dependencies = [
"chrono", "chrono",
"dyn-clone", "dyn-clone",
"fern", "fern",
"float-cmp",
"futures-util", "futures-util",
"log 0.4.11", "log 0.4.11",
"regex", "regex",

View File

@ -18,3 +18,4 @@ log = "0.4"
fern = {version = "0.6", features = ["colored"]} fern = {version = "0.6", features = ["colored"]}
chrono = "0.4" chrono = "0.4"
byteorder = "1" byteorder = "1"
float-cmp = "0.8"

View File

@ -13,7 +13,7 @@ use tokio::sync::oneshot;
use crate::connectors::{Client, ExchangeKind}; use crate::connectors::{Client, ExchangeKind};
use crate::currency::SymbolPair; use crate::currency::SymbolPair;
use crate::events::{ActorMessage, Event, Message}; use crate::events::{ActorMessage, Event, Message};
use crate::models::{ExecutedOrder, OrderForm, OrderKind, Position, PriceTicker}; use crate::models::{ExecutedOrder, OrderBook, OrderForm, OrderKind, Position, PriceTicker};
use crate::strategy::{FastOrderStrategy, OrderStrategy, PositionStrategy, TrailingStop}; use crate::strategy::{FastOrderStrategy, OrderStrategy, PositionStrategy, TrailingStop};
use crate::BoxError; use crate::BoxError;
@ -88,6 +88,8 @@ impl PriceManager {
_ => {} _ => {}
} }
message.respond_to.send((None, None));
Ok(()) Ok(())
} }
@ -200,7 +202,9 @@ impl PositionManagerHandle {
}) })
.await?; .await?;
Ok(recv.await?) let response = recv.await?;
Ok(response)
} }
} }
@ -251,16 +255,14 @@ impl PositionManager {
} }
pub async fn update(&mut self, tick: u64) -> Result<OptionUpdate, BoxError> { pub async fn update(&mut self, tick: u64) -> Result<OptionUpdate, BoxError> {
debug!("Updating {}", self.pair); // debug!("Updating {}", self.pair);
let opt_active_positions = self.client.active_positions(&self.pair).await?; let opt_active_positions = self.client.active_positions(&self.pair).await?;
let mut events = vec![];
self.current_tick = tick; self.current_tick = tick;
// we assume there is only ONE active position per pair // we assume there is only ONE active position per pair
match opt_active_positions { match opt_active_positions {
// no open positions, no events and no order forms returned // no open positions, no events and no messages returned
None => return Ok((None, None)), None => return Ok((None, None)),
Some(positions) => { Some(positions) => {
@ -271,24 +273,25 @@ impl PositionManager {
.next() .next()
{ {
// no open positions for our pair, setting active position to none // no open positions for our pair, setting active position to none
None => self.active_position = None, None => {
self.active_position = None;
return Ok((None, None));
}
// applying strategy to open position and saving into struct // applying strategy to open position and saving into struct
Some(position) => { Some(position) => {
let (position_after_strategy, strategy_events, _) = let (position_after_strategy, strategy_events, strategy_messages) =
self.strategy.on_new_tick(position, &self); self.strategy.on_new_tick(position, &self);
events.extend(strategy_events);
self.positions_history self.positions_history
.insert(self.current_tick(), position_after_strategy.clone()); .insert(self.current_tick(), position_after_strategy.clone());
self.active_position = Some(position_after_strategy); self.active_position = Some(position_after_strategy);
return Ok((strategy_events, strategy_messages));
} }
} }
} }
}; };
Ok((None, None))
} }
pub fn position_previous_tick(&self, id: u64, tick: Option<u64>) -> Option<&Position> { pub fn position_previous_tick(&self, id: u64, tick: Option<u64>) -> Option<&Position> {
@ -374,8 +377,6 @@ pub struct OrderManager {
} }
impl OrderManager { impl OrderManager {
const UNDERCUT_PERC: f64 = 0.005;
pub fn new( pub fn new(
receiver: Receiver<ActorMessage>, receiver: Receiver<ActorMessage>,
pair: SymbolPair, pair: SymbolPair,
@ -401,13 +402,15 @@ impl OrderManager {
_ => {} _ => {}
}; };
msg.respond_to.send((None, None));
Ok(()) Ok(())
} }
pub async fn close_position(&mut self, position: &Position) -> Result<(), BoxError> { pub async fn close_position(&mut self, position: &Position) -> Result<(), BoxError> {
let open_order = self.tracked_positions.get(&position.id()); let open_order = self.tracked_positions.get(&position.id());
info!("Closing position {}", position.id()); info!("Closing position #{}", position.id());
// checking if the position has an open order. // checking if the position has an open order.
// If so, the strategy method is called, otherwise we open // If so, the strategy method is called, otherwise we open
@ -422,9 +425,10 @@ impl OrderManager {
} }
None => { None => {
info!("Getting current prices..."); info!("Getting current prices...");
let current_prices = self.client.current_prices(&self.pair).await?; let order_book = self.client.order_book(&self.pair).await?;
info!("Calculating best closing price..."); info!("Calculating best closing price...");
let closing_price = self.best_closing_price(&position, &current_prices)?; let closing_price = self.best_closing_price(&position, &order_book)?;
info!("Submitting order..."); info!("Submitting order...");
// submitting order // submitting order
@ -449,23 +453,41 @@ impl OrderManager {
} }
pub fn update(&self) -> Result<OptionUpdate, BoxError> { pub fn update(&self) -> Result<OptionUpdate, BoxError> {
unimplemented!() // TODO: implement me
Ok((None, None))
} }
pub fn best_closing_price( pub fn best_closing_price(
&self, &self,
position: &Position, position: &Position,
price_ticker: &TradingPairTicker, order_book: &OrderBook,
) -> Result<f64, BoxError> { ) -> Result<f64, BoxError> {
let ask = order_book.lowest_ask();
let bid = order_book.highest_bid();
let avg = (bid + ask) / 2.0;
let delta = (ask - bid) / 10.0;
let price = { let price = {
let intermediate_price = {
if position.is_short() { if position.is_short() {
price_ticker.ask bid + delta
} else { } else {
price_ticker.bid ask - delta
} }
}; };
Ok(price * (1.0 - OrderManager::UNDERCUT_PERC)) if avg > 9999.0 {
if position.is_short() {
intermediate_price.ceil()
} else {
intermediate_price.floor()
}
} else {
intermediate_price
}
};
Ok(price)
} }
} }
@ -492,9 +514,29 @@ impl PairManager {
client.clone(), client.clone(),
Box::new(TrailingStop::new()), Box::new(TrailingStop::new()),
), ),
// dispatcher: Dispatcher::new(),
} }
} }
pub async fn update_managers(&mut self, tick: u64) -> Result<(), BoxError> {
let (opt_price_events, opt_price_messages) = self.price_manager.update(tick).await?;
let (opt_pos_events, opt_pos_messages) = self.position_manager.update(tick).await?;
let (opt_order_events, opt_order_messages) = self.order_manager.update(tick).await?;
// TODO: to move into Handler?
if let Some(messages) = opt_pos_messages {
for m in messages {
match m {
Message::ClosePosition { position } => {
self.order_manager.close_position(position).await?;
}
_ => {}
}
}
}
Ok(())
}
} }
pub struct ExchangeManager { pub struct ExchangeManager {
@ -518,36 +560,56 @@ impl ExchangeManager {
} }
} }
pub async fn update_managers(&mut self, tick: u64) -> Result<OptionUpdate, BoxError> { pub async fn update_managers(&mut self, tick: u64) -> Result<(), BoxError> {
let (price_opt_events, price_opt_signals) = self.update_price_managers(tick).await?;
let (pos_opt_events, pos_opt_signals) = self.update_position_managers(tick).await?;
debug!("{:?}", pos_opt_signals);
Ok((pos_opt_events, price_opt_signals))
}
async fn update_position_managers(&mut self, tick: u64) -> Result<OptionUpdate, BoxError> {
let mut futures: FuturesUnordered<_> = self let mut futures: FuturesUnordered<_> = self
.pair_managers .pair_managers
.iter_mut() .iter_mut()
.map(|x| x.position_manager.update(tick)) .map(|x| x.update_managers(tick))
.collect(); .collect();
while let Some(x) = futures.next().await {} // execute the futures
while let Some(_) = futures.next().await {}
Ok((None, None)) Ok(())
} }
async fn update_price_managers(&mut self, tick: u64) -> Result<OptionUpdate, BoxError> { // async fn update_position_managers(&mut self, tick: u64) -> Result<OptionUpdate, BoxError> {
let mut futures: FuturesUnordered<_> = self // let mut res_events = Vec::new();
.pair_managers // let mut res_messages = Vec::new();
.iter_mut() //
.map(|x| x.price_manager.update(tick)) // let mut futures: FuturesUnordered<_> = self
.collect(); // .pair_managers
// .iter_mut()
while let Some(x) = futures.next().await {} // .map(|x| x.position_manager.update(tick))
// .collect();
Ok((None, None)) //
} // while let Some(future_result) = futures.next().await {
// let (opt_events, opt_messages) = future_result?;
//
// if let Some(events) = opt_events {
// res_events.extend(events);
// }
//
// if let Some(messages) = opt_messages {
// res_messages.extend(messages);
// }
// }
//
// Ok((
// (!res_events.is_empty()).then_some(res_events),
// (!res_messages.is_empty()).then_some(res_messages),
// ))
// }
//
// async fn update_price_managers(&mut self, tick: u64) -> Result<OptionUpdate, BoxError> {
// let mut futures: FuturesUnordered<_> = self
// .pair_managers
// .iter_mut()
// .map(|x| x.price_manager.update(tick))
// .collect();
//
// while let Some(x) = futures.next().await {}
//
// Ok((None, None))
// }
} }

View File

@ -1,10 +1,11 @@
use std::fmt::Display; use std::fmt::Display;
use std::hash::{Hash, Hasher};
use chrono::{DateTime, TimeZone}; use chrono::{DateTime, TimeZone};
use crate::currency::SymbolPair; use crate::currency::SymbolPair;
use crate::BoxError; use crate::BoxError;
use std::hash::{Hash, Hasher}; use float_cmp::ApproxEq;
/*************** /***************
* Prices * Prices
@ -28,6 +29,83 @@ pub struct PriceTicker {
* Orders * 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(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ExecutedOrder { pub struct ExecutedOrder {
pub id: i64, pub id: i64,
@ -74,7 +152,7 @@ pub enum OrderKind {
ExchangeIoc, ExchangeIoc,
} }
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct OrderForm { pub struct OrderForm {
/// Order Type: LIMIT, EXCHANGE LIMIT, MARKET, EXCHANGE MARKET, /// Order Type: LIMIT, EXCHANGE LIMIT, MARKET, EXCHANGE MARKET,
/// STOP, EXCHANGE STOP, STOP LIMIT, EXCHANGE STOP LIMIT, /// STOP, EXCHANGE STOP, STOP LIMIT, EXCHANGE STOP LIMIT,