rust #10
@ -4,7 +4,7 @@ use std::future::Future;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use crate::managers::{OptionUpdate, OrderManager, PositionManager, PriceManager};
|
||||
use crate::models::{Position, PositionProfitState};
|
||||
use crate::models::{OrderKind, Position, PositionProfitState};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -15,8 +15,13 @@ pub struct ActorMessage {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
Update { tick: u64 },
|
||||
ClosePosition { position: Position },
|
||||
Update {
|
||||
tick: u64,
|
||||
},
|
||||
ClosePosition {
|
||||
position: Position,
|
||||
order_kind: OrderKind,
|
||||
},
|
||||
OpenPosition,
|
||||
}
|
||||
|
||||
|
@ -353,12 +353,19 @@ impl OrderManagerHandle {
|
||||
Ok(recv.await?)
|
||||
}
|
||||
|
||||
pub async fn close_position(&mut self, position: Position) -> Result<OptionUpdate, BoxError> {
|
||||
pub async fn close_position(
|
||||
&mut self,
|
||||
position: Position,
|
||||
order_kind: OrderKind,
|
||||
) -> Result<OptionUpdate, BoxError> {
|
||||
let (send, recv) = oneshot::channel();
|
||||
|
||||
self.sender
|
||||
.send(ActorMessage {
|
||||
message: Message::ClosePosition { position },
|
||||
message: Message::ClosePosition {
|
||||
position,
|
||||
order_kind,
|
||||
},
|
||||
respond_to: send,
|
||||
})
|
||||
.await?;
|
||||
@ -398,7 +405,10 @@ impl OrderManager {
|
||||
Message::Update { .. } => {
|
||||
self.update();
|
||||
}
|
||||
Message::ClosePosition { position, .. } => self.close_position(&position).await?,
|
||||
Message::ClosePosition {
|
||||
position,
|
||||
order_kind,
|
||||
} => self.close_position(&position, &order_kind).await?,
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@ -407,87 +417,177 @@ impl OrderManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn close_position(&mut self, position: &Position) -> Result<(), BoxError> {
|
||||
pub async fn close_position(
|
||||
&mut self,
|
||||
position: &Position,
|
||||
order_kind: &OrderKind,
|
||||
) -> Result<(), BoxError> {
|
||||
let open_order = self.tracked_positions.get(&position.id());
|
||||
|
||||
info!("Closing position #{}", position.id());
|
||||
debug!("Closing position #{}", position.id());
|
||||
|
||||
debug!("Getting current prices...");
|
||||
let order_book = self.client.order_book(&self.pair).await?;
|
||||
|
||||
// checking if the position has an open order.
|
||||
// If so, the strategy method is called, otherwise we open
|
||||
// an undercut limit order at the best current price.
|
||||
match open_order {
|
||||
Some(open_order) => {
|
||||
info!("There is an open order. Calling strategy.");
|
||||
debug!("There is an open order. Calling strategy.");
|
||||
let (_, messages) =
|
||||
self.strategy
|
||||
.on_position_close(open_order, position, &order_book)?;
|
||||
|
||||
self.tracked_positions = self
|
||||
.strategy
|
||||
.on_position_close(open_order, &self.tracked_positions);
|
||||
if let Some(messages) = messages {
|
||||
for m in messages {
|
||||
match m {
|
||||
Message::ClosePosition {
|
||||
position,
|
||||
order_kind,
|
||||
} => {
|
||||
// TODO FIXME
|
||||
match order_kind {
|
||||
OrderKind::Market => {
|
||||
self.submit_market_order(1.0, position.amount().neg())
|
||||
.await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
debug!("Received unsupported message from order strategy. Unimplemented.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
info!("Getting current prices...");
|
||||
let order_book = self.client.order_book(&self.pair).await?;
|
||||
let closing_price = self.best_closing_price(&position, &order_book);
|
||||
|
||||
info!("Calculating best closing price...");
|
||||
let closing_price = self.best_closing_price(&position, &order_book)?;
|
||||
|
||||
info!("Submitting order...");
|
||||
// submitting order
|
||||
let order_form = OrderForm::new(
|
||||
&self.pair,
|
||||
closing_price,
|
||||
position.amount().neg(),
|
||||
OrderKind::Limit,
|
||||
);
|
||||
|
||||
match self.client.submit_order(order_form).await {
|
||||
Err(e) => error!("Could not submit order: {}", e),
|
||||
Ok(o) => {
|
||||
self.tracked_positions.insert(position.id(), o);
|
||||
info!("Done!");
|
||||
let active_order = match order_kind {
|
||||
OrderKind::Limit => {
|
||||
self.submit_limit_order(closing_price, position.amount().neg())
|
||||
.await?
|
||||
}
|
||||
OrderKind::ExchangeLimit => {
|
||||
self.submit_exchange_limit_order(closing_price, position.amount().neg())
|
||||
.await?
|
||||
}
|
||||
OrderKind::Market => {
|
||||
self.submit_market_order(closing_price, position.amount().neg())
|
||||
.await?
|
||||
}
|
||||
OrderKind::ExchangeMarket => {
|
||||
self.submit_exchange_market_order(closing_price, position.amount().neg())
|
||||
.await?
|
||||
}
|
||||
_ => {
|
||||
unimplemented!()
|
||||
}
|
||||
};
|
||||
|
||||
self.tracked_positions.insert(position.id(), active_order);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn submit_limit_order(
|
||||
&mut self,
|
||||
closing_price: f64,
|
||||
amount: f64,
|
||||
) -> Result<ActiveOrder, BoxError> {
|
||||
info!(
|
||||
"Submitting exchange limit order of {} {}...",
|
||||
amount,
|
||||
self.pair.base()
|
||||
);
|
||||
let order_form = OrderForm::new(&self.pair, closing_price, amount, OrderKind::Limit);
|
||||
|
||||
Ok(self.client.submit_order(order_form).await?)
|
||||
}
|
||||
|
||||
pub async fn submit_exchange_limit_order(
|
||||
&mut self,
|
||||
closing_price: f64,
|
||||
amount: f64,
|
||||
) -> Result<ActiveOrder, BoxError> {
|
||||
info!(
|
||||
"Submitting exchange limit order of {} {}...",
|
||||
amount,
|
||||
self.pair.base()
|
||||
);
|
||||
let order_form =
|
||||
OrderForm::new(&self.pair, closing_price, amount, OrderKind::ExchangeLimit);
|
||||
|
||||
Ok(self.client.submit_order(order_form).await?)
|
||||
}
|
||||
|
||||
pub async fn submit_market_order(
|
||||
&mut self,
|
||||
closing_price: f64,
|
||||
amount: f64,
|
||||
) -> Result<ActiveOrder, BoxError> {
|
||||
info!(
|
||||
"Submitting market order of {} {}...",
|
||||
amount,
|
||||
self.pair.base()
|
||||
);
|
||||
let order_form = OrderForm::new(&self.pair, closing_price, amount, OrderKind::Market);
|
||||
|
||||
Ok(self.client.submit_order(order_form).await?)
|
||||
}
|
||||
|
||||
pub async fn submit_exchange_market_order(
|
||||
&mut self,
|
||||
closing_price: f64,
|
||||
amount: f64,
|
||||
) -> Result<ActiveOrder, BoxError> {
|
||||
info!(
|
||||
"Submitting market order of {} {}...",
|
||||
amount,
|
||||
self.pair.base()
|
||||
);
|
||||
let order_form =
|
||||
OrderForm::new(&self.pair, closing_price, amount, OrderKind::ExchangeMarket);
|
||||
|
||||
Ok(self.client.submit_order(order_form).await?)
|
||||
}
|
||||
|
||||
pub fn update(&self) -> Result<OptionUpdate, BoxError> {
|
||||
// TODO: implement me
|
||||
Ok((None, None))
|
||||
}
|
||||
|
||||
pub fn best_closing_price(
|
||||
&self,
|
||||
position: &Position,
|
||||
order_book: &OrderBook,
|
||||
) -> Result<f64, BoxError> {
|
||||
pub fn best_closing_price(&self, position: &Position, order_book: &OrderBook) -> f64 {
|
||||
let ask = order_book.lowest_ask();
|
||||
let bid = order_book.highest_bid();
|
||||
let avg = (bid + ask) / 2.0;
|
||||
let delta = (ask - bid) / 10.0;
|
||||
|
||||
let price = {
|
||||
let intermediate_price = {
|
||||
let closing_price = {
|
||||
let closing_price = {
|
||||
if position.is_short() {
|
||||
bid + delta
|
||||
bid - delta
|
||||
} else {
|
||||
ask - delta
|
||||
ask + delta
|
||||
}
|
||||
};
|
||||
|
||||
if avg > 9999.0 {
|
||||
if position.is_short() {
|
||||
intermediate_price.ceil()
|
||||
closing_price.ceil()
|
||||
} else {
|
||||
intermediate_price.floor()
|
||||
closing_price.floor()
|
||||
}
|
||||
} else {
|
||||
intermediate_price
|
||||
closing_price
|
||||
}
|
||||
};
|
||||
|
||||
Ok(price)
|
||||
closing_price
|
||||
}
|
||||
}
|
||||
|
||||
@ -496,7 +596,6 @@ pub struct PairManager {
|
||||
price_manager: PriceManagerHandle,
|
||||
order_manager: OrderManagerHandle,
|
||||
position_manager: PositionManagerHandle,
|
||||
// dispatcher: Dispatcher,
|
||||
}
|
||||
|
||||
impl PairManager {
|
||||
@ -507,7 +606,7 @@ impl PairManager {
|
||||
order_manager: OrderManagerHandle::new(
|
||||
pair.clone(),
|
||||
client.clone(),
|
||||
Box::new(FastOrderStrategy {}),
|
||||
Box::new(FastOrderStrategy::default()),
|
||||
),
|
||||
position_manager: PositionManagerHandle::new(
|
||||
pair.clone(),
|
||||
@ -527,8 +626,13 @@ impl PairManager {
|
||||
if let Some(messages) = opt_pos_messages {
|
||||
for m in messages {
|
||||
match m {
|
||||
Message::ClosePosition { position } => {
|
||||
self.order_manager.close_position(position).await?;
|
||||
Message::ClosePosition {
|
||||
position,
|
||||
order_kind,
|
||||
} => {
|
||||
self.order_manager
|
||||
.close_position(position, order_kind)
|
||||
.await?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -1,12 +1,16 @@
|
||||
use dyn_clone::DynClone;
|
||||
use log::debug;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
use log::{debug, info};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use crate::events::{Event, EventKind, EventMetadata, Message};
|
||||
use crate::managers::{OrderManager, PositionManager, TrackedPositionsMap};
|
||||
use crate::models::{ActiveOrder, OrderForm, Position, PositionProfitState};
|
||||
use tokio::sync::oneshot;
|
||||
use crate::models::{
|
||||
ActiveOrder, OrderBook, OrderBookEntry, OrderForm, OrderKind, Position, PositionProfitState,
|
||||
};
|
||||
use crate::BoxError;
|
||||
|
||||
/***************
|
||||
* DEFINITIONS
|
||||
@ -38,8 +42,9 @@ pub trait OrderStrategy: DynClone + Send {
|
||||
fn on_position_close(
|
||||
&self,
|
||||
order: &ActiveOrder,
|
||||
tracked_positions: &HashMap<u64, ActiveOrder>,
|
||||
) -> TrackedPositionsMap;
|
||||
open_position: &Position,
|
||||
order_book: &OrderBook,
|
||||
) -> Result<(Option<Vec<Event>>, Option<Vec<Message>>), BoxError>;
|
||||
}
|
||||
|
||||
impl Debug for dyn OrderStrategy {
|
||||
@ -106,6 +111,7 @@ impl PositionStrategy for TrailingStop {
|
||||
debug!("Inserting close position message...");
|
||||
messages.push(Message::ClosePosition {
|
||||
position: position.clone(),
|
||||
order_kind: OrderKind::Limit,
|
||||
});
|
||||
PositionProfitState::Critical
|
||||
}
|
||||
@ -181,7 +187,23 @@ impl PositionStrategy for TrailingStop {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FastOrderStrategy {}
|
||||
pub struct FastOrderStrategy {
|
||||
// threshold (%) for which we trigger a market order
|
||||
// to close an open position
|
||||
threshold: f64,
|
||||
}
|
||||
|
||||
impl Default for FastOrderStrategy {
|
||||
fn default() -> Self {
|
||||
Self { threshold: 0.2 }
|
||||
}
|
||||
}
|
||||
|
||||
impl FastOrderStrategy {
|
||||
pub fn new(threshold: f64) -> Self {
|
||||
Self { threshold }
|
||||
}
|
||||
}
|
||||
|
||||
impl OrderStrategy for FastOrderStrategy {
|
||||
fn name(&self) -> String {
|
||||
@ -195,8 +217,36 @@ impl OrderStrategy for FastOrderStrategy {
|
||||
fn on_position_close(
|
||||
&self,
|
||||
order: &ActiveOrder,
|
||||
tracked_positions: &HashMap<u64, ActiveOrder>,
|
||||
) -> TrackedPositionsMap {
|
||||
unimplemented!()
|
||||
active_position: &Position,
|
||||
order_book: &OrderBook,
|
||||
) -> Result<(Option<Vec<Event>>, Option<Vec<Message>>), BoxError> {
|
||||
let mut messages = vec![];
|
||||
|
||||
// long
|
||||
let offer_comparison = {
|
||||
if order.amount > 0.0 {
|
||||
order_book.highest_bid()
|
||||
} else {
|
||||
order_book.lowest_ask()
|
||||
}
|
||||
};
|
||||
|
||||
// if the best offer is higher than our threshold,
|
||||
// ask the manager to close the position with a market order
|
||||
let delta = (1.0 - (offer_comparison / order.price)) * 100.0;
|
||||
|
||||
debug!(
|
||||
"Offer comp: {} | Our offer: {} | Current delta: {}",
|
||||
offer_comparison, order.price, delta
|
||||
);
|
||||
|
||||
if delta > self.threshold {
|
||||
messages.push(Message::ClosePosition {
|
||||
position: active_position.clone(),
|
||||
order_kind: OrderKind::Market,
|
||||
})
|
||||
}
|
||||
|
||||
Ok((None, (!messages.is_empty()).then_some(messages)))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user