internal order modeling overhaul
This commit is contained in:
parent
a1d905ebea
commit
5b84c99703
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
|||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use tokio::time::delay_for;
|
use tokio::time::delay_for;
|
||||||
|
|
||||||
use crate::connectors::{Client, ExchangeKind};
|
use crate::connectors::{Client, ExchangeDetails};
|
||||||
use crate::currency::{Symbol, SymbolPair};
|
use crate::currency::{Symbol, SymbolPair};
|
||||||
use crate::events::Event;
|
use crate::events::Event;
|
||||||
use crate::managers::{ExchangeManager, OrderManager, PositionManager, PriceManager};
|
use crate::managers::{ExchangeManager, OrderManager, PositionManager, PriceManager};
|
||||||
@ -21,7 +21,7 @@ pub struct BfxBot {
|
|||||||
|
|
||||||
impl BfxBot {
|
impl BfxBot {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
exchanges: Vec<ExchangeKind>,
|
exchanges: Vec<ExchangeDetails>,
|
||||||
trading_symbols: Vec<Symbol>,
|
trading_symbols: Vec<Symbol>,
|
||||||
quote: Symbol,
|
quote: Symbol,
|
||||||
tick_duration: Duration,
|
tick_duration: Duration,
|
||||||
|
@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bitfinex::api::Bitfinex;
|
use bitfinex::api::Bitfinex;
|
||||||
use bitfinex::orders::{OrderMeta, OrderResponse};
|
use bitfinex::orders::{CancelOrderForm, OrderMeta, OrderResponse};
|
||||||
use bitfinex::ticker::TradingPairTicker;
|
use bitfinex::ticker::TradingPairTicker;
|
||||||
use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
|
use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
@ -13,12 +13,17 @@ use log::debug;
|
|||||||
use crate::currency::SymbolPair;
|
use crate::currency::SymbolPair;
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
ActiveOrder, OrderBook, OrderBookEntry, OrderForm, OrderKind, Position, PositionState,
|
ActiveOrder, OrderBook, OrderBookEntry, OrderForm, OrderKind, Position, PositionState,
|
||||||
PriceTicker,
|
PriceTicker, TradingPlatform,
|
||||||
};
|
};
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
|
pub enum Exchange {
|
||||||
|
Bitfinex,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
|
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
|
||||||
pub enum ExchangeKind {
|
pub enum ExchangeDetails {
|
||||||
Bitfinex { api_key: String, api_secret: String },
|
Bitfinex { api_key: String, api_secret: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,14 +31,14 @@ pub enum ExchangeKind {
|
|||||||
/// because it already uses an [`Arc`] internally.
|
/// because it already uses an [`Arc`] internally.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Client {
|
pub struct Client {
|
||||||
exchange: ExchangeKind,
|
exchange: ExchangeDetails,
|
||||||
inner: Arc<Box<dyn Connector>>,
|
inner: Arc<Box<dyn Connector>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
pub fn new(exchange: &ExchangeKind) -> Self {
|
pub fn new(exchange: &ExchangeDetails) -> Self {
|
||||||
let inner = match &exchange {
|
let inner = match &exchange {
|
||||||
ExchangeKind::Bitfinex {
|
ExchangeDetails::Bitfinex {
|
||||||
api_key,
|
api_key,
|
||||||
api_secret,
|
api_secret,
|
||||||
} => BitfinexConnector::new(&api_key, &api_secret),
|
} => BitfinexConnector::new(&api_key, &api_secret),
|
||||||
@ -60,7 +65,7 @@ impl Client {
|
|||||||
self.inner.active_orders(pair).await
|
self.inner.active_orders(pair).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn submit_order(&self, order: OrderForm) -> Result<ActiveOrder, BoxError> {
|
pub async fn submit_order(&self, order: &OrderForm) -> Result<ActiveOrder, BoxError> {
|
||||||
self.inner.submit_order(order).await
|
self.inner.submit_order(order).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +85,7 @@ pub trait Connector: Send + Sync {
|
|||||||
async fn current_prices(&self, pair: &SymbolPair) -> Result<TradingPairTicker, BoxError>;
|
async fn current_prices(&self, pair: &SymbolPair) -> Result<TradingPairTicker, BoxError>;
|
||||||
async fn order_book(&self, pair: &SymbolPair) -> Result<OrderBook, BoxError>;
|
async fn order_book(&self, pair: &SymbolPair) -> Result<OrderBook, BoxError>;
|
||||||
async fn active_orders(&self, pair: &SymbolPair) -> Result<Vec<ActiveOrder>, BoxError>;
|
async fn active_orders(&self, pair: &SymbolPair) -> Result<Vec<ActiveOrder>, BoxError>;
|
||||||
async fn submit_order(&self, order: OrderForm) -> Result<ActiveOrder, BoxError>;
|
async fn submit_order(&self, order: &OrderForm) -> Result<ActiveOrder, BoxError>;
|
||||||
async fn cancel_order(&self, order: &ActiveOrder) -> Result<ActiveOrder, BoxError>;
|
async fn cancel_order(&self, order: &ActiveOrder) -> Result<ActiveOrder, BoxError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +116,7 @@ impl BitfinexConnector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_trading_pair(&self, pair: &SymbolPair) -> String {
|
fn format_trading_pair(pair: &SymbolPair) -> String {
|
||||||
if pair.to_string().to_lowercase().contains("test") {
|
if pair.to_string().to_lowercase().contains("test") {
|
||||||
format!("{}:{}", pair.base(), pair.quote())
|
format!("{}:{}", pair.base(), pair.quote())
|
||||||
} else {
|
} else {
|
||||||
@ -143,11 +148,9 @@ impl Connector for BitfinexConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn current_prices(&self, pair: &SymbolPair) -> Result<TradingPairTicker, BoxError> {
|
async fn current_prices(&self, pair: &SymbolPair) -> Result<TradingPairTicker, BoxError> {
|
||||||
let ticker: TradingPairTicker = self
|
let symbol_name = BitfinexConnector::format_trading_pair(pair);
|
||||||
.bfx
|
|
||||||
.ticker
|
let ticker: TradingPairTicker = self.bfx.ticker.trading_pair(symbol_name).await?;
|
||||||
.trading_pair(self.format_trading_pair(pair))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(ticker)
|
Ok(ticker)
|
||||||
}
|
}
|
||||||
@ -156,37 +159,52 @@ impl Connector for BitfinexConnector {
|
|||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn submit_order(&self, order: OrderForm) -> Result<ActiveOrder, BoxError> {
|
async fn submit_order(&self, order: &OrderForm) -> Result<ActiveOrder, BoxError> {
|
||||||
// TODO: change trading pair formatting. awful.
|
let symbol_name = format!("t{}", BitfinexConnector::format_trading_pair(order.pair()));
|
||||||
let order_form = match &self.affiliate_code {
|
|
||||||
Some(affiliate_code) => bitfinex::orders::OrderForm::new(
|
let order_form = match order.kind() {
|
||||||
format!("t{}", self.format_trading_pair(order.pair())),
|
OrderKind::Limit { price, amount } => {
|
||||||
*order.price(),
|
bitfinex::orders::OrderForm::new(symbol_name, price, amount, order.into())
|
||||||
*order.amount(),
|
}
|
||||||
order.kind().into(),
|
OrderKind::Market { amount } => {
|
||||||
)
|
bitfinex::orders::OrderForm::new(symbol_name, 0.0, amount, order.into())
|
||||||
.with_meta(OrderMeta::new(affiliate_code.clone())),
|
}
|
||||||
None => bitfinex::orders::OrderForm::new(
|
OrderKind::Stop { price, amount } => {
|
||||||
format!("t{}", self.format_trading_pair(order.pair())),
|
bitfinex::orders::OrderForm::new(symbol_name, price, amount, order.into())
|
||||||
*order.price(),
|
}
|
||||||
*order.amount(),
|
OrderKind::StopLimit {
|
||||||
order.kind().into(),
|
price,
|
||||||
),
|
amount,
|
||||||
};
|
limit_price,
|
||||||
|
} => bitfinex::orders::OrderForm::new(symbol_name, price, amount, order.into())
|
||||||
|
.with_price_aux_limit(limit_price)?,
|
||||||
|
OrderKind::TrailingStop { distance, amount } => {
|
||||||
|
bitfinex::orders::OrderForm::new(symbol_name, 0.0, amount, order.into())
|
||||||
|
.with_price_trailing(distance)?
|
||||||
|
}
|
||||||
|
OrderKind::FillOrKill { price, amount } => {
|
||||||
|
bitfinex::orders::OrderForm::new(symbol_name, price, amount, order.into())
|
||||||
|
}
|
||||||
|
OrderKind::ImmediateOrCancel { price, amount } => {
|
||||||
|
bitfinex::orders::OrderForm::new(symbol_name, price, amount, order.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.with_meta(OrderMeta::new(
|
||||||
|
BitfinexConnector::AFFILIATE_CODE.to_string(),
|
||||||
|
));
|
||||||
|
|
||||||
let response = self.bfx.orders.submit_order(&order_form).await?;
|
let response = self.bfx.orders.submit_order(&order_form).await?;
|
||||||
|
|
||||||
Ok(response.try_into()?)
|
Ok((&response).try_into()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn order_book(&self, pair: &SymbolPair) -> Result<OrderBook, BoxError> {
|
async fn order_book(&self, pair: &SymbolPair) -> Result<OrderBook, BoxError> {
|
||||||
|
let symbol_name = BitfinexConnector::format_trading_pair(pair);
|
||||||
|
|
||||||
let x = self
|
let x = self
|
||||||
.bfx
|
.bfx
|
||||||
.book
|
.book
|
||||||
.trading_pair(
|
.trading_pair(symbol_name, bitfinex::book::BookPrecision::P0)
|
||||||
self.format_trading_pair(&pair),
|
|
||||||
bitfinex::book::BookPrecision::P0,
|
|
||||||
)
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let entries = x
|
let entries = x
|
||||||
@ -202,39 +220,35 @@ impl Connector for BitfinexConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn cancel_order(&self, order: &ActiveOrder) -> Result<ActiveOrder, BoxError> {
|
async fn cancel_order(&self, order: &ActiveOrder) -> Result<ActiveOrder, BoxError> {
|
||||||
let date = DateTime::<Utc>::from_utc(
|
let cancel_form = order.into();
|
||||||
NaiveDateTime::from_timestamp(order.update_timestamp as i64, 0),
|
|
||||||
Utc,
|
|
||||||
);
|
|
||||||
let cancel_form = bitfinex::orders::CancelOrderForm::new(order.id, order.client_id, date);
|
|
||||||
|
|
||||||
Ok(self
|
Ok((&self.bfx.orders.cancel_order(&cancel_form).await?).try_into()?)
|
||||||
.bfx
|
|
||||||
.orders
|
|
||||||
.cancel_order(&cancel_form)
|
|
||||||
.await?
|
|
||||||
.try_into()?)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<bitfinex::orders::OrderResponse> for ActiveOrder {
|
impl From<&ActiveOrder> for CancelOrderForm {
|
||||||
|
fn from(o: &ActiveOrder) -> Self {
|
||||||
|
Self::from_id(o.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&bitfinex::orders::OrderResponse> for ActiveOrder {
|
||||||
type Error = BoxError;
|
type Error = BoxError;
|
||||||
|
|
||||||
fn try_from(response: OrderResponse) -> Result<Self, Self::Error> {
|
fn try_from(response: &OrderResponse) -> Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
exchange: Exchange::Bitfinex,
|
||||||
id: response.id(),
|
id: response.id(),
|
||||||
group_id: response.gid(),
|
group_id: response.gid(),
|
||||||
client_id: response.cid(),
|
client_id: Some(response.cid()),
|
||||||
symbol: SymbolPair::from_str(response.symbol())?,
|
symbol: SymbolPair::from_str(response.symbol())?,
|
||||||
creation_timestamp: response.mts_create(),
|
current_form: OrderForm::new(
|
||||||
update_timestamp: response.mts_update(),
|
SymbolPair::from_str(response.symbol())?,
|
||||||
amount: response.amount(),
|
response.into(),
|
||||||
amount_original: response.amount_orig(),
|
response.into(),
|
||||||
order_type: (&response.order_type()).into(),
|
),
|
||||||
previous_order_type: response.prev_order_type().map(|x| (&x).into()),
|
creation_timestamp: 0,
|
||||||
price: response.price(),
|
update_timestamp: 0,
|
||||||
price_avg: response.price_avg(),
|
|
||||||
hidden: response.hidden(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,62 +280,90 @@ impl TryInto<Position> for bitfinex::positions::Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&OrderKind> for bitfinex::orders::OrderKind {
|
impl From<&OrderForm> for bitfinex::orders::OrderKind {
|
||||||
fn from(o: &OrderKind) -> Self {
|
fn from(o: &OrderForm) -> Self {
|
||||||
match o {
|
match o.platform() {
|
||||||
OrderKind::Limit => Self::Limit,
|
TradingPlatform::Exchange => match o.kind() {
|
||||||
OrderKind::ExchangeLimit => Self::ExchangeLimit,
|
OrderKind::Limit { .. } => bitfinex::orders::OrderKind::ExchangeLimit,
|
||||||
OrderKind::Market => Self::Market,
|
OrderKind::Market { .. } => bitfinex::orders::OrderKind::ExchangeMarket,
|
||||||
OrderKind::ExchangeMarket => Self::ExchangeMarket,
|
OrderKind::Stop { .. } => bitfinex::orders::OrderKind::ExchangeStop,
|
||||||
OrderKind::Stop => Self::Stop,
|
OrderKind::StopLimit { .. } => bitfinex::orders::OrderKind::ExchangeStopLimit,
|
||||||
OrderKind::ExchangeStop => Self::ExchangeStop,
|
OrderKind::TrailingStop { .. } => bitfinex::orders::OrderKind::ExchangeTrailingStop,
|
||||||
OrderKind::StopLimit => Self::StopLimit,
|
OrderKind::FillOrKill { .. } => bitfinex::orders::OrderKind::ExchangeFok,
|
||||||
OrderKind::ExchangeStopLimit => Self::ExchangeStopLimit,
|
OrderKind::ImmediateOrCancel { .. } => bitfinex::orders::OrderKind::ExchangeIoc,
|
||||||
OrderKind::TrailingStop => Self::TrailingStop,
|
},
|
||||||
OrderKind::Fok => Self::Fok,
|
TradingPlatform::Margin => match o.kind() {
|
||||||
OrderKind::ExchangeFok => Self::ExchangeFok,
|
OrderKind::Limit { .. } => bitfinex::orders::OrderKind::Limit,
|
||||||
OrderKind::Ioc => Self::Ioc,
|
OrderKind::Market { .. } => bitfinex::orders::OrderKind::Market,
|
||||||
OrderKind::ExchangeIoc => Self::ExchangeIoc,
|
OrderKind::Stop { .. } => bitfinex::orders::OrderKind::Stop,
|
||||||
|
OrderKind::StopLimit { .. } => bitfinex::orders::OrderKind::StopLimit,
|
||||||
|
OrderKind::TrailingStop { .. } => bitfinex::orders::OrderKind::TrailingStop,
|
||||||
|
OrderKind::FillOrKill { .. } => bitfinex::orders::OrderKind::Fok,
|
||||||
|
OrderKind::ImmediateOrCancel { .. } => bitfinex::orders::OrderKind::Ioc,
|
||||||
|
},
|
||||||
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&bitfinex::orders::OrderKind> for OrderKind {
|
impl From<&bitfinex::orders::OrderResponse> for TradingPlatform {
|
||||||
fn from(o: &bitfinex::orders::OrderKind) -> Self {
|
fn from(response: &OrderResponse) -> Self {
|
||||||
match o {
|
match response.order_type() {
|
||||||
bitfinex::orders::OrderKind::Limit => OrderKind::Limit,
|
bitfinex::orders::OrderKind::Limit
|
||||||
bitfinex::orders::OrderKind::ExchangeLimit => OrderKind::ExchangeLimit,
|
| bitfinex::orders::OrderKind::Market
|
||||||
bitfinex::orders::OrderKind::Market => OrderKind::Market,
|
| bitfinex::orders::OrderKind::StopLimit
|
||||||
bitfinex::orders::OrderKind::ExchangeMarket => OrderKind::ExchangeMarket,
|
| bitfinex::orders::OrderKind::Stop
|
||||||
bitfinex::orders::OrderKind::Stop => OrderKind::Stop,
|
| bitfinex::orders::OrderKind::TrailingStop
|
||||||
bitfinex::orders::OrderKind::ExchangeStop => OrderKind::ExchangeStop,
|
| bitfinex::orders::OrderKind::Fok
|
||||||
bitfinex::orders::OrderKind::StopLimit => OrderKind::StopLimit,
|
| bitfinex::orders::OrderKind::Ioc => Self::Margin,
|
||||||
bitfinex::orders::OrderKind::ExchangeStopLimit => OrderKind::ExchangeStopLimit,
|
_ => Self::Exchange,
|
||||||
bitfinex::orders::OrderKind::TrailingStop => OrderKind::TrailingStop,
|
|
||||||
bitfinex::orders::OrderKind::Fok => OrderKind::Fok,
|
|
||||||
bitfinex::orders::OrderKind::ExchangeFok => OrderKind::ExchangeFok,
|
|
||||||
bitfinex::orders::OrderKind::Ioc => OrderKind::Ioc,
|
|
||||||
bitfinex::orders::OrderKind::ExchangeIoc => OrderKind::ExchangeIoc,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<OrderKind> for bitfinex::orders::OrderKind {
|
impl From<&bitfinex::orders::OrderResponse> for OrderKind {
|
||||||
fn from(o: OrderKind) -> Self {
|
fn from(response: &OrderResponse) -> Self {
|
||||||
match o {
|
match response.order_type() {
|
||||||
OrderKind::Limit => Self::Limit,
|
bitfinex::orders::OrderKind::Limit | bitfinex::orders::OrderKind::ExchangeLimit => {
|
||||||
OrderKind::ExchangeLimit => Self::ExchangeLimit,
|
Self::Limit {
|
||||||
OrderKind::Market => Self::Market,
|
price: response.price(),
|
||||||
OrderKind::ExchangeMarket => Self::ExchangeMarket,
|
amount: response.amount(),
|
||||||
OrderKind::Stop => Self::Stop,
|
}
|
||||||
OrderKind::ExchangeStop => Self::ExchangeStop,
|
}
|
||||||
OrderKind::StopLimit => Self::StopLimit,
|
bitfinex::orders::OrderKind::Market | bitfinex::orders::OrderKind::ExchangeMarket => {
|
||||||
OrderKind::ExchangeStopLimit => Self::ExchangeStopLimit,
|
Self::Market {
|
||||||
OrderKind::TrailingStop => Self::TrailingStop,
|
amount: response.amount(),
|
||||||
OrderKind::Fok => Self::Fok,
|
}
|
||||||
OrderKind::ExchangeFok => Self::ExchangeFok,
|
}
|
||||||
OrderKind::Ioc => Self::Ioc,
|
bitfinex::orders::OrderKind::Stop | bitfinex::orders::OrderKind::ExchangeStop => {
|
||||||
OrderKind::ExchangeIoc => Self::ExchangeIoc,
|
Self::Stop {
|
||||||
|
price: response.price(),
|
||||||
|
amount: response.amount(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitfinex::orders::OrderKind::StopLimit
|
||||||
|
| bitfinex::orders::OrderKind::ExchangeStopLimit => Self::StopLimit {
|
||||||
|
price: response.price(),
|
||||||
|
amount: response.amount(),
|
||||||
|
limit_price: response.price_aux_limit(),
|
||||||
|
},
|
||||||
|
bitfinex::orders::OrderKind::TrailingStop
|
||||||
|
| bitfinex::orders::OrderKind::ExchangeTrailingStop => Self::TrailingStop {
|
||||||
|
distance: response.price_trailing(),
|
||||||
|
amount: response.amount(),
|
||||||
|
},
|
||||||
|
bitfinex::orders::OrderKind::Fok | bitfinex::orders::OrderKind::ExchangeFok => {
|
||||||
|
Self::FillOrKill {
|
||||||
|
price: response.price(),
|
||||||
|
amount: response.amount(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitfinex::orders::OrderKind::Ioc | bitfinex::orders::OrderKind::ExchangeIoc => {
|
||||||
|
Self::ImmediateOrCancel {
|
||||||
|
price: response.price(),
|
||||||
|
amount: response.amount(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use std::future::Future;
|
|||||||
use tokio::task::JoinHandle;
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
use crate::managers::{OptionUpdate, OrderManager, PositionManager, PriceManager};
|
use crate::managers::{OptionUpdate, OrderManager, PositionManager, PriceManager};
|
||||||
use crate::models::{OrderKind, Position, PositionProfitState};
|
use crate::models::{OrderForm, OrderKind, Position, PositionProfitState};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -20,7 +20,7 @@ pub enum Message {
|
|||||||
},
|
},
|
||||||
ClosePosition {
|
ClosePosition {
|
||||||
position: Position,
|
position: Position,
|
||||||
order_kind: OrderKind,
|
order_form: OrderForm,
|
||||||
},
|
},
|
||||||
OpenPosition,
|
OpenPosition,
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,12 @@ use tokio::sync::mpsc::channel;
|
|||||||
use tokio::sync::mpsc::{Receiver, Sender};
|
use tokio::sync::mpsc::{Receiver, Sender};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use crate::connectors::{Client, ExchangeKind};
|
use crate::connectors::{Client, ExchangeDetails};
|
||||||
use crate::currency::SymbolPair;
|
use crate::currency::SymbolPair;
|
||||||
use crate::events::{ActorMessage, Event, Message};
|
use crate::events::{ActorMessage, Event, Message};
|
||||||
use crate::models::{ActiveOrder, OrderBook, OrderForm, OrderKind, Position, PriceTicker};
|
use crate::models::{
|
||||||
|
ActiveOrder, OrderBook, OrderForm, OrderKind, Position, PriceTicker, TradingPlatform,
|
||||||
|
};
|
||||||
use crate::strategy::{FastOrderStrategy, OrderStrategy, PositionStrategy, TrailingStop};
|
use crate::strategy::{FastOrderStrategy, OrderStrategy, PositionStrategy, TrailingStop};
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
|
|
||||||
@ -356,7 +358,7 @@ impl OrderManagerHandle {
|
|||||||
pub async fn close_position(
|
pub async fn close_position(
|
||||||
&mut self,
|
&mut self,
|
||||||
position: Position,
|
position: Position,
|
||||||
order_kind: OrderKind,
|
order_form: OrderForm,
|
||||||
) -> Result<OptionUpdate, BoxError> {
|
) -> Result<OptionUpdate, BoxError> {
|
||||||
let (send, recv) = oneshot::channel();
|
let (send, recv) = oneshot::channel();
|
||||||
|
|
||||||
@ -364,7 +366,7 @@ impl OrderManagerHandle {
|
|||||||
.send(ActorMessage {
|
.send(ActorMessage {
|
||||||
message: Message::ClosePosition {
|
message: Message::ClosePosition {
|
||||||
position,
|
position,
|
||||||
order_kind,
|
order_form,
|
||||||
},
|
},
|
||||||
respond_to: send,
|
respond_to: send,
|
||||||
})
|
})
|
||||||
@ -407,8 +409,8 @@ impl OrderManager {
|
|||||||
}
|
}
|
||||||
Message::ClosePosition {
|
Message::ClosePosition {
|
||||||
position,
|
position,
|
||||||
order_kind,
|
order_form,
|
||||||
} => self.close_position(&position, &order_kind).await?,
|
} => self.close_position(&position, &order_form).await?,
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -420,12 +422,11 @@ impl OrderManager {
|
|||||||
pub async fn close_position(
|
pub async fn close_position(
|
||||||
&mut self,
|
&mut self,
|
||||||
position: &Position,
|
position: &Position,
|
||||||
order_kind: &OrderKind,
|
order_form: &OrderForm,
|
||||||
) -> Result<(), BoxError> {
|
) -> Result<(), BoxError> {
|
||||||
let open_order = self.tracked_positions.get(&position.id());
|
let open_order = self.tracked_positions.get(&position.id());
|
||||||
|
|
||||||
debug!("Closing position #{}", position.id());
|
debug!("Closing position #{}", position.id());
|
||||||
|
|
||||||
debug!("Getting current prices...");
|
debug!("Getting current prices...");
|
||||||
let order_book = self.client.order_book(&self.pair).await?;
|
let order_book = self.client.order_book(&self.pair).await?;
|
||||||
|
|
||||||
@ -442,26 +443,13 @@ impl OrderManager {
|
|||||||
if let Some(messages) = messages {
|
if let Some(messages) = messages {
|
||||||
for m in messages {
|
for m in messages {
|
||||||
match m {
|
match m {
|
||||||
Message::ClosePosition {
|
Message::ClosePosition { order_form, .. } => {
|
||||||
position,
|
info!("Closing open order with a {} order", order_form.kind());
|
||||||
order_kind,
|
if let Ok(_) = self.client.submit_order(&order_form).await {
|
||||||
} => {
|
|
||||||
// TODO FIXME
|
|
||||||
match order_kind {
|
|
||||||
OrderKind::Market => {
|
|
||||||
info!("Closing open order with a market order.");
|
|
||||||
if let Ok(_) = self
|
|
||||||
.submit_market_order(1.0, position.amount().neg())
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
// cancel active order
|
|
||||||
info!("Cancelling open order #{}", open_order.id);
|
info!("Cancelling open order #{}", open_order.id);
|
||||||
self.client.cancel_order(open_order).await?;
|
self.client.cancel_order(open_order).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
_ => {
|
||||||
debug!("Received unsupported message from order strategy. Unimplemented.")
|
debug!("Received unsupported message from order strategy. Unimplemented.")
|
||||||
}
|
}
|
||||||
@ -472,27 +460,18 @@ impl OrderManager {
|
|||||||
None => {
|
None => {
|
||||||
let closing_price = self.best_closing_price(&position, &order_book);
|
let closing_price = self.best_closing_price(&position, &order_book);
|
||||||
|
|
||||||
let active_order = match order_kind {
|
// TODO: hardocoded platform to Margin!
|
||||||
OrderKind::Limit => {
|
let order_form = OrderForm::new(
|
||||||
self.submit_limit_order(closing_price, position.amount().neg())
|
self.pair.clone(),
|
||||||
.await?
|
OrderKind::Limit {
|
||||||
}
|
price: closing_price,
|
||||||
OrderKind::ExchangeLimit => {
|
amount: position.amount().neg(),
|
||||||
self.submit_exchange_limit_order(closing_price, position.amount().neg())
|
},
|
||||||
.await?
|
TradingPlatform::Margin,
|
||||||
}
|
);
|
||||||
OrderKind::Market => {
|
|
||||||
self.submit_market_order(closing_price, position.amount().neg())
|
info!("Submitting {} order", order_form.kind());
|
||||||
.await?
|
let active_order = self.client.submit_order(&order_form).await?;
|
||||||
}
|
|
||||||
OrderKind::ExchangeMarket => {
|
|
||||||
self.submit_exchange_market_order(closing_price, position.amount().neg())
|
|
||||||
.await?
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.tracked_positions.insert(position.id(), active_order);
|
self.tracked_positions.insert(position.id(), active_order);
|
||||||
}
|
}
|
||||||
@ -501,68 +480,6 @@ impl OrderManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn submit_limit_order(
|
|
||||||
&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(
|
|
||||||
&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(
|
|
||||||
&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(
|
|
||||||
&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> {
|
pub fn update(&self) -> Result<OptionUpdate, BoxError> {
|
||||||
// TODO: implement me
|
// TODO: implement me
|
||||||
Ok((None, None))
|
Ok((None, None))
|
||||||
@ -635,10 +552,10 @@ impl PairManager {
|
|||||||
match m {
|
match m {
|
||||||
Message::ClosePosition {
|
Message::ClosePosition {
|
||||||
position,
|
position,
|
||||||
order_kind,
|
order_form,
|
||||||
} => {
|
} => {
|
||||||
self.order_manager
|
self.order_manager
|
||||||
.close_position(position, order_kind)
|
.close_position(position, order_form)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -651,13 +568,13 @@ impl PairManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExchangeManager {
|
pub struct ExchangeManager {
|
||||||
kind: ExchangeKind,
|
kind: ExchangeDetails,
|
||||||
pair_managers: Vec<PairManager>,
|
pair_managers: Vec<PairManager>,
|
||||||
client: Client,
|
client: Client,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExchangeManager {
|
impl ExchangeManager {
|
||||||
pub fn new(kind: &ExchangeKind, pairs: &Vec<SymbolPair>) -> Self {
|
pub fn new(kind: &ExchangeDetails, pairs: &Vec<SymbolPair>) -> Self {
|
||||||
let client = Client::new(kind);
|
let client = Client::new(kind);
|
||||||
let pair_managers = pairs
|
let pair_managers = pairs
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
use chrono::{DateTime, TimeZone};
|
use chrono::{DateTime, TimeZone};
|
||||||
use float_cmp::ApproxEq;
|
use float_cmp::ApproxEq;
|
||||||
|
|
||||||
|
use crate::connectors::{Exchange, ExchangeDetails};
|
||||||
use crate::currency::SymbolPair;
|
use crate::currency::SymbolPair;
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
|
|
||||||
@ -108,24 +110,19 @@ impl OrderBook {
|
|||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ActiveOrder {
|
pub struct ActiveOrder {
|
||||||
|
pub(crate) exchange: Exchange,
|
||||||
pub(crate) id: u64,
|
pub(crate) id: u64,
|
||||||
pub(crate) group_id: Option<u64>,
|
pub(crate) group_id: Option<u64>,
|
||||||
pub(crate) client_id: u64,
|
pub(crate) client_id: Option<u64>,
|
||||||
pub(crate) symbol: SymbolPair,
|
pub(crate) symbol: SymbolPair,
|
||||||
|
pub(crate) current_form: OrderForm,
|
||||||
pub(crate) creation_timestamp: u64,
|
pub(crate) creation_timestamp: u64,
|
||||||
pub(crate) update_timestamp: u64,
|
pub(crate) update_timestamp: u64,
|
||||||
pub(crate) amount: f64,
|
|
||||||
pub(crate) amount_original: f64,
|
|
||||||
pub(crate) order_type: OrderKind,
|
|
||||||
pub(crate) previous_order_type: Option<OrderKind>,
|
|
||||||
pub(crate) price: f64,
|
|
||||||
pub(crate) price_avg: f64,
|
|
||||||
pub(crate) hidden: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hash for ActiveOrder {
|
impl Hash for ActiveOrder {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
state.write(&self.id.to_le_bytes())
|
state.write(&self.id.to_le_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,118 +134,195 @@ impl PartialEq for ActiveOrder {
|
|||||||
|
|
||||||
impl Eq for ActiveOrder {}
|
impl Eq for ActiveOrder {}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum OrderKind {
|
pub enum TradingPlatform {
|
||||||
Limit,
|
Exchange,
|
||||||
ExchangeLimit,
|
Derivative,
|
||||||
Market,
|
Funding,
|
||||||
ExchangeMarket,
|
Margin,
|
||||||
Stop,
|
|
||||||
ExchangeStop,
|
|
||||||
StopLimit,
|
|
||||||
ExchangeStopLimit,
|
|
||||||
TrailingStop,
|
|
||||||
Fok,
|
|
||||||
ExchangeFok,
|
|
||||||
Ioc,
|
|
||||||
ExchangeIoc,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
impl TradingPlatform {
|
||||||
pub struct OrderForm {
|
pub fn as_str(&self) -> &'static str {
|
||||||
/// Order Type: LIMIT, EXCHANGE LIMIT, MARKET, EXCHANGE MARKET,
|
match self {
|
||||||
/// STOP, EXCHANGE STOP, STOP LIMIT, EXCHANGE STOP LIMIT,
|
TradingPlatform::Exchange => "Exchange",
|
||||||
/// TRAILING STOP, EXCHANGE TRAILING STOP, FOK,
|
TradingPlatform::Derivative => "Derivative",
|
||||||
/// EXCHANGE FOK, IOC, EXCHANGE IOC
|
TradingPlatform::Funding => "Funding",
|
||||||
kind: OrderKind,
|
TradingPlatform::Margin => "Margin",
|
||||||
/// Symbol for desired pair
|
}
|
||||||
pair: SymbolPair,
|
}
|
||||||
/// Price of order
|
}
|
||||||
|
|
||||||
|
impl Display for TradingPlatform {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum OrderKind {
|
||||||
|
Limit {
|
||||||
price: f64,
|
price: f64,
|
||||||
/// Amount of order (positive for buy, negative for sell)
|
|
||||||
amount: f64,
|
amount: f64,
|
||||||
/// Set the leverage for a derivative order, supported by derivative symbol orders only.
|
},
|
||||||
/// The value should be between 1 and 100 inclusive.
|
Market {
|
||||||
/// The field is optional, if omitted the default leverage value of 10 will be used.
|
amount: f64,
|
||||||
leverage: Option<u32>,
|
},
|
||||||
/// The trailing price for a trailing stop order
|
Stop {
|
||||||
price_trailing: Option<String>,
|
price: f64,
|
||||||
/// Auxiliary Limit price (for STOP LIMIT)
|
amount: f64,
|
||||||
price_aux_limit: Option<String>,
|
},
|
||||||
/// Time-In-Force: datetime for automatic order cancellation (ie. 2020-01-01 10:45:23) )
|
StopLimit {
|
||||||
tif: Option<String>,
|
price: f64,
|
||||||
|
amount: f64,
|
||||||
|
limit_price: f64,
|
||||||
|
},
|
||||||
|
TrailingStop {
|
||||||
|
distance: f64,
|
||||||
|
amount: f64,
|
||||||
|
},
|
||||||
|
FillOrKill {
|
||||||
|
price: f64,
|
||||||
|
amount: f64,
|
||||||
|
},
|
||||||
|
ImmediateOrCancel {
|
||||||
|
price: f64,
|
||||||
|
amount: f64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
impl OrderKind {
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
OrderKind::Limit { .. } => "Limit",
|
||||||
|
OrderKind::Market { .. } => "Market",
|
||||||
|
OrderKind::Stop { .. } => "Stop",
|
||||||
|
OrderKind::StopLimit { .. } => "Stop Limit",
|
||||||
|
OrderKind::TrailingStop { .. } => "Trailing Stop",
|
||||||
|
OrderKind::FillOrKill { .. } => "Fill or Kill",
|
||||||
|
OrderKind::ImmediateOrCancel { .. } => "Immediate or Cancel",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for OrderKind {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
OrderKind::Limit { price, amount } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"[{} | Price: {:0.5}, Amount: {:0.5}]",
|
||||||
|
self.as_str(),
|
||||||
|
price,
|
||||||
|
amount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
OrderKind::Market { amount } => {
|
||||||
|
write!(f, "[{} | Amount: {:0.5}]", self.as_str(), amount)
|
||||||
|
}
|
||||||
|
OrderKind::Stop { price, amount } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"[{} | Price: {:0.5}, Amount: {:0.5}]",
|
||||||
|
self.as_str(),
|
||||||
|
price,
|
||||||
|
amount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
OrderKind::StopLimit {
|
||||||
|
price,
|
||||||
|
amount,
|
||||||
|
limit_price,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"[{} | Price: {:0.5}, Amount: {:0.5}, Limit Price: {:0.5}]",
|
||||||
|
self.as_str(),
|
||||||
|
price,
|
||||||
|
amount,
|
||||||
|
limit_price
|
||||||
|
)
|
||||||
|
}
|
||||||
|
OrderKind::TrailingStop { distance, amount } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"[{} | Distance: {:0.5}, Amount: {:0.5}]",
|
||||||
|
self.as_str(),
|
||||||
|
distance,
|
||||||
|
amount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
OrderKind::FillOrKill { price, amount } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"[{} | Price: {:0.5}, Amount: {:0.5}]",
|
||||||
|
self.as_str(),
|
||||||
|
price,
|
||||||
|
amount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
OrderKind::ImmediateOrCancel { price, amount } => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"[{} | Price: {:0.5}, Amount: {:0.5}]",
|
||||||
|
self.as_str(),
|
||||||
|
price,
|
||||||
|
amount
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct OrderForm {
|
||||||
|
pair: SymbolPair,
|
||||||
|
kind: OrderKind,
|
||||||
|
platform: TradingPlatform,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OrderForm {
|
impl OrderForm {
|
||||||
pub fn new(pair: &SymbolPair, price: f64, amount: f64, kind: OrderKind) -> Self {
|
pub fn new(pair: SymbolPair, order_kind: OrderKind, platform: TradingPlatform) -> Self {
|
||||||
OrderForm {
|
Self {
|
||||||
kind,
|
pair,
|
||||||
pair: pair.clone(),
|
kind: order_kind,
|
||||||
price,
|
platform,
|
||||||
amount,
|
|
||||||
leverage: None,
|
|
||||||
price_trailing: None,
|
|
||||||
price_aux_limit: None,
|
|
||||||
tif: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_leverage(mut self, leverage: u32) -> Self {
|
pub fn pair(&self) -> &SymbolPair {
|
||||||
self.leverage = Some(leverage);
|
&self.pair
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_price_trailing(mut self, trailing: f64) -> Result<Self, BoxError> {
|
|
||||||
match self.kind {
|
|
||||||
OrderKind::TrailingStop => {
|
|
||||||
self.price_trailing = Some(trailing.to_string());
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
_ => Err("Invalid order type.".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_price_aux_limit(mut self, limit: f64) -> Result<Self, BoxError> {
|
|
||||||
match self.kind {
|
|
||||||
OrderKind::StopLimit | OrderKind::ExchangeStopLimit => {
|
|
||||||
self.price_aux_limit = Some(limit.to_string());
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
_ => Err("Invalid order type.".into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_tif<T: TimeZone>(mut self, tif: DateTime<T>) -> Self
|
|
||||||
where
|
|
||||||
T::Offset: Display,
|
|
||||||
{
|
|
||||||
self.tif = Some(tif.format("%Y-%m-%d %H:%M:%S").to_string());
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> OrderKind {
|
pub fn kind(&self) -> OrderKind {
|
||||||
self.kind
|
self.kind
|
||||||
}
|
}
|
||||||
pub fn pair(&self) -> &SymbolPair {
|
|
||||||
&self.pair
|
pub fn platform(&self) -> &TradingPlatform {
|
||||||
|
&self.platform
|
||||||
}
|
}
|
||||||
pub fn price(&self) -> &f64 {
|
|
||||||
&self.price
|
pub fn amount(&self) -> f64 {
|
||||||
|
match self.kind {
|
||||||
|
OrderKind::Limit { amount, .. } => amount,
|
||||||
|
OrderKind::Market { amount } => amount,
|
||||||
|
OrderKind::Stop { amount, .. } => amount,
|
||||||
|
OrderKind::StopLimit { amount, .. } => amount,
|
||||||
|
OrderKind::TrailingStop { amount, .. } => amount,
|
||||||
|
OrderKind::FillOrKill { amount, .. } => amount,
|
||||||
|
OrderKind::ImmediateOrCancel { amount, .. } => amount,
|
||||||
}
|
}
|
||||||
pub fn amount(&self) -> &f64 {
|
|
||||||
&self.amount
|
|
||||||
}
|
}
|
||||||
pub fn leverage(&self) -> Option<u32> {
|
|
||||||
self.leverage
|
pub fn price(&self) -> Option<f64> {
|
||||||
|
match self.kind {
|
||||||
|
OrderKind::Limit { price, .. } => Some(price),
|
||||||
|
OrderKind::Market { .. } => None,
|
||||||
|
OrderKind::Stop { price, .. } => Some(price),
|
||||||
|
OrderKind::StopLimit { price, .. } => Some(price),
|
||||||
|
OrderKind::TrailingStop { .. } => None,
|
||||||
|
OrderKind::FillOrKill { price, .. } => Some(price),
|
||||||
|
OrderKind::ImmediateOrCancel { price, .. } => Some(price),
|
||||||
}
|
}
|
||||||
pub fn price_trailing(&self) -> &Option<String> {
|
|
||||||
&self.price_trailing
|
|
||||||
}
|
|
||||||
pub fn price_aux_limit(&self) -> &Option<String> {
|
|
||||||
&self.price_aux_limit
|
|
||||||
}
|
|
||||||
pub fn tif(&self) -> &Option<String> {
|
|
||||||
&self.tif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Debug, Formatter};
|
use std::fmt::{Debug, Formatter};
|
||||||
|
use std::ops::Neg;
|
||||||
|
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
use log::{debug, info};
|
use log::{debug, info};
|
||||||
@ -9,6 +10,7 @@ use crate::events::{Event, EventKind, EventMetadata, Message};
|
|||||||
use crate::managers::{OrderManager, PositionManager, TrackedPositionsMap};
|
use crate::managers::{OrderManager, PositionManager, TrackedPositionsMap};
|
||||||
use crate::models::{
|
use crate::models::{
|
||||||
ActiveOrder, OrderBook, OrderBookEntry, OrderForm, OrderKind, Position, PositionProfitState,
|
ActiveOrder, OrderBook, OrderBookEntry, OrderForm, OrderKind, Position, PositionProfitState,
|
||||||
|
TradingPlatform,
|
||||||
};
|
};
|
||||||
use crate::BoxError;
|
use crate::BoxError;
|
||||||
|
|
||||||
@ -111,7 +113,14 @@ impl PositionStrategy for TrailingStop {
|
|||||||
debug!("Inserting close position message...");
|
debug!("Inserting close position message...");
|
||||||
messages.push(Message::ClosePosition {
|
messages.push(Message::ClosePosition {
|
||||||
position: position.clone(),
|
position: position.clone(),
|
||||||
order_kind: OrderKind::Limit,
|
order_form: OrderForm::new(
|
||||||
|
position.pair().clone(),
|
||||||
|
OrderKind::Limit {
|
||||||
|
price: 0.0,
|
||||||
|
amount: position.amount(),
|
||||||
|
},
|
||||||
|
TradingPlatform::Margin,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
PositionProfitState::Critical
|
PositionProfitState::Critical
|
||||||
}
|
}
|
||||||
@ -195,7 +204,7 @@ pub struct FastOrderStrategy {
|
|||||||
|
|
||||||
impl Default for FastOrderStrategy {
|
impl Default for FastOrderStrategy {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { threshold: 0.2 }
|
Self { threshold: 0.05 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +233,7 @@ impl OrderStrategy for FastOrderStrategy {
|
|||||||
|
|
||||||
// long
|
// long
|
||||||
let offer_comparison = {
|
let offer_comparison = {
|
||||||
if order.amount > 0.0 {
|
if order.current_form.amount() > 0.0 {
|
||||||
order_book.highest_bid()
|
order_book.highest_bid()
|
||||||
} else {
|
} else {
|
||||||
order_book.lowest_ask()
|
order_book.lowest_ask()
|
||||||
@ -233,12 +242,22 @@ impl OrderStrategy for FastOrderStrategy {
|
|||||||
|
|
||||||
// if the best offer is higher than our threshold,
|
// if the best offer is higher than our threshold,
|
||||||
// ask the manager to close the position with a market order
|
// ask the manager to close the position with a market order
|
||||||
let delta = (1.0 - (offer_comparison / order.price)) * 100.0;
|
let order_price = order
|
||||||
|
.current_form
|
||||||
|
.price()
|
||||||
|
.ok_or("The active order does not have a price!")?;
|
||||||
|
let delta = (1.0 - (offer_comparison / order_price)) * 100.0;
|
||||||
|
|
||||||
if delta > self.threshold {
|
if delta > self.threshold {
|
||||||
messages.push(Message::ClosePosition {
|
messages.push(Message::ClosePosition {
|
||||||
position: active_position.clone(),
|
position: active_position.clone(),
|
||||||
order_kind: OrderKind::Market,
|
order_form: OrderForm::new(
|
||||||
|
order.symbol.clone(),
|
||||||
|
OrderKind::Market {
|
||||||
|
amount: order.current_form.amount(),
|
||||||
|
},
|
||||||
|
order.current_form.platform().clone(),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user