other traits and shit
This commit is contained in:
parent
78b57b3899
commit
ae648b70a4
37
rustybot/Cargo.lock
generated
37
rustybot/Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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)))),
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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(),
|
||||
|
Loading…
Reference in New Issue
Block a user