implemented Default for FastOrderStrategy. FastOrderStrategy closes position with a Market order if threshold is overridden

This commit is contained in:
Giulio De Pasquale 2021-01-23 11:46:39 +00:00
parent 47f17efcfb
commit a1354c2862
3 changed files with 217 additions and 58 deletions

View File

@ -4,7 +4,7 @@ use std::future::Future;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
use crate::managers::{OptionUpdate, OrderManager, PositionManager, PriceManager}; use crate::managers::{OptionUpdate, OrderManager, PositionManager, PriceManager};
use crate::models::{Position, PositionProfitState}; use crate::models::{OrderKind, Position, PositionProfitState};
use tokio::sync::oneshot; use tokio::sync::oneshot;
#[derive(Debug)] #[derive(Debug)]
@ -15,8 +15,13 @@ pub struct ActorMessage {
#[derive(Debug)] #[derive(Debug)]
pub enum Message { pub enum Message {
Update { tick: u64 }, Update {
ClosePosition { position: Position }, tick: u64,
},
ClosePosition {
position: Position,
order_kind: OrderKind,
},
OpenPosition, OpenPosition,
} }

View File

@ -353,12 +353,19 @@ impl OrderManagerHandle {
Ok(recv.await?) Ok(recv.await?)
} }
pub async fn close_position(&mut self, position: Position) -> Result<OptionUpdate, BoxError> { pub async fn close_position(
&mut self,
position: Position,
order_kind: OrderKind,
) -> Result<OptionUpdate, BoxError> {
let (send, recv) = oneshot::channel(); let (send, recv) = oneshot::channel();
self.sender self.sender
.send(ActorMessage { .send(ActorMessage {
message: Message::ClosePosition { position }, message: Message::ClosePosition {
position,
order_kind,
},
respond_to: send, respond_to: send,
}) })
.await?; .await?;
@ -398,7 +405,10 @@ impl OrderManager {
Message::Update { .. } => { Message::Update { .. } => {
self.update(); self.update();
} }
Message::ClosePosition { position, .. } => self.close_position(&position).await?, Message::ClosePosition {
position,
order_kind,
} => self.close_position(&position, &order_kind).await?,
_ => {} _ => {}
}; };
@ -407,87 +417,177 @@ impl OrderManager {
Ok(()) Ok(())
} }
pub async fn close_position(&mut self, position: &Position) -> Result<(), BoxError> { pub async fn close_position(
&mut self,
position: &Position,
order_kind: &OrderKind,
) -> 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()); debug!("Closing position #{}", position.id());
debug!("Getting current prices...");
let order_book = self.client.order_book(&self.pair).await?;
// 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
// an undercut limit order at the best current price. // an undercut limit order at the best current price.
match open_order { match open_order {
Some(open_order) => { Some(open_order) => {
info!("There is an open order. Calling strategy."); debug!("There is an open order. Calling strategy.");
let (_, messages) =
self.strategy
.on_position_close(open_order, position, &order_book)?;
self.tracked_positions = self if let Some(messages) = messages {
.strategy for m in messages {
.on_position_close(open_order, &self.tracked_positions); match m {
Message::ClosePosition {
position,
order_kind,
} => {
// TODO FIXME
match order_kind {
OrderKind::Market => {
self.submit_market_order(1.0, position.amount().neg())
.await?;
}
_ => {}
}
}
_ => {
debug!("Received unsupported message from order strategy. Unimplemented.")
}
}
}
}
} }
None => { None => {
info!("Getting current prices..."); let closing_price = self.best_closing_price(&position, &order_book);
let order_book = self.client.order_book(&self.pair).await?;
info!("Calculating best closing price..."); let active_order = match order_kind {
let closing_price = self.best_closing_price(&position, &order_book)?; OrderKind::Limit => {
self.submit_limit_order(closing_price, position.amount().neg())
info!("Submitting order..."); .await?
// submitting order }
let order_form = OrderForm::new( OrderKind::ExchangeLimit => {
&self.pair, self.submit_exchange_limit_order(closing_price, position.amount().neg())
closing_price, .await?
position.amount().neg(), }
OrderKind::Limit, OrderKind::Market => {
); self.submit_market_order(closing_price, position.amount().neg())
.await?
match self.client.submit_order(order_form).await { }
Err(e) => error!("Could not submit order: {}", e), OrderKind::ExchangeMarket => {
Ok(o) => { self.submit_exchange_market_order(closing_price, position.amount().neg())
self.tracked_positions.insert(position.id(), o); .await?
info!("Done!"); }
_ => {
unimplemented!()
} }
}; };
self.tracked_positions.insert(position.id(), active_order);
} }
} }
Ok(()) Ok(())
} }
pub async fn submit_limit_order(
&mut self,
closing_price: f64,
amount: f64,
) -> Result<ActiveOrder, BoxError> {
info!(
"Submitting exchange limit order of {} {}...",
amount,
self.pair.base()
);
let order_form = OrderForm::new(&self.pair, closing_price, amount, OrderKind::Limit);
Ok(self.client.submit_order(order_form).await?)
}
pub async fn submit_exchange_limit_order(
&mut self,
closing_price: f64,
amount: f64,
) -> Result<ActiveOrder, BoxError> {
info!(
"Submitting exchange limit order of {} {}...",
amount,
self.pair.base()
);
let order_form =
OrderForm::new(&self.pair, closing_price, amount, OrderKind::ExchangeLimit);
Ok(self.client.submit_order(order_form).await?)
}
pub async fn submit_market_order(
&mut self,
closing_price: f64,
amount: f64,
) -> Result<ActiveOrder, BoxError> {
info!(
"Submitting market order of {} {}...",
amount,
self.pair.base()
);
let order_form = OrderForm::new(&self.pair, closing_price, amount, OrderKind::Market);
Ok(self.client.submit_order(order_form).await?)
}
pub async fn submit_exchange_market_order(
&mut self,
closing_price: f64,
amount: f64,
) -> Result<ActiveOrder, BoxError> {
info!(
"Submitting market order of {} {}...",
amount,
self.pair.base()
);
let order_form =
OrderForm::new(&self.pair, closing_price, amount, OrderKind::ExchangeMarket);
Ok(self.client.submit_order(order_form).await?)
}
pub fn update(&self) -> Result<OptionUpdate, BoxError> { pub fn update(&self) -> Result<OptionUpdate, BoxError> {
// TODO: implement me // TODO: implement me
Ok((None, None)) Ok((None, None))
} }
pub fn best_closing_price( pub fn best_closing_price(&self, position: &Position, order_book: &OrderBook) -> f64 {
&self,
position: &Position,
order_book: &OrderBook,
) -> Result<f64, BoxError> {
let ask = order_book.lowest_ask(); let ask = order_book.lowest_ask();
let bid = order_book.highest_bid(); let bid = order_book.highest_bid();
let avg = (bid + ask) / 2.0; let avg = (bid + ask) / 2.0;
let delta = (ask - bid) / 10.0; let delta = (ask - bid) / 10.0;
let price = { let closing_price = {
let intermediate_price = { let closing_price = {
if position.is_short() { if position.is_short() {
bid + delta bid - delta
} else { } else {
ask - delta ask + delta
} }
}; };
if avg > 9999.0 { if avg > 9999.0 {
if position.is_short() { if position.is_short() {
intermediate_price.ceil() closing_price.ceil()
} else { } else {
intermediate_price.floor() closing_price.floor()
} }
} else { } else {
intermediate_price closing_price
} }
}; };
Ok(price) closing_price
} }
} }
@ -496,7 +596,6 @@ pub struct PairManager {
price_manager: PriceManagerHandle, price_manager: PriceManagerHandle,
order_manager: OrderManagerHandle, order_manager: OrderManagerHandle,
position_manager: PositionManagerHandle, position_manager: PositionManagerHandle,
// dispatcher: Dispatcher,
} }
impl PairManager { impl PairManager {
@ -507,7 +606,7 @@ impl PairManager {
order_manager: OrderManagerHandle::new( order_manager: OrderManagerHandle::new(
pair.clone(), pair.clone(),
client.clone(), client.clone(),
Box::new(FastOrderStrategy {}), Box::new(FastOrderStrategy::default()),
), ),
position_manager: PositionManagerHandle::new( position_manager: PositionManagerHandle::new(
pair.clone(), pair.clone(),
@ -527,8 +626,13 @@ impl PairManager {
if let Some(messages) = opt_pos_messages { if let Some(messages) = opt_pos_messages {
for m in messages { for m in messages {
match m { match m {
Message::ClosePosition { position } => { Message::ClosePosition {
self.order_manager.close_position(position).await?; position,
order_kind,
} => {
self.order_manager
.close_position(position, order_kind)
.await?;
} }
_ => {} _ => {}
} }

View File

@ -1,12 +1,16 @@
use dyn_clone::DynClone;
use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use dyn_clone::DynClone;
use log::{debug, info};
use tokio::sync::oneshot;
use crate::events::{Event, EventKind, EventMetadata, Message}; use crate::events::{Event, EventKind, EventMetadata, Message};
use crate::managers::{OrderManager, PositionManager, TrackedPositionsMap}; use crate::managers::{OrderManager, PositionManager, TrackedPositionsMap};
use crate::models::{ActiveOrder, OrderForm, Position, PositionProfitState}; use crate::models::{
use tokio::sync::oneshot; ActiveOrder, OrderBook, OrderBookEntry, OrderForm, OrderKind, Position, PositionProfitState,
};
use crate::BoxError;
/*************** /***************
* DEFINITIONS * DEFINITIONS
@ -38,8 +42,9 @@ pub trait OrderStrategy: DynClone + Send {
fn on_position_close( fn on_position_close(
&self, &self,
order: &ActiveOrder, order: &ActiveOrder,
tracked_positions: &HashMap<u64, ActiveOrder>, open_position: &Position,
) -> TrackedPositionsMap; order_book: &OrderBook,
) -> Result<(Option<Vec<Event>>, Option<Vec<Message>>), BoxError>;
} }
impl Debug for dyn OrderStrategy { impl Debug for dyn OrderStrategy {
@ -106,6 +111,7 @@ impl PositionStrategy for TrailingStop {
debug!("Inserting close position message..."); debug!("Inserting close position message...");
messages.push(Message::ClosePosition { messages.push(Message::ClosePosition {
position: position.clone(), position: position.clone(),
order_kind: OrderKind::Limit,
}); });
PositionProfitState::Critical PositionProfitState::Critical
} }
@ -181,7 +187,23 @@ impl PositionStrategy for TrailingStop {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FastOrderStrategy {} pub struct FastOrderStrategy {
// threshold (%) for which we trigger a market order
// to close an open position
threshold: f64,
}
impl Default for FastOrderStrategy {
fn default() -> Self {
Self { threshold: 0.2 }
}
}
impl FastOrderStrategy {
pub fn new(threshold: f64) -> Self {
Self { threshold }
}
}
impl OrderStrategy for FastOrderStrategy { impl OrderStrategy for FastOrderStrategy {
fn name(&self) -> String { fn name(&self) -> String {
@ -195,8 +217,36 @@ impl OrderStrategy for FastOrderStrategy {
fn on_position_close( fn on_position_close(
&self, &self,
order: &ActiveOrder, order: &ActiveOrder,
tracked_positions: &HashMap<u64, ActiveOrder>, active_position: &Position,
) -> TrackedPositionsMap { order_book: &OrderBook,
unimplemented!() ) -> Result<(Option<Vec<Event>>, Option<Vec<Message>>), BoxError> {
let mut messages = vec![];
// long
let offer_comparison = {
if order.amount > 0.0 {
order_book.highest_bid()
} else {
order_book.lowest_ask()
}
};
// if the best offer is higher than our threshold,
// ask the manager to close the position with a market order
let delta = (1.0 - (offer_comparison / order.price)) * 100.0;
debug!(
"Offer comp: {} | Our offer: {} | Current delta: {}",
offer_comparison, order.price, delta
);
if delta > self.threshold {
messages.push(Message::ClosePosition {
position: active_position.clone(),
order_kind: OrderKind::Market,
})
}
Ok((None, (!messages.is_empty()).then_some(messages)))
} }
} }