From 2c151ae6c18907a05f766e9e60d2ec6f20462381 Mon Sep 17 00:00:00 2001 From: Giulio De Pasquale Date: Thu, 14 Jan 2021 18:36:56 +0000 Subject: [PATCH] position manager working --- rustybot/src/bot.rs | 31 +++++++++++++------ rustybot/src/connectors.rs | 19 +++++++++--- rustybot/src/managers.rs | 63 ++++++++++++++++++++++++++++++++------ rustybot/src/models.rs | 2 +- rustybot/src/strategy.rs | 11 +++++-- 5 files changed, 99 insertions(+), 27 deletions(-) diff --git a/rustybot/src/bot.rs b/rustybot/src/bot.rs index 8e92842..f023d97 100644 --- a/rustybot/src/bot.rs +++ b/rustybot/src/bot.rs @@ -33,21 +33,21 @@ impl BfxBot { tick_duration: Duration, ) -> Self { let clients: Vec<_> = exchanges.iter().map(|x| Client::new(x)).collect(); + let pairs: Vec<_> = trading_symbols + .iter() + .map(|x| SymbolPair::new(quote.clone(), x.clone())) + .collect(); let mut pos_managers = Vec::new(); let mut order_managers = Vec::new(); let mut pair_statuses = Vec::new(); for c in clients { - pos_managers.push(PositionManager::new(c.clone())); - order_managers.push(OrderManager::new(c.clone())); - pair_statuses.extend( - trading_symbols - .iter() - .map(|x| SymbolPair::new(quote.clone(), x.clone())) - .map(|x| PriceManager::new(x, c.clone())) - .collect::>(), - ) + for p in &pairs { + pos_managers.push(PositionManager::new(p.clone(), c.clone())); + order_managers.push(OrderManager::new(p.clone(), c.clone())); + pair_statuses.push(PriceManager::new(p.clone(), c.clone())); + } } BfxBot { @@ -79,10 +79,23 @@ impl BfxBot { async fn update_managers(&mut self) -> Result<(), BoxError> { self.update_price_managers().await?; + self.update_position_managers().await?; Ok(()) } + async fn update_position_managers(&mut self) -> Result>, BoxError> { + for mgr in &mut self.pos_managers { + let tick = self.ticker.current_tick(); + + mgr.update(tick).await?; + + println!("{:?}", mgr); + } + + Ok(None) + } + async fn update_price_managers(&mut self) -> Result>, BoxError> { let futures: Vec<_> = self .price_managers diff --git a/rustybot/src/connectors.rs b/rustybot/src/connectors.rs index bbe5725..c7c99df 100644 --- a/rustybot/src/connectors.rs +++ b/rustybot/src/connectors.rs @@ -45,7 +45,10 @@ impl Client { } } - pub async fn active_positions(&self, pair: &SymbolPair) -> Result, BoxError> { + pub async fn active_positions( + &self, + pair: &SymbolPair, + ) -> Result>, BoxError> { self.inner.active_positions(pair).await } @@ -70,7 +73,7 @@ impl Client { #[async_trait] pub trait Connector: Send + Sync { - async fn active_positions(&self, pair: &SymbolPair) -> Result, BoxError>; + async fn active_positions(&self, pair: &SymbolPair) -> Result>, BoxError>; async fn current_prices(&self, pair: &SymbolPair) -> Result; async fn active_orders(&self, pair: &SymbolPair) -> Result, BoxError>; async fn submit_order( @@ -123,14 +126,20 @@ impl BitfinexConnector { #[async_trait] impl Connector for BitfinexConnector { - async fn active_positions(&self, pair: &SymbolPair) -> Result, BoxError> { + async fn active_positions(&self, pair: &SymbolPair) -> Result>, BoxError> { let active_positions = self.bfx.positions.active_positions().await?; - Ok(active_positions + let positions: Vec<_> = active_positions .into_iter() .filter_map(|x| x.try_into().ok()) .filter(|x: &Position| x.pair() == pair) - .collect()) + .collect(); + + if positions.is_empty() { + Ok(None) + } else { + Ok(Some(positions)) + } } async fn current_prices(&self, pair: &SymbolPair) -> Result { diff --git a/rustybot/src/managers.rs b/rustybot/src/managers.rs index e9a2721..4a0d0c9 100644 --- a/rustybot/src/managers.rs +++ b/rustybot/src/managers.rs @@ -147,18 +147,21 @@ impl PriceEntry { } } +#[derive(Debug)] pub struct PositionManager { - queue: VecDeque, - open_positions: Vec, + pair: SymbolPair, + positions_history: HashMap, + active_position: Option, client: Client, strategy: Option>, } impl PositionManager { - pub fn new(client: Client) -> Self { + pub fn new(pair: SymbolPair, client: Client) -> Self { PositionManager { - queue: VecDeque::new(), - open_positions: Vec::new(), + pair, + positions_history: HashMap::new(), + active_position: None, client, strategy: None, } @@ -169,21 +172,61 @@ impl PositionManager { self } - pub fn update(&self) -> Option> { - unimplemented!() + pub async fn update(&mut self, tick: u64) -> Result>, BoxError> { + let mut opt_active_positions = self.client.active_positions(&self.pair).await?; + let mut events = vec![]; + + if opt_active_positions.is_none() { + return Ok(None); + } + + // we assume there is only ONE active position per pair + match opt_active_positions + .unwrap() + .into_iter() + .filter(|x| x.pair() == &self.pair) + .next() + { + Some(position) => { + // applying strategy to position + let active_position = { + match &self.strategy { + Some(strategy) => { + let (pos, strategy_events, _) = strategy.on_new_tick(&position, &self); + + events.extend(strategy_events); + pos + } + None => position, + } + }; + + self.positions_history.insert(tick, active_position.clone()); + self.active_position = Some(active_position); + } + None => { + self.active_position = None; + } + } + + if events.is_empty() { + Ok(None) + } else { + Ok(Some(events)) + } } } pub struct OrderManager { - queue: VecDeque, + pair: SymbolPair, open_orders: Vec, client: Client, } impl OrderManager { - pub fn new(client: Client) -> Self { + pub fn new(pair: SymbolPair, client: Client) -> Self { OrderManager { - queue: VecDeque::new(), + pair, open_orders: Vec::new(), client, } diff --git a/rustybot/src/models.rs b/rustybot/src/models.rs index f769bc6..e0423ed 100644 --- a/rustybot/src/models.rs +++ b/rustybot/src/models.rs @@ -66,7 +66,7 @@ pub enum OrderKind { * Positions ***************/ -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct Position { pair: SymbolPair, state: PositionState, diff --git a/rustybot/src/strategy.rs b/rustybot/src/strategy.rs index 23ed3cf..11f569c 100644 --- a/rustybot/src/strategy.rs +++ b/rustybot/src/strategy.rs @@ -1,18 +1,25 @@ use std::collections::HashMap; use crate::events::{Event, EventKind, EventMetadata, SignalKind}; -use crate::managers::PriceManager; +use crate::managers::{PositionManager, PriceManager}; use crate::models::{Position, PositionProfitState}; use dyn_clone::DynClone; +use std::fmt::{Debug, Formatter}; pub trait PositionStrategy: DynClone { fn on_new_tick( &self, position: &Position, - status: &PriceManager, + manager: &PositionManager, ) -> (Position, Vec, Vec); } +impl Debug for dyn PositionStrategy { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "PositionStrategy") + } +} + // #[derive(Clone, Debug)] // pub struct TrailingStop { // stop_percentages: HashMap,