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