rust #10
| @ -404,104 +404,52 @@ impl OrderManager { | ||||
|         info!("Closing position #{}", position_id); | ||||
| 
 | ||||
|         debug!("Retrieving open orders, positions and current prices..."); | ||||
|         let (open_orders, order_book, open_positions) = tokio::join!( | ||||
|         let (res_open_orders, res_order_book, res_open_positions) = tokio::join!( | ||||
|             self.client.active_orders(&self.pair), | ||||
|             self.client.order_book(&self.pair), | ||||
|             self.client.active_positions(&self.pair) | ||||
|         ); | ||||
| 
 | ||||
|         let open_orders = match open_orders { | ||||
|             Ok(open_orders) => open_orders, | ||||
|             Err(e) => { | ||||
|                 error!("Could not retrieve open orders: {}", e); | ||||
|                 return Err(e); | ||||
|             } | ||||
|         }; | ||||
|         let (open_orders, order_book, open_positions) = | ||||
|             (res_open_orders?, res_order_book?, res_open_positions?); | ||||
| 
 | ||||
|         let order_book = match order_book { | ||||
|             Ok(order_book) => order_book, | ||||
|             Err(e) => { | ||||
|                 error!("Could not retrieve order book: {}", e); | ||||
|                 return Err(e); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         let position = match open_positions { | ||||
|             Ok(opt_positions) => { | ||||
|                 let positions = opt_positions.ok_or::<BoxError>("No open positions".into())?; | ||||
| 
 | ||||
|                 positions | ||||
|                     .into_iter() | ||||
|                     .find(|x| x.id() == position_id) | ||||
|                     .ok_or::<BoxError>("Position #{} not found in open positions".into())? | ||||
|             } | ||||
| 
 | ||||
|             Err(e) => { | ||||
|                 error!("Could not retrieve open positions: {}", e); | ||||
|                 return Err(e); | ||||
|             } | ||||
|         }; | ||||
|         let position = open_positions | ||||
|             .ok_or("No open positions!")? | ||||
|             .into_iter() | ||||
|             .find(|x| x.id() == position_id) | ||||
|             .ok_or("Position #{} not found in open positions.")?; | ||||
| 
 | ||||
|         let opt_position_order = open_orders | ||||
|             .iter() | ||||
|             .find(|x| x.current_form.amount().neg() == position.amount()); | ||||
| 
 | ||||
|         // 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 opt_position_order { | ||||
|         // If so, don't do anything since the order is taken care of
 | ||||
|         // in the update phase.
 | ||||
|         // If no order is open, send an undercut limit order at the best current price.
 | ||||
|         if let None = opt_position_order { | ||||
|             // No open order, undercutting best price with limit order
 | ||||
|             None => { | ||||
|                 let closing_price = self.best_closing_price(&position, &order_book); | ||||
|             let closing_price = self.best_closing_price(&position, &order_book); | ||||
| 
 | ||||
|                 // TODO: hardocoded platform to Margin!
 | ||||
|                 let order_form = OrderForm::new( | ||||
|                     self.pair.clone(), | ||||
|                     OrderKind::Limit { | ||||
|                         price: closing_price, | ||||
|                         amount: position.amount().neg(), | ||||
|                     }, | ||||
|                     TradingPlatform::Margin, | ||||
|             // TODO: hardcoded platform to Margin!
 | ||||
|             let order_form = OrderForm::new( | ||||
|                 self.pair.clone(), | ||||
|                 OrderKind::Limit { | ||||
|                     price: closing_price, | ||||
|                     amount: position.amount().neg(), | ||||
|                 }, | ||||
|                 TradingPlatform::Margin, | ||||
|             ); | ||||
| 
 | ||||
|             info!("Submitting {} order", order_form.kind()); | ||||
|             if let Err(e) = self.client.submit_order(&order_form).await { | ||||
|                 error!( | ||||
|                     "Could not submit {} to close position #{}: {}", | ||||
|                     order_form.kind(), | ||||
|                     position.id(), | ||||
|                     e | ||||
|                 ); | ||||
| 
 | ||||
|                 info!("Submitting {} order", order_form.kind()); | ||||
|                 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() | ||||
|                 ); | ||||
| 
 | ||||
|                 let (_, strat_messages) = | ||||
|                     self.strategy | ||||
|                         .on_position_order(active_order, &position, &order_book)?; | ||||
| 
 | ||||
|                 if let Some(messages) = strat_messages { | ||||
|                     for m in messages { | ||||
|                         match m { | ||||
|                             Message::SubmitOrder { order } => { | ||||
|                                 info!("Closing open order."); | ||||
|                                 info!("Cancelling open order #{}", active_order.id); | ||||
|                                 self.client.cancel_order(active_order).await?; | ||||
| 
 | ||||
|                                 info!("Submitting {}...", order.kind()); | ||||
|                                 self.client.submit_order(&order).await?; | ||||
|                             } | ||||
|                             _ => { | ||||
|                                 debug!("Received unsupported message from order strategy. Unimplemented.") | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 return Err(e); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @ -512,11 +460,42 @@ impl OrderManager { | ||||
|     pub async fn update(&self) -> Result<OptionUpdate, BoxError> { | ||||
|         trace!("\t[OrderManager] Updating {}", self.pair); | ||||
| 
 | ||||
|         let (open_orders, opt_open_positions) = tokio::join!( | ||||
|         let (res_open_orders, res_order_book) = tokio::join!( | ||||
|             self.client.active_orders(&self.pair), | ||||
|             self.client.active_positions(&self.pair) | ||||
|             self.client.order_book(&self.pair) | ||||
|         ); | ||||
|         let (_open_orders, _opt_open_positions) = (open_orders?, opt_open_positions?); | ||||
| 
 | ||||
|         let (open_orders, order_book) = (res_open_orders?, res_order_book?); | ||||
| 
 | ||||
|         for active_order in open_orders { | ||||
|             debug!( | ||||
|                 "Found open order, calling \"{}\" strategy.", | ||||
|                 self.strategy.name() | ||||
|             ); | ||||
| 
 | ||||
|             let (_, strat_messages) = self.strategy.on_open_order(&active_order, &order_book)?; | ||||
| 
 | ||||
|             if let Some(messages) = strat_messages { | ||||
|                 for m in messages { | ||||
|                     match m { | ||||
|                         Message::SubmitOrder { order: order_form } => { | ||||
|                             info!("Closing open order..."); | ||||
|                             info!("\tCancelling open order #{}", &active_order.id); | ||||
|                             self.client.cancel_order(&active_order).await?; | ||||
| 
 | ||||
|                             info!("\tSubmitting {}...", order_form.kind()); | ||||
|                             self.client.submit_order(&order_form).await?; | ||||
|                             info!("Done!"); | ||||
|                         } | ||||
|                         _ => { | ||||
|                             debug!( | ||||
|                                 "Received unsupported message from order strategy. Unimplemented." | ||||
|                             ) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Ok((None, None)) | ||||
|     } | ||||
|  | ||||
| @ -5,6 +5,7 @@ use dyn_clone::DynClone; | ||||
| use log::info; | ||||
| 
 | ||||
| use crate::events::{Event, EventKind, EventMetadata, Message}; | ||||
| use crate::managers::OptionUpdate; | ||||
| use crate::models::{ | ||||
|     ActiveOrder, OrderBook, OrderBookEntry, OrderForm, OrderKind, Position, PositionProfitState, | ||||
|     PositionState, TradingPlatform, | ||||
| @ -42,15 +43,19 @@ pub trait OrderStrategy: DynClone + Send + Sync { | ||||
|     fn name(&self) -> String; | ||||
|     /// This method is called when the OrderManager checks the open orders on a new tick.
 | ||||
|     /// It should manage if some orders have to be closed or keep open.
 | ||||
|     fn on_open_order(&self); | ||||
|     /// This method is called when the OrderManager is requested to close
 | ||||
|     /// a position that has an open order associated to it.
 | ||||
|     fn on_position_order( | ||||
|     fn on_open_order( | ||||
|         &self, | ||||
|         order: &ActiveOrder, | ||||
|         open_position: &Position, | ||||
|         order_book: &OrderBook, | ||||
|     ) -> Result<(Option<Vec<Event>>, Option<Vec<Message>>), BoxError>; | ||||
|     ) -> Result<OptionUpdate, BoxError>; | ||||
|     // /// This method is called when the OrderManager is requested to close
 | ||||
|     // /// a position that has an open order associated to it.
 | ||||
|     // fn on_position_order(
 | ||||
|     //     &self,
 | ||||
|     //     order: &ActiveOrder,
 | ||||
|     //     open_position: &Position,
 | ||||
|     //     order_book: &OrderBook,
 | ||||
|     // ) -> Result<OptionUpdate, BoxError>;
 | ||||
| } | ||||
| 
 | ||||
| impl Debug for dyn OrderStrategy { | ||||
| @ -81,12 +86,6 @@ impl TrailingStop { | ||||
|     } | ||||
| 
 | ||||
|     fn update_stop_percentage(&mut self, position: &Position) { | ||||
|         info!( | ||||
|             "\tState: {:?} | PL%: {:0.2}", | ||||
|             position.profit_state().unwrap(), | ||||
|             position.pl_perc() | ||||
|         ); | ||||
| 
 | ||||
|         if let Some(profit_state) = position.profit_state() { | ||||
|             let profit_state_delta = match profit_state { | ||||
|                 PositionProfitState::MinimumProfit => Some(0.2), | ||||
| @ -95,12 +94,6 @@ impl TrailingStop { | ||||
|             }; | ||||
| 
 | ||||
|             if let Some(profit_state_delta) = profit_state_delta { | ||||
|                 println!( | ||||
|                     "PL%: {:0.2} | Delta: {}", | ||||
|                     position.pl_perc(), | ||||
|                     profit_state_delta | ||||
|                 ); | ||||
| 
 | ||||
|                 let current_stop_percentage = position.pl_perc() - profit_state_delta; | ||||
| 
 | ||||
|                 match profit_state { | ||||
| @ -121,6 +114,12 @@ impl TrailingStop { | ||||
|                     _ => {} | ||||
|                 } | ||||
|             } | ||||
|             info!( | ||||
|                 "\tState: {:?} | PL%: {:0.2} | Stop: {:0.2}", | ||||
|                 position.profit_state().unwrap(), | ||||
|                 position.pl_perc(), | ||||
|                 self.stop_percentages.get(&position.id()).unwrap_or(&0.0) | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -223,6 +222,7 @@ impl PositionStrategy for TrailingStop { | ||||
|         if let Some(profit_state) = position.profit_state() { | ||||
|             match profit_state { | ||||
|                 PositionProfitState::Critical => { | ||||
|                     info!("Maximum loss reached. Closing position."); | ||||
|                     return (position, None, Some(vec![close_message])); | ||||
|                 } | ||||
|                 _ => {} | ||||
| @ -267,16 +267,11 @@ impl OrderStrategy for FastOrderStrategy { | ||||
|         "Fast order strategy".into() | ||||
|     } | ||||
| 
 | ||||
|     fn on_open_order(&self) { | ||||
|         unimplemented!() | ||||
|     } | ||||
| 
 | ||||
|     fn on_position_order( | ||||
|     fn on_open_order( | ||||
|         &self, | ||||
|         order: &ActiveOrder, | ||||
|         _: &Position, | ||||
|         order_book: &OrderBook, | ||||
|     ) -> Result<(Option<Vec<Event>>, Option<Vec<Message>>), BoxError> { | ||||
|     ) -> Result<OptionUpdate, BoxError> { | ||||
|         let mut messages = vec![]; | ||||
| 
 | ||||
|         // long
 | ||||
| @ -310,4 +305,44 @@ impl OrderStrategy for FastOrderStrategy { | ||||
| 
 | ||||
|         Ok((None, (!messages.is_empty()).then_some(messages))) | ||||
|     } | ||||
| 
 | ||||
|     // fn on_position_order(
 | ||||
|     //     &self,
 | ||||
|     //     order: &ActiveOrder,
 | ||||
|     //     _: &Position,
 | ||||
|     //     order_book: &OrderBook,
 | ||||
|     // ) -> Result<OptionUpdate, BoxError> {
 | ||||
|     //     let mut messages = vec![];
 | ||||
|     //
 | ||||
|     //     // long
 | ||||
|     //     let offer_comparison = {
 | ||||
|     //         if order.current_form.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 order_price = order
 | ||||
|     //         .current_form
 | ||||
|     //         .price()
 | ||||
|     //         .ok_or("The active order does not have a price!")?;
 | ||||
|     //     let delta = (1.0 - (offer_comparison / order_price)).abs() * 100.0;
 | ||||
|     //
 | ||||
|     //     if delta > self.threshold {
 | ||||
|     //         messages.push(Message::SubmitOrder {
 | ||||
|     //             order: OrderForm::new(
 | ||||
|     //                 order.symbol.clone(),
 | ||||
|     //                 OrderKind::Market {
 | ||||
|     //                     amount: order.current_form.amount(),
 | ||||
|     //                 },
 | ||||
|     //                 order.current_form.platform().clone(),
 | ||||
|     //             ),
 | ||||
|     //         })
 | ||||
|     //     }
 | ||||
|     //
 | ||||
|     //     Ok((None, (!messages.is_empty()).then_some(messages)))
 | ||||
|     // }
 | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user