From 78b57b3899274ab64fd351ee98ce6ad6adc28c1b Mon Sep 17 00:00:00 2001 From: Giulio De Pasquale Date: Tue, 5 Jan 2021 12:58:47 +0000 Subject: [PATCH] traits and shit --- rustybot/Cargo.lock | 12 ++++ rustybot/Cargo.toml | 1 + rustybot/src/bot.rs | 43 +++++--------- rustybot/src/events.rs | 26 ++++----- rustybot/src/main.rs | 43 ++++++++------ rustybot/src/orders.rs | 21 +++++++ rustybot/src/pairs.rs | 17 +++--- rustybot/src/positions.rs | 118 ++++++++++++++++++++++++++------------ rustybot/src/strategy.rs | 13 ++--- 9 files changed, 179 insertions(+), 115 deletions(-) create mode 100644 rustybot/src/orders.rs diff --git a/rustybot/Cargo.lock b/rustybot/Cargo.lock index a320dd2..90fdb3c 100644 --- a/rustybot/Cargo.lock +++ b/rustybot/Cargo.lock @@ -15,6 +15,17 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +[[package]] +name = "async-trait" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -1016,6 +1027,7 @@ checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232" name = "rustybot" version = "0.1.0" dependencies = [ + "async-trait", "bitfinex", "futures-util", "tokio 0.2.24", diff --git a/rustybot/Cargo.toml b/rustybot/Cargo.toml index 8b3ea3c..fd2d334 100644 --- a/rustybot/Cargo.toml +++ b/rustybot/Cargo.toml @@ -11,3 +11,4 @@ bitfinex = { path= "/home/giulio/dev/bitfinex-rs" } tokio = { version = "0.2", features=["full"]} tokio-tungstenite = "*" futures-util = { version = "0.3", default-features = false, features = ["async-await", "sink", "std"] } +async-trait = "0.1" \ No newline at end of file diff --git a/rustybot/src/bot.rs b/rustybot/src/bot.rs index 3b2fa7b..1be1429 100644 --- a/rustybot/src/bot.rs +++ b/rustybot/src/bot.rs @@ -3,34 +3,17 @@ use std::collections::HashMap; use bitfinex::api::Bitfinex; use bitfinex::positions::Position; +use bitfinex::ticker::TradingPairTicker; use tokio::time::delay_for; -use crate::BoxError; +use crate::connectors::Connector; use crate::currency::{Symbol, SymbolPair}; use crate::pairs::PairStatus; use crate::ticker::Ticker; -use bitfinex::ticker::TradingPairTicker; +use crate::BoxError; -pub struct BfxWrapper { - bfx: Bitfinex -} - -impl BfxWrapper { - pub fn new(api_key: &str, api_secret: &str) -> Self { - BfxWrapper { - bfx: Bitfinex::new(Some(api_key.into()), Some(api_secret.into())) - } - } - - pub async fn current_prices(&self, pair: &SymbolPair) -> Result { - let ticker: TradingPairTicker = self.bfx.ticker.trading_pair(pair.clone()).await?; - - Ok(ticker) - } -} - -pub struct BfxBot { - pub bfx: BfxWrapper, +pub struct BfxBot<'a> { + connector: Box, ticker: Ticker, pair_status: Vec, quote: Symbol, @@ -39,10 +22,15 @@ pub struct BfxBot { // ledger: String, } -impl BfxBot { - pub fn new>(api_key: S, api_secret: S, trading_symbols: Vec, quote: Symbol, tick_duration: Duration) -> Self { +impl<'a> BfxBot<'a> { + pub fn new( + connector: C, + trading_symbols: Vec, + quote: Symbol, + tick_duration: Duration, + ) -> Self { BfxBot { - bfx: BfxWrapper::new(&api_key.into(), &api_secret.into()), + connector: Box::new(connector), ticker: Ticker::new(tick_duration), pair_status: trading_symbols .iter() @@ -59,11 +47,11 @@ impl BfxBot { pub async fn current_prices(&self, symbol: Symbol) -> Result { let trading_pair = SymbolPair::new(self.quote.clone(), symbol.clone()); - if !self.trading_symbols.contains(&symbol){ + if !self.trading_symbols.contains(&symbol) { return Err("Symbol not supported.".into()); } - self.bfx.current_prices(&trading_pair).await + self.connector.current_prices(&trading_pair).await } pub async fn update(&mut self) { @@ -80,4 +68,3 @@ impl BfxBot { // for p in active_positions {} // } } - diff --git a/rustybot/src/events.rs b/rustybot/src/events.rs index e3c9ff2..5ef52e5 100644 --- a/rustybot/src/events.rs +++ b/rustybot/src/events.rs @@ -1,12 +1,11 @@ use std::collections::{HashMap, HashSet}; use std::future::Future; -use bitfinex::positions::Position; use tokio::stream::StreamExt; use tokio::task::JoinHandle; use crate::pairs::PairStatus; -use crate::positions::{PositionState, PositionWrapper}; +use crate::positions::{Position, PositionState}; use crate::BoxError; #[derive(Copy, Clone)] @@ -80,11 +79,10 @@ impl Event { pub struct EventDispatcher { event_handlers: HashMap JoinHandle<()>>>>, position_state_handlers: - HashMap JoinHandle<()>>>>, + HashMap JoinHandle<()>>>>, on_any_event_handlers: Vec JoinHandle<()>>>, - on_any_position_state_handlers: - Vec JoinHandle<()>>>, + on_any_position_state_handlers: Vec JoinHandle<()>>>, } impl EventDispatcher { @@ -109,17 +107,15 @@ impl EventDispatcher { } } - pub fn call_position_state_handlers(&self, pw: &PositionWrapper, status: &PairStatus) { - if let Some(state) = pw.state() { - if let Some(functions) = self.position_state_handlers.get(&state) { - for f in functions { - f(pw, status); - } + pub fn call_position_state_handlers(&self, position: &Position, status: &PairStatus) { + if let Some(functions) = self.position_state_handlers.get(&position.state()) { + for f in functions { + f(position, status); } } for f in &self.on_any_position_state_handlers { - f(pw, status); + f(position, status); } } @@ -145,18 +141,18 @@ impl EventDispatcher { state: PositionState, f: F, ) where - F: Fn(&PositionWrapper, &PairStatus) -> Fut, + F: Fn(&Position, &PairStatus) -> Fut, Fut: Future + Send, { match state { PositionState::Any => self .on_any_position_state_handlers - .push(Box::new(move |pw, s| tokio::spawn(f(&pw, s)))), + .push(Box::new(move |p, s| tokio::spawn(f(&p, s)))), _ => self .position_state_handlers .entry(state) .or_default() - .push(Box::new(move |pw, s| tokio::spawn(f(&pw, s)))), + .push(Box::new(move |p, s| tokio::spawn(f(&p, s)))), } } } diff --git a/rustybot/src/main.rs b/rustybot/src/main.rs index 97cda11..5885bdd 100644 --- a/rustybot/src/main.rs +++ b/rustybot/src/main.rs @@ -1,35 +1,40 @@ -use bitfinex::api::Bitfinex; -use bitfinex::ticker::TradingPairTicker; -use tokio::time::{Duration, delay_for}; +use tokio::time::{delay_for, Duration}; use crate::bot::BfxBot; use crate::currency::{Symbol, SymbolPair}; -mod ticker; +mod bot; +mod connectors; +mod currency; mod events; +mod orders; mod pairs; mod positions; mod strategy; -mod bot; -mod currency; - +mod ticker; pub type BoxError = Box; #[tokio::main] async fn main() -> Result<(), BoxError> { - let test_api_key = "P1EVE68DJByDAkGQvpIkTwfrbYXd2Vo2ZaIhTYb9vx2"; - let test_api_secret = "1nicg8z0zKVEt5Rb7ZDpIYjVYVTgvCaCPMZqB0niFli"; - - let mut bot = BfxBot::new(test_api_key, test_api_secret, vec![Symbol::BTC, Symbol::ETH, Symbol::XMR], Symbol::USD, Duration::new(20, 0)); - - loop { - let ticker = bot.current_prices("ETH".into()).await?; - bot.update().await; - - // let ticker = bot.current_prices("ETH".into()).await?; - println!("{:?}", ticker); - } + // let test_api_key = "P1EVE68DJByDAkGQvpIkTwfrbYXd2Vo2ZaIhTYb9vx2"; + // let test_api_secret = "1nicg8z0zKVEt5Rb7ZDpIYjVYVTgvCaCPMZqB0niFli"; + // + // let mut bot = BfxBot::new( + // test_api_key, + // test_api_secret, + // vec![Symbol::BTC, Symbol::ETH, Symbol::XMR], + // Symbol::USD, + // Duration::new(20, 0), + // ); + // + // loop { + // let ticker = bot.current_prices("ETH".into()).await?; + // bot.update().await; + // + // // let ticker = bot.current_prices("ETH".into()).await?; + // println!("{:?}", ticker); + // } Ok(()) } diff --git a/rustybot/src/orders.rs b/rustybot/src/orders.rs new file mode 100644 index 0000000..7569fa8 --- /dev/null +++ b/rustybot/src/orders.rs @@ -0,0 +1,21 @@ +pub struct Order { + pub id: i64, + pub group_id: Option, + pub client_id: i64, + pub symbol: String, + pub creation_timestamp: i64, + pub update_timestamp: i64, + pub amount: f64, + pub amount_original: f64, + pub order_type: String, + pub previous_order_type: Option, + pub flags: Option, + pub order_status: Option, + pub price: f64, + pub price_avg: f64, + pub price_trailing: Option, + pub price_aux_limit: Option, + pub notify: i32, + pub hidden: i32, + pub placed_id: Option, +} diff --git a/rustybot/src/pairs.rs b/rustybot/src/pairs.rs index e24e1a3..b2e986b 100644 --- a/rustybot/src/pairs.rs +++ b/rustybot/src/pairs.rs @@ -1,10 +1,9 @@ use std::collections::HashMap; -use bitfinex::positions::Position; - use crate::currency::SymbolPair; use crate::events::{Event, EventDispatcher}; -use crate::positions::PositionWrapper; +use crate::orders::Order; +use crate::positions::Position; use crate::strategy::Strategy; pub struct PairStatus { @@ -12,8 +11,8 @@ pub struct PairStatus { dispatcher: EventDispatcher, prices: HashMap, events: Vec, - // orders: HashMap>, - positions: HashMap>, + orders: HashMap>, + positions: HashMap>, current_tick: u64, strategy: Option>, } @@ -28,6 +27,7 @@ impl PairStatus { positions: HashMap::new(), current_tick, strategy, + orders: HashMap::new(), } } @@ -36,7 +36,8 @@ impl PairStatus { match &self.strategy { Some(strategy) => strategy.position_on_new_tick(&position, &self), None => ( - PositionWrapper::new(position.clone(), position.pl(), position.pl_perc(), None), + position, + // PositionWrapper::new(position.clone(), position.pl(), position.pl_perc(), None), vec![], vec![], ), @@ -67,9 +68,9 @@ impl PairStatus { self.current_tick } - pub fn previous_pw(&self, id: u64) -> Option<&PositionWrapper> { + pub fn previous_pw(&self, id: u64) -> Option<&Position> { self.positions .get(&(self.current_tick - 1)) - .and_then(|x| x.iter().find(|x| x.position().position_id() == id)) + .and_then(|x| x.iter().find(|x| x.position_id() == id)) } } diff --git a/rustybot/src/positions.rs b/rustybot/src/positions.rs index 313f961..8d3655e 100644 --- a/rustybot/src/positions.rs +++ b/rustybot/src/positions.rs @@ -1,4 +1,81 @@ -use bitfinex::positions::Position; +use crate::currency::{Symbol, SymbolPair}; + +#[derive(Clone)] +pub struct Position { + pair: SymbolPair, + state: PositionState, + amount: f64, + base_price: f64, + pl: f64, + pl_perc: f64, + price_liq: f64, + position_id: u64, + creation_date: Option, + creation_update: Option, +} + +impl Position { + pub fn new( + pair: SymbolPair, + state: PositionState, + amount: f64, + base_price: f64, + pl: f64, + pl_perc: f64, + price_liq: f64, + position_id: u64, + ) -> Self { + Position { + pair, + state, + amount, + base_price, + pl, + pl_perc, + price_liq, + position_id, + creation_date: None, + creation_update: None, + } + } + + pub fn with_creation_date(mut self, creation_date: u64) -> Self { + self.creation_date = Some(creation_date); + self.creation_update = Some(creation_date); + self + } + + pub fn pair(&self) -> &SymbolPair { + &self.pair + } + pub fn state(&self) -> PositionState { + self.state + } + pub fn amount(&self) -> f64 { + self.amount + } + pub fn base_price(&self) -> f64 { + self.base_price + } + pub fn pl(&self) -> f64 { + self.pl + } + pub fn pl_perc(&self) -> f64 { + self.pl_perc + } + pub fn price_liq(&self) -> f64 { + self.price_liq + } + pub fn position_id(&self) -> u64 { + self.position_id + } + pub fn creation_date(&self) -> Option { + self.creation_date + } + pub fn creation_update(&self) -> Option { + self.creation_update + } +} #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub enum PositionState { @@ -7,6 +84,7 @@ pub enum PositionState { BreakEven, MinimumProfit, Profit, + Closed, Any, } @@ -17,44 +95,8 @@ impl PositionState { PositionState::Critical | PositionState::Loss => "red", PositionState::BreakEven => "yellow", PositionState::MinimumProfit | PositionState::Profit => "green", + PositionState::Closed => "gray", } .into() } } - -#[derive(Clone)] -pub struct PositionWrapper { - position: Position, - net_pl: f64, - net_pl_perc: f64, - state: Option, -} - -impl PositionWrapper { - pub fn new( - position: Position, - net_pl: f64, - net_pl_perc: f64, - state: Option, - ) -> Self { - PositionWrapper { - position, - net_pl, - net_pl_perc, - state, - } - } - - pub fn position(&self) -> &Position { - &self.position - } - pub fn net_pl(&self) -> f64 { - self.net_pl - } - pub fn net_pl_perc(&self) -> f64 { - self.net_pl_perc - } - pub fn state(&self) -> Option { - self.state - } -} diff --git a/rustybot/src/strategy.rs b/rustybot/src/strategy.rs index 5b70ccd..7251a98 100644 --- a/rustybot/src/strategy.rs +++ b/rustybot/src/strategy.rs @@ -1,17 +1,15 @@ use std::collections::HashMap; -use bitfinex::positions::Position; - use crate::events::{Event, EventKind, EventMetadata, SignalKind}; use crate::pairs::PairStatus; -use crate::positions::{PositionState, PositionWrapper}; +use crate::positions::{Position, PositionState}; pub trait Strategy { fn position_on_new_tick( &self, position: &Position, status: &PairStatus, - ) -> (PositionWrapper, Vec, Vec); + ) -> (Position, Vec, Vec); } struct TrailingStop { @@ -42,7 +40,7 @@ impl Strategy for TrailingStop { &self, position: &Position, status: &PairStatus, - ) -> (PositionWrapper, Vec, Vec) { + ) -> (Position, Vec, Vec) { let mut signals = vec![]; let pl_perc = TrailingStop::net_pl_percentage(position.pl_perc(), TrailingStop::TAKER_FEE); let events = vec![]; @@ -66,11 +64,12 @@ impl Strategy for TrailingStop { let opt_pre_pw = status.previous_pw(position.position_id()); let event_metadata = EventMetadata::new(Some(position.position_id()), None); - let pw = PositionWrapper::new(position.clone(), position.pl(), pl_perc, Some(state)); + // let pw = PositionWrapper::new(position.clone(), position.pl(), pl_perc, Some(state)); + let pw = position.clone(); match opt_pre_pw { Some(prev) => { - if prev.state() == Some(state) { + if prev.state() == state { return (pw, events, signals); } }