other traits and shit

This commit is contained in:
Giulio De Pasquale 2021-01-05 14:51:03 +00:00
parent 78b57b3899
commit ae648b70a4
6 changed files with 142 additions and 51 deletions

37
rustybot/Cargo.lock generated
View File

@ -15,6 +15,15 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
dependencies = [
"memchr",
]
[[package]]
name = "async-trait"
version = "0.1.42"
@ -958,6 +967,24 @@ version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]]
name = "regex"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
@ -1030,6 +1057,7 @@ dependencies = [
"async-trait",
"bitfinex",
"futures-util",
"regex",
"tokio 0.2.24",
"tokio-tungstenite",
]
@ -1195,6 +1223,15 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]]
name = "tinyvec"
version = "1.1.0"

View File

@ -12,3 +12,4 @@ 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"
regex = "1"

View File

@ -1,19 +1,26 @@
use core::fmt;
use std::borrow::Cow;
use std::fmt::{Display, Formatter};
use std::convert::TryFrom;
use std::fmt::{Display, Error, Formatter};
use regex::Regex;
use crate::BoxError;
#[derive(Clone, PartialEq, Hash)]
pub struct Symbol {
name: Cow<'static, str>
name: Cow<'static, str>,
}
impl<S> From<S> for Symbol where S: Into<String> {
impl<S> From<S> for Symbol
where
S: Into<String>,
{
fn from(item: S) -> Self {
Symbol::new(item.into())
}
}
impl Symbol {
pub const XMR: Symbol = Symbol::new_static("XMR");
pub const BTC: Symbol = Symbol::new_static("BTC");
@ -24,11 +31,15 @@ impl Symbol {
pub const EUR: Symbol = Symbol::new_static("EUR");
pub fn new(name: String) -> Self {
Symbol { name: Cow::from(name) }
Symbol {
name: Cow::from(name),
}
}
pub const fn new_static(name: &'static str) -> Self {
Symbol { name: Cow::Borrowed(name) }
Symbol {
name: Cow::Borrowed(name),
}
}
pub fn name(&self) -> &str {
@ -72,6 +83,23 @@ impl Into<String> for SymbolPair {
}
}
impl TryFrom<&str> for SymbolPair {
type Error = BoxError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
const REGEX: &str = r"^[t|f](?P<base>\w{3,4}):?(?P<quote>\w{3,4})";
let captures = Regex::new(REGEX)?.captures(&value).ok_or("Invalid input")?;
let quote = captures.name("quote").ok_or("Quote not found")?.as_str();
let base = captures.name("base").ok_or("Base not found")?.as_str();
Ok(SymbolPair {
quote: quote.into(),
base: base.into(),
})
}
}
impl Display for SymbolPair {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.base, self.quote)
@ -96,7 +124,13 @@ struct Balance {
impl Balance {
pub fn new(pair: SymbolPair, base_price: f64, base_amount: f64, wallet: WalletKind) -> Self {
Balance { pair, base_price, base_amount, quote_equivalent: base_amount * base_price, wallet }
Balance {
pair,
base_price,
base_amount,
quote_equivalent: base_amount * base_price,
wallet,
}
}
pub fn pair(&self) -> &SymbolPair {
@ -123,7 +157,10 @@ struct BalanceGroup {
impl BalanceGroup {
pub fn new() -> Self {
BalanceGroup { balances: Vec::new(), quote_equivalent: 0f64 }
BalanceGroup {
balances: Vec::new(),
quote_equivalent: 0f64,
}
}
pub fn add_balance(&mut self, balance: &Balance) {
@ -133,7 +170,8 @@ impl BalanceGroup {
}
pub fn currency_names(&self) -> Vec<String> {
self.balances.iter()
self.balances
.iter()
.map(|x| x.pair().base().name().into())
.collect()
}
@ -142,6 +180,3 @@ impl BalanceGroup {
&self.balances
}
}

View File

@ -5,7 +5,7 @@ use tokio::stream::StreamExt;
use tokio::task::JoinHandle;
use crate::pairs::PairStatus;
use crate::positions::{Position, PositionState};
use crate::positions::{Position, PositionProfitState, PositionState};
use crate::BoxError;
#[derive(Copy, Clone)]
@ -78,20 +78,20 @@ 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(&Position, &PairStatus) -> JoinHandle<()>>>>,
profit_state_handlers:
HashMap<PositionProfitState, 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(&Position, &PairStatus) -> JoinHandle<()>>>,
on_any_profit_state_handlers: Vec<Box<dyn Fn(&Position, &PairStatus) -> JoinHandle<()>>>,
}
impl EventDispatcher {
pub fn new() -> Self {
EventDispatcher {
event_handlers: HashMap::new(),
position_state_handlers: HashMap::new(),
profit_state_handlers: HashMap::new(),
on_any_event_handlers: Vec::new(),
on_any_position_state_handlers: Vec::new(),
on_any_profit_state_handlers: Vec::new(),
}
}
@ -108,13 +108,15 @@ impl EventDispatcher {
}
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);
if let Some(profit_state) = &position.profit_state() {
if let Some(functions) = self.profit_state_handlers.get(profit_state) {
for f in functions {
f(position, status);
}
}
}
for f in &self.on_any_position_state_handlers {
for f in &self.on_any_profit_state_handlers {
f(position, status);
}
}
@ -138,18 +140,18 @@ impl EventDispatcher {
pub fn register_positionstate_handler<F: 'static, Fut: 'static>(
&mut self,
state: PositionState,
state: PositionProfitState,
f: F,
) where
F: Fn(&Position, &PairStatus) -> Fut,
Fut: Future<Output = ()> + Send,
{
match state {
PositionState::Any => self
.on_any_position_state_handlers
.push(Box::new(move |p, s| tokio::spawn(f(&p, s)))),
// PositionProfitState::Any => self
// .on_any_position_state_handlers
// .push(Box::new(move |p, s| tokio::spawn(f(&p, s)))),
_ => self
.position_state_handlers
.profit_state_handlers
.entry(state)
.or_default()
.push(Box::new(move |p, s| tokio::spawn(f(&p, s)))),

View File

@ -4,6 +4,7 @@ use crate::currency::{Symbol, SymbolPair};
pub struct Position {
pair: SymbolPair,
state: PositionState,
profit_state: Option<PositionProfitState>,
amount: f64,
base_price: f64,
pl: f64,
@ -36,12 +37,22 @@ impl Position {
position_id,
creation_date: None,
creation_update: None,
profit_state: None,
}
}
pub fn with_creation_date(mut self, creation_date: u64) -> Self {
self.creation_date = Some(creation_date);
self.creation_update = Some(creation_date);
pub fn with_creation_date(mut self, creation_date: Option<u64>) -> Self {
self.creation_date = creation_date;
self
}
pub fn with_creation_update(mut self, creation_update: Option<u64>) -> Self {
self.creation_update = creation_update;
self
}
pub fn with_profit_state(mut self, profit_state: Option<PositionProfitState>) -> Self {
self.profit_state = profit_state;
self
}
@ -69,6 +80,9 @@ impl Position {
pub fn position_id(&self) -> u64 {
self.position_id
}
pub fn profit_state(&self) -> Option<PositionProfitState> {
self.profit_state
}
pub fn creation_date(&self) -> Option<u64> {
self.creation_date
}
@ -78,25 +92,27 @@ impl Position {
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum PositionState {
pub enum PositionProfitState {
Critical,
Loss,
BreakEven,
MinimumProfit,
Profit,
Closed,
Any,
}
impl PositionState {
impl PositionProfitState {
fn color(self) -> String {
match self {
PositionState::Any => "blue",
PositionState::Critical | PositionState::Loss => "red",
PositionState::BreakEven => "yellow",
PositionState::MinimumProfit | PositionState::Profit => "green",
PositionState::Closed => "gray",
PositionProfitState::Critical | PositionProfitState::Loss => "red",
PositionProfitState::BreakEven => "yellow",
PositionProfitState::MinimumProfit | PositionProfitState::Profit => "green",
}
.into()
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum PositionState {
Closed,
Open,
}

View File

@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::events::{Event, EventKind, EventMetadata, SignalKind};
use crate::pairs::PairStatus;
use crate::positions::{Position, PositionState};
use crate::positions::{Position, PositionProfitState, PositionState};
pub trait Strategy {
fn position_on_new_tick(
@ -47,18 +47,18 @@ impl Strategy for TrailingStop {
let state = {
if pl_perc > TrailingStop::GOOD_PROFIT_PERC {
PositionState::Profit
PositionProfitState::Profit
} else if TrailingStop::MIN_PROFIT_PERC <= pl_perc
&& pl_perc < TrailingStop::GOOD_PROFIT_PERC
{
PositionState::MinimumProfit
PositionProfitState::MinimumProfit
} else if 0.0 <= pl_perc && pl_perc < TrailingStop::MIN_PROFIT_PERC {
PositionState::BreakEven
PositionProfitState::BreakEven
} else if TrailingStop::MAX_LOSS_PERC < pl_perc && pl_perc < 0.0 {
PositionState::Loss
PositionProfitState::Loss
} else {
signals.push(SignalKind::ClosePosition);
PositionState::Critical
PositionProfitState::Critical
}
};
@ -69,7 +69,7 @@ impl Strategy for TrailingStop {
match opt_pre_pw {
Some(prev) => {
if prev.state() == state {
if prev.profit_state() == Some(state) {
return (pw, events, signals);
}
}
@ -79,25 +79,25 @@ impl Strategy for TrailingStop {
let events = {
let mut events = vec![];
if state == PositionState::Profit {
if state == PositionProfitState::Profit {
events.push(Event::new(
EventKind::ReachedGoodProfit,
status.current_tick(),
Some(event_metadata),
));
} else if state == PositionState::MinimumProfit {
} else if state == PositionProfitState::MinimumProfit {
events.push(Event::new(
EventKind::ReachedMinProfit,
status.current_tick(),
Some(event_metadata),
));
} else if state == PositionState::BreakEven {
} else if state == PositionProfitState::BreakEven {
events.push(Event::new(
EventKind::ReachedBreakEven,
status.current_tick(),
Some(event_metadata),
));
} else if state == PositionState::Loss {
} else if state == PositionProfitState::Loss {
events.push(Event::new(
EventKind::ReachedLoss,
status.current_tick(),