From 9b92d38318c80961ca4079ec035b99978d60ca9a Mon Sep 17 00:00:00 2001 From: Giulio De Pasquale Date: Sun, 24 Jan 2021 13:41:18 +0000 Subject: [PATCH] retrieving updated information on open orders before calling strategy --- rustybot/src/connectors.rs | 89 ++++++++++++++++++++++++++++++++++++-- rustybot/src/managers.rs | 81 +++++++++++++++++++++------------- 2 files changed, 135 insertions(+), 35 deletions(-) diff --git a/rustybot/src/connectors.rs b/rustybot/src/connectors.rs index 06b4756..c71cc6a 100644 --- a/rustybot/src/connectors.rs +++ b/rustybot/src/connectors.rs @@ -155,8 +155,10 @@ impl Connector for BitfinexConnector { Ok(ticker) } - async fn active_orders(&self, pair: &SymbolPair) -> Result, BoxError> { - unimplemented!() + async fn active_orders(&self, _: &SymbolPair) -> Result, BoxError> { + let response = self.bfx.orders.active_orders().await?; + + Ok(response.iter().map(Into::into).collect()) } async fn submit_order(&self, order: &OrderForm) -> Result { @@ -321,6 +323,21 @@ impl From<&bitfinex::orders::OrderResponse> for TradingPlatform { } } +impl From<&bitfinex::orders::ActiveOrder> for TradingPlatform { + fn from(response: &bitfinex::orders::ActiveOrder) -> Self { + match response.order_type() { + bitfinex::orders::OrderKind::Limit + | bitfinex::orders::OrderKind::Market + | bitfinex::orders::OrderKind::StopLimit + | bitfinex::orders::OrderKind::Stop + | bitfinex::orders::OrderKind::TrailingStop + | bitfinex::orders::OrderKind::Fok + | bitfinex::orders::OrderKind::Ioc => Self::Margin, + _ => Self::Exchange, + } + } +} + impl From<&bitfinex::orders::OrderResponse> for OrderKind { fn from(response: &OrderResponse) -> Self { match response.order_type() { @@ -345,11 +362,11 @@ impl From<&bitfinex::orders::OrderResponse> for OrderKind { | bitfinex::orders::OrderKind::ExchangeStopLimit => Self::StopLimit { price: response.price(), amount: response.amount(), - limit_price: response.price_aux_limit(), + limit_price: response.price_aux_limit().expect("Limit price not found!"), }, bitfinex::orders::OrderKind::TrailingStop | bitfinex::orders::OrderKind::ExchangeTrailingStop => Self::TrailingStop { - distance: response.price_trailing(), + distance: response.price_trailing().expect("Distance not found!"), amount: response.amount(), }, bitfinex::orders::OrderKind::Fok | bitfinex::orders::OrderKind::ExchangeFok => { @@ -368,6 +385,70 @@ impl From<&bitfinex::orders::OrderResponse> for OrderKind { } } +impl From<&bitfinex::orders::ActiveOrder> for OrderKind { + fn from(response: &bitfinex::orders::ActiveOrder) -> Self { + match response.order_type() { + bitfinex::orders::OrderKind::Limit | bitfinex::orders::OrderKind::ExchangeLimit => { + Self::Limit { + price: response.price(), + amount: response.amount(), + } + } + bitfinex::orders::OrderKind::Market | bitfinex::orders::OrderKind::ExchangeMarket => { + Self::Market { + amount: response.amount(), + } + } + bitfinex::orders::OrderKind::Stop | bitfinex::orders::OrderKind::ExchangeStop => { + Self::Stop { + price: response.price(), + amount: response.amount(), + } + } + bitfinex::orders::OrderKind::StopLimit + | bitfinex::orders::OrderKind::ExchangeStopLimit => Self::StopLimit { + price: response.price(), + amount: response.amount(), + limit_price: response.price_aux_limit().expect("Limit price not found!"), + }, + bitfinex::orders::OrderKind::TrailingStop + | bitfinex::orders::OrderKind::ExchangeTrailingStop => Self::TrailingStop { + distance: response.price_trailing().expect("Distance not found!"), + amount: response.amount(), + }, + bitfinex::orders::OrderKind::Fok | bitfinex::orders::OrderKind::ExchangeFok => { + Self::FillOrKill { + price: response.price(), + amount: response.amount(), + } + } + bitfinex::orders::OrderKind::Ioc | bitfinex::orders::OrderKind::ExchangeIoc => { + Self::ImmediateOrCancel { + price: response.price(), + amount: response.amount(), + } + } + } + } +} + +impl From<&bitfinex::orders::ActiveOrder> for ActiveOrder { + fn from(order: &bitfinex::orders::ActiveOrder) -> Self { + let pair = SymbolPair::from_str(&order.symbol()).expect("Invalid symbol!"); + + Self { + exchange: Exchange::Bitfinex, + id: order.id(), + group_id: order.group_id().map(|x| x as u64), + client_id: Some(order.client_id()), + symbol: pair.clone(), + current_form: OrderForm::new(pair, order.into(), order.into()), + creation_timestamp: order.creation_timestamp(), + update_timestamp: order.update_timestamp(), + } + } +} + impl From for PriceTicker { fn from(t: TradingPairTicker) -> Self { Self { diff --git a/rustybot/src/managers.rs b/rustybot/src/managers.rs index 20e0bc3..912e890 100644 --- a/rustybot/src/managers.rs +++ b/rustybot/src/managers.rs @@ -319,7 +319,8 @@ impl PositionManager { * ORDERS ******************/ -pub type TrackedPositionsMap = HashMap; +// Position ID: Order ID +pub type TrackedPositionsMap = HashMap; pub struct OrderManagerHandle { sender: Sender, @@ -405,7 +406,7 @@ impl OrderManager { pub async fn handle_message(&mut self, msg: ActorMessage) -> Result<(), BoxError> { match msg.message { Message::Update { .. } => { - self.update(); + self.update().await?; } Message::ClosePosition { position, @@ -424,39 +425,23 @@ impl OrderManager { position: &Position, order_form: &OrderForm, ) -> Result<(), BoxError> { - let open_order = self.tracked_positions.get(&position.id()); + info!("Closing position #{}", position.id()); + + debug!("Retrieving open orders..."); + let open_orders = self.client.active_orders(&self.pair).await?; + + let opt_position_order = open_orders + .iter() + .find(|x| x.current_form.amount().neg() == position.amount()); - 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. // If so, the strategy method is called, otherwise we open // an undercut limit order at the best current price. - match open_order { - Some(open_order) => { - debug!("There is an open order. Calling strategy."); - let (_, messages) = - self.strategy - .on_position_close(open_order, position, &order_book)?; - - if let Some(messages) = messages { - for m in messages { - match m { - Message::ClosePosition { order_form, .. } => { - info!("Closing open order with a {} order", order_form.kind()); - if let Ok(_) = self.client.submit_order(&order_form).await { - info!("Cancelling open order #{}", open_order.id); - self.client.cancel_order(open_order).await?; - } - } - _ => { - debug!("Received unsupported message from order strategy. Unimplemented.") - } - } - } - } - } + match opt_position_order { + // No open order, undercutting best price with limit order None => { let closing_price = self.best_closing_price(&position, &order_book); @@ -471,16 +456,50 @@ impl OrderManager { ); info!("Submitting {} order", order_form.kind()); - let active_order = self.client.submit_order(&order_form).await?; + if let Err(e) = self.client.submit_order(&order_form).await { + error!( + "Could not submit {} to close position #{}: {}", + order_form.kind(), + position.id(), + e + ); + return Err(e); + } + } + Some(active_order) => { + debug!( + "Found open order, calling \"{}\" strategy.", + self.strategy.name() + ); - self.tracked_positions.insert(position.id(), active_order); + let (_, strat_messages) = + self.strategy + .on_position_close(active_order, position, &order_book)?; + + if let Some(messages) = strat_messages { + for m in messages { + match m { + Message::ClosePosition { order_form, .. } => { + info!("Closing open order with a {} order", order_form.kind()); + + if let Ok(_) = self.client.submit_order(&order_form).await { + info!("Cancelling open order #{}", active_order.id); + self.client.cancel_order(active_order).await?; + } + } + _ => { + debug!("Received unsupported message from order strategy. Unimplemented.") + } + } + } + } } } Ok(()) } - pub fn update(&self) -> Result { + pub async fn update(&self) -> Result { // TODO: implement me Ok((None, None)) }