traits and shit
This commit is contained in:
parent
ea7c8394a3
commit
78b57b3899
12
rustybot/Cargo.lock
generated
12
rustybot/Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
@ -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<TradingPairTicker, BoxError> {
|
||||
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<dyn Connector + 'a>,
|
||||
ticker: Ticker,
|
||||
pair_status: Vec<PairStatus>,
|
||||
quote: Symbol,
|
||||
@ -39,10 +22,15 @@ pub struct BfxBot {
|
||||
// ledger: String,
|
||||
}
|
||||
|
||||
impl BfxBot {
|
||||
pub fn new<S: Into<String>>(api_key: S, api_secret: S, trading_symbols: Vec<Symbol>, quote: Symbol, tick_duration: Duration) -> Self {
|
||||
impl<'a> BfxBot<'a> {
|
||||
pub fn new<C: Connector + 'a>(
|
||||
connector: C,
|
||||
trading_symbols: Vec<Symbol>,
|
||||
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()
|
||||
@ -63,7 +51,7 @@ impl BfxBot {
|
||||
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 {}
|
||||
// }
|
||||
}
|
||||
|
||||
|
@ -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<EventKind, Vec<Box<dyn Fn(&Event, &PairStatus) -> JoinHandle<()>>>>,
|
||||
position_state_handlers:
|
||||
HashMap<PositionState, Vec<Box<dyn Fn(&PositionWrapper, &PairStatus) -> JoinHandle<()>>>>,
|
||||
HashMap<PositionState, Vec<Box<dyn Fn(&Position, &PairStatus) -> JoinHandle<()>>>>,
|
||||
|
||||
on_any_event_handlers: Vec<Box<dyn Fn(&Event, &PairStatus) -> JoinHandle<()>>>,
|
||||
on_any_position_state_handlers:
|
||||
Vec<Box<dyn Fn(&PositionWrapper, &PairStatus) -> JoinHandle<()>>>,
|
||||
on_any_position_state_handlers: Vec<Box<dyn Fn(&Position, &PairStatus) -> 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) {
|
||||
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(pw, status);
|
||||
}
|
||||
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<Output = ()> + 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)))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<dyn std::error::Error + Send + Sync>;
|
||||
|
||||
#[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 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?;
|
||||
println!("{:?}", ticker);
|
||||
}
|
||||
// bot.update().await;
|
||||
//
|
||||
// // let ticker = bot.current_prices("ETH".into()).await?;
|
||||
// println!("{:?}", ticker);
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
21
rustybot/src/orders.rs
Normal file
21
rustybot/src/orders.rs
Normal file
@ -0,0 +1,21 @@
|
||||
pub struct Order {
|
||||
pub id: i64,
|
||||
pub group_id: Option<i32>,
|
||||
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<String>,
|
||||
pub flags: Option<i32>,
|
||||
pub order_status: Option<String>,
|
||||
pub price: f64,
|
||||
pub price_avg: f64,
|
||||
pub price_trailing: Option<f64>,
|
||||
pub price_aux_limit: Option<f64>,
|
||||
pub notify: i32,
|
||||
pub hidden: i32,
|
||||
pub placed_id: Option<i32>,
|
||||
}
|
@ -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<u64, f64>,
|
||||
events: Vec<Event>,
|
||||
// orders: HashMap<u64, Vec<Order>>,
|
||||
positions: HashMap<u64, Vec<PositionWrapper>>,
|
||||
orders: HashMap<u64, Vec<Order>>,
|
||||
positions: HashMap<u64, Vec<Position>>,
|
||||
current_tick: u64,
|
||||
strategy: Option<Box<dyn Strategy>>,
|
||||
}
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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<u64>,
|
||||
creation_update: Option<u64>,
|
||||
}
|
||||
|
||||
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<u64> {
|
||||
self.creation_date
|
||||
}
|
||||
pub fn creation_update(&self) -> Option<u64> {
|
||||
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<PositionState>,
|
||||
}
|
||||
|
||||
impl PositionWrapper {
|
||||
pub fn new(
|
||||
position: Position,
|
||||
net_pl: f64,
|
||||
net_pl_perc: f64,
|
||||
state: Option<PositionState>,
|
||||
) -> 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<PositionState> {
|
||||
self.state
|
||||
}
|
||||
}
|
||||
|
@ -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<Event>, Vec<SignalKind>);
|
||||
) -> (Position, Vec<Event>, Vec<SignalKind>);
|
||||
}
|
||||
|
||||
struct TrailingStop {
|
||||
@ -42,7 +40,7 @@ impl Strategy for TrailingStop {
|
||||
&self,
|
||||
position: &Position,
|
||||
status: &PairStatus,
|
||||
) -> (PositionWrapper, Vec<Event>, Vec<SignalKind>) {
|
||||
) -> (Position, Vec<Event>, Vec<SignalKind>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user