position strategy working

This commit is contained in:
Giulio De Pasquale 2021-01-14 19:20:58 +00:00
parent c5b4aba548
commit 2db59942eb
4 changed files with 153 additions and 129 deletions

View File

@ -1,12 +1,13 @@
use core::time::Duration; use core::time::Duration;
use log::{error, info}; use log::{debug, error, info};
use tokio::time::delay_for; use tokio::time::delay_for;
use crate::connectors::{Client, ExchangeKind}; use crate::connectors::{Client, ExchangeKind};
use crate::currency::{Symbol, SymbolPair}; use crate::currency::{Symbol, SymbolPair};
use crate::events::Event; use crate::events::Event;
use crate::managers::{OrderManager, PositionManager, PriceManager}; use crate::managers::{OrderManager, PositionManager, PriceManager};
use crate::strategy::PositionStrategy;
use crate::ticker::Ticker; use crate::ticker::Ticker;
use crate::BoxError; use crate::BoxError;
@ -54,6 +55,16 @@ impl BfxBot {
} }
} }
pub fn with_position_strategy(mut self, strategy: Box<dyn PositionStrategy>) -> Self {
self.pos_managers = self
.pos_managers
.into_iter()
.map(|x| x.with_strategy(dyn_clone::clone_box(&*strategy)))
.collect();
self
}
pub async fn start_loop(&mut self) -> Result<(), BoxError> { pub async fn start_loop(&mut self) -> Result<(), BoxError> {
if let Err(e) = self.update_managers().await { if let Err(e) = self.update_managers().await {
error!("Error while starting managers: {}", e); error!("Error while starting managers: {}", e);

View File

@ -7,6 +7,7 @@ use tokio::time::Duration;
use crate::bot::BfxBot; use crate::bot::BfxBot;
use crate::connectors::ExchangeKind; use crate::connectors::ExchangeKind;
use crate::currency::Symbol; use crate::currency::Symbol;
use crate::strategy::TrailingStop;
mod bot; mod bot;
mod connectors; mod connectors;
@ -38,7 +39,8 @@ async fn main() -> Result<(), BoxError> {
vec![Symbol::TESTBTC], vec![Symbol::TESTBTC],
Symbol::TESTUSD, Symbol::TESTUSD,
Duration::new(1, 0), Duration::new(1, 0),
); )
.with_position_strategy(Box::new(TrailingStop::new()));
Ok(bot.start_loop().await?) Ok(bot.start_loop().await?)
} }

View File

@ -84,23 +84,6 @@ impl PriceManager {
pub fn pair(&self) -> &SymbolPair { pub fn pair(&self) -> &SymbolPair {
&self.pair &self.pair
} }
// pub fn position_previous_tick(&self, id: u64, tick: Option<u64>) -> Option<&Position> {
// let tick = match tick {
// Some(tick) => {
// if tick < 1 {
// 1
// } else {
// tick
// }
// }
// None => self.current_tick() - 1,
// };
//
// self.positions
// .get(&tick)
// .and_then(|x| x.iter().find(|x| x.position_id() == id))
// }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -148,6 +131,7 @@ impl PriceEntry {
#[derive(Debug)] #[derive(Debug)]
pub struct PositionManager { pub struct PositionManager {
current_tick: u64,
pair: SymbolPair, pair: SymbolPair,
positions_history: HashMap<u64, Position>, positions_history: HashMap<u64, Position>,
active_position: Option<Position>, active_position: Option<Position>,
@ -158,6 +142,7 @@ pub struct PositionManager {
impl PositionManager { impl PositionManager {
pub fn new(pair: SymbolPair, client: Client) -> Self { pub fn new(pair: SymbolPair, client: Client) -> Self {
PositionManager { PositionManager {
current_tick: 0,
pair, pair,
positions_history: HashMap::new(), positions_history: HashMap::new(),
active_position: None, active_position: None,
@ -171,10 +156,16 @@ impl PositionManager {
self self
} }
pub fn current_tick(&self) -> u64 {
self.current_tick
}
pub async fn update(&mut self, tick: u64) -> Result<Option<Vec<Event>>, BoxError> { pub async fn update(&mut self, tick: u64) -> Result<Option<Vec<Event>>, BoxError> {
let opt_active_positions = self.client.active_positions(&self.pair).await?; let opt_active_positions = self.client.active_positions(&self.pair).await?;
let mut events = vec![]; let mut events = vec![];
self.current_tick = tick;
if opt_active_positions.is_none() { if opt_active_positions.is_none() {
return Ok(None); return Ok(None);
} }
@ -200,7 +191,8 @@ impl PositionManager {
} }
}; };
self.positions_history.insert(tick, active_position.clone()); self.positions_history
.insert(self.current_tick(), active_position.clone());
self.active_position = Some(active_position); self.active_position = Some(active_position);
} }
None => { None => {
@ -214,6 +206,24 @@ impl PositionManager {
Ok(Some(events)) Ok(Some(events))
} }
} }
pub fn position_previous_tick(&self, id: u64, tick: Option<u64>) -> Option<&Position> {
let tick = match tick {
Some(tick) => {
if tick < 1 {
1
} else {
tick
}
}
None => self.current_tick() - 1,
};
self.positions_history
.get(&tick)
.filter(|x| x.position_id() == id)
.and_then(|x| Some(x))
}
} }
pub struct OrderManager { pub struct OrderManager {

View File

@ -1,10 +1,11 @@
use std::collections::HashMap;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use dyn_clone::DynClone; use dyn_clone::DynClone;
use crate::events::{Event, SignalKind}; use crate::events::{Event, EventKind, EventMetadata, SignalKind};
use crate::managers::PositionManager; use crate::managers::PositionManager;
use crate::models::Position; use crate::models::{Position, PositionProfitState};
pub trait PositionStrategy: DynClone { pub trait PositionStrategy: DynClone {
fn on_new_tick( fn on_new_tick(
@ -20,110 +21,110 @@ impl Debug for dyn PositionStrategy {
} }
} }
// #[derive(Clone, Debug)] #[derive(Clone, Debug)]
// pub struct TrailingStop { pub struct TrailingStop {
// stop_percentages: HashMap<u64, f64>, stop_percentages: HashMap<u64, f64>,
// } }
//
// impl TrailingStop { impl TrailingStop {
// const BREAK_EVEN_PERC: f64 = 0.2; const BREAK_EVEN_PERC: f64 = 0.2;
// const MIN_PROFIT_PERC: f64 = TrailingStop::BREAK_EVEN_PERC + 0.3; const MIN_PROFIT_PERC: f64 = TrailingStop::BREAK_EVEN_PERC + 0.3;
// const GOOD_PROFIT_PERC: f64 = TrailingStop::MIN_PROFIT_PERC * 2.5; const GOOD_PROFIT_PERC: f64 = TrailingStop::MIN_PROFIT_PERC * 2.5;
// const MAX_LOSS_PERC: f64 = -1.7; const MAX_LOSS_PERC: f64 = -1.7;
//
// const TAKER_FEE: f64 = 0.2; const TAKER_FEE: f64 = 0.2;
//
// pub fn new() -> Self { pub fn new() -> Self {
// TrailingStop { TrailingStop {
// stop_percentages: HashMap::new(), stop_percentages: HashMap::new(),
// } }
// } }
//
// fn net_pl_percentage(pl: f64, fee: f64) -> f64 { fn net_pl_percentage(pl: f64, fee: f64) -> f64 {
// pl - fee pl - fee
// } }
// } }
//
// impl PositionStrategy for TrailingStop { impl PositionStrategy for TrailingStop {
// fn on_new_tick( fn on_new_tick(
// &self, &self,
// position: &Position, position: &Position,
// status: &PairStatus, manager: &PositionManager,
// ) -> (Position, Vec<Event>, Vec<SignalKind>) { ) -> (Position, Vec<Event>, Vec<SignalKind>) {
// let mut signals = vec![]; let mut signals = vec![];
// let pl_perc = TrailingStop::net_pl_percentage(position.pl_perc(), TrailingStop::TAKER_FEE); let pl_perc = TrailingStop::net_pl_percentage(position.pl_perc(), TrailingStop::TAKER_FEE);
// let events = vec![]; let events = vec![];
//
// let state = { let state = {
// if pl_perc > TrailingStop::GOOD_PROFIT_PERC { if pl_perc > TrailingStop::GOOD_PROFIT_PERC {
// PositionProfitState::Profit PositionProfitState::Profit
// } else if TrailingStop::MIN_PROFIT_PERC <= pl_perc } else if TrailingStop::MIN_PROFIT_PERC <= pl_perc
// && pl_perc < TrailingStop::GOOD_PROFIT_PERC && pl_perc < TrailingStop::GOOD_PROFIT_PERC
// { {
// PositionProfitState::MinimumProfit PositionProfitState::MinimumProfit
// } else if 0.0 <= pl_perc && pl_perc < TrailingStop::MIN_PROFIT_PERC { } else if 0.0 <= pl_perc && pl_perc < TrailingStop::MIN_PROFIT_PERC {
// PositionProfitState::BreakEven PositionProfitState::BreakEven
// } else if TrailingStop::MAX_LOSS_PERC < pl_perc && pl_perc < 0.0 { } else if TrailingStop::MAX_LOSS_PERC < pl_perc && pl_perc < 0.0 {
// PositionProfitState::Loss PositionProfitState::Loss
// } else { } else {
// signals.push(SignalKind::ClosePosition { signals.push(SignalKind::ClosePosition {
// position_id: position.position_id(), position_id: position.position_id(),
// }); });
// PositionProfitState::Critical PositionProfitState::Critical
// } }
// }; };
//
// let opt_pre_pw = status.position_previous_tick(position.position_id(), None); let opt_pre_pw = manager.position_previous_tick(position.position_id(), None);
// let event_metadata = EventMetadata::new(Some(position.position_id()), None); let event_metadata = EventMetadata::new(Some(position.position_id()), None);
// let new_position = position.clone().with_profit_state(Some(state)); let new_position = position.clone().with_profit_state(Some(state));
//
// match opt_pre_pw { match opt_pre_pw {
// Some(prev) => { Some(prev) => {
// if prev.profit_state() == Some(state) { if prev.profit_state() == Some(state) {
// return (new_position, events, signals); return (new_position, events, signals);
// } }
// } }
// None => return (new_position, events, signals), None => return (new_position, events, signals),
// }; };
//
// let events = { let events = {
// let mut events = vec![]; let mut events = vec![];
//
// if state == PositionProfitState::Profit { if state == PositionProfitState::Profit {
// events.push(Event::new( events.push(Event::new(
// EventKind::ReachedGoodProfit, EventKind::ReachedGoodProfit,
// status.current_tick(), manager.current_tick(),
// Some(event_metadata), Some(event_metadata),
// )); ));
// } else if state == PositionProfitState::MinimumProfit { } else if state == PositionProfitState::MinimumProfit {
// events.push(Event::new( events.push(Event::new(
// EventKind::ReachedMinProfit, EventKind::ReachedMinProfit,
// status.current_tick(), manager.current_tick(),
// Some(event_metadata), Some(event_metadata),
// )); ));
// } else if state == PositionProfitState::BreakEven { } else if state == PositionProfitState::BreakEven {
// events.push(Event::new( events.push(Event::new(
// EventKind::ReachedBreakEven, EventKind::ReachedBreakEven,
// status.current_tick(), manager.current_tick(),
// Some(event_metadata), Some(event_metadata),
// )); ));
// } else if state == PositionProfitState::Loss { } else if state == PositionProfitState::Loss {
// events.push(Event::new( events.push(Event::new(
// EventKind::ReachedLoss, EventKind::ReachedLoss,
// status.current_tick(), manager.current_tick(),
// Some(event_metadata), Some(event_metadata),
// )); ));
// } else { } else {
// events.push(Event::new( events.push(Event::new(
// EventKind::ReachedMaxLoss, EventKind::ReachedMaxLoss,
// status.current_tick(), manager.current_tick(),
// Some(event_metadata), Some(event_metadata),
// )); ));
// } }
//
// events events
// }; };
//
// return (new_position, events, signals); return (new_position, events, signals);
// } }
// } }