implemented orderbook structs, modified algorithm to calculate best price when closing orders
This commit is contained in:
parent
945f5f63c1
commit
e6cb512a17
10
rustybot/Cargo.lock
generated
10
rustybot/Cargo.lock
generated
@ -301,6 +301,15 @@ dependencies = [
|
||||
"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]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@ -1132,6 +1141,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"dyn-clone",
|
||||
"fern",
|
||||
"float-cmp",
|
||||
"futures-util",
|
||||
"log 0.4.11",
|
||||
"regex",
|
||||
|
@ -17,4 +17,5 @@ dyn-clone = "1"
|
||||
log = "0.4"
|
||||
fern = {version = "0.6", features = ["colored"]}
|
||||
chrono = "0.4"
|
||||
byteorder = "1"
|
||||
byteorder = "1"
|
||||
float-cmp = "0.8"
|
@ -13,7 +13,7 @@ use tokio::sync::oneshot;
|
||||
use crate::connectors::{Client, ExchangeKind};
|
||||
use crate::currency::SymbolPair;
|
||||
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::BoxError;
|
||||
|
||||
@ -88,6 +88,8 @@ impl PriceManager {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
message.respond_to.send((None, None));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -200,7 +202,9 @@ impl PositionManagerHandle {
|
||||
})
|
||||
.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> {
|
||||
debug!("Updating {}", self.pair);
|
||||
// debug!("Updating {}", self.pair);
|
||||
|
||||
let opt_active_positions = self.client.active_positions(&self.pair).await?;
|
||||
let mut events = vec![];
|
||||
|
||||
self.current_tick = tick;
|
||||
|
||||
// we assume there is only ONE active position per pair
|
||||
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)),
|
||||
|
||||
Some(positions) => {
|
||||
@ -271,24 +273,25 @@ impl PositionManager {
|
||||
.next()
|
||||
{
|
||||
// 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
|
||||
Some(position) => {
|
||||
let (position_after_strategy, strategy_events, _) =
|
||||
let (position_after_strategy, strategy_events, strategy_messages) =
|
||||
self.strategy.on_new_tick(position, &self);
|
||||
|
||||
events.extend(strategy_events);
|
||||
|
||||
self.positions_history
|
||||
.insert(self.current_tick(), position_after_strategy.clone());
|
||||
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> {
|
||||
@ -374,8 +377,6 @@ pub struct OrderManager {
|
||||
}
|
||||
|
||||
impl OrderManager {
|
||||
const UNDERCUT_PERC: f64 = 0.005;
|
||||
|
||||
pub fn new(
|
||||
receiver: Receiver<ActorMessage>,
|
||||
pair: SymbolPair,
|
||||
@ -401,13 +402,15 @@ impl OrderManager {
|
||||
_ => {}
|
||||
};
|
||||
|
||||
msg.respond_to.send((None, None));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn close_position(&mut self, position: &Position) -> Result<(), BoxError> {
|
||||
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.
|
||||
// If so, the strategy method is called, otherwise we open
|
||||
@ -422,9 +425,10 @@ impl OrderManager {
|
||||
}
|
||||
None => {
|
||||
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...");
|
||||
let closing_price = self.best_closing_price(&position, ¤t_prices)?;
|
||||
let closing_price = self.best_closing_price(&position, &order_book)?;
|
||||
|
||||
info!("Submitting order...");
|
||||
// submitting order
|
||||
@ -449,23 +453,41 @@ impl OrderManager {
|
||||
}
|
||||
|
||||
pub fn update(&self) -> Result<OptionUpdate, BoxError> {
|
||||
unimplemented!()
|
||||
// TODO: implement me
|
||||
Ok((None, None))
|
||||
}
|
||||
|
||||
pub fn best_closing_price(
|
||||
&self,
|
||||
position: &Position,
|
||||
price_ticker: &TradingPairTicker,
|
||||
order_book: &OrderBook,
|
||||
) -> 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 = {
|
||||
if position.is_short() {
|
||||
price_ticker.ask
|
||||
let intermediate_price = {
|
||||
if position.is_short() {
|
||||
bid + delta
|
||||
} else {
|
||||
ask - delta
|
||||
}
|
||||
};
|
||||
|
||||
if avg > 9999.0 {
|
||||
if position.is_short() {
|
||||
intermediate_price.ceil()
|
||||
} else {
|
||||
intermediate_price.floor()
|
||||
}
|
||||
} else {
|
||||
price_ticker.bid
|
||||
intermediate_price
|
||||
}
|
||||
};
|
||||
|
||||
Ok(price * (1.0 - OrderManager::UNDERCUT_PERC))
|
||||
Ok(price)
|
||||
}
|
||||
}
|
||||
|
||||
@ -492,9 +514,29 @@ impl PairManager {
|
||||
client.clone(),
|
||||
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 {
|
||||
@ -518,36 +560,56 @@ impl ExchangeManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_managers(&mut self, tick: u64) -> Result<OptionUpdate, 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> {
|
||||
pub async fn update_managers(&mut self, tick: u64) -> Result<(), BoxError> {
|
||||
let mut futures: FuturesUnordered<_> = self
|
||||
.pair_managers
|
||||
.iter_mut()
|
||||
.map(|x| x.position_manager.update(tick))
|
||||
.map(|x| x.update_managers(tick))
|
||||
.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> {
|
||||
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))
|
||||
}
|
||||
// async fn update_position_managers(&mut self, tick: u64) -> Result<OptionUpdate, BoxError> {
|
||||
// let mut res_events = Vec::new();
|
||||
// let mut res_messages = Vec::new();
|
||||
//
|
||||
// let mut futures: FuturesUnordered<_> = self
|
||||
// .pair_managers
|
||||
// .iter_mut()
|
||||
// .map(|x| x.position_manager.update(tick))
|
||||
// .collect();
|
||||
//
|
||||
// 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))
|
||||
// }
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
use std::fmt::Display;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use chrono::{DateTime, TimeZone};
|
||||
|
||||
use crate::currency::SymbolPair;
|
||||
use crate::BoxError;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use float_cmp::ApproxEq;
|
||||
|
||||
/***************
|
||||
* Prices
|
||||
@ -28,6 +29,83 @@ pub struct PriceTicker {
|
||||
* 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)]
|
||||
pub struct ExecutedOrder {
|
||||
pub id: i64,
|
||||
@ -74,7 +152,7 @@ pub enum OrderKind {
|
||||
ExchangeIoc,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OrderForm {
|
||||
/// Order Type: LIMIT, EXCHANGE LIMIT, MARKET, EXCHANGE MARKET,
|
||||
/// STOP, EXCHANGE STOP, STOP LIMIT, EXCHANGE STOP LIMIT,
|
||||
|
Loading…
Reference in New Issue
Block a user