use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::Stream;
use rust_decimal::Decimal;
use crate::{
ClassifiedError, ClientOrderId, Extensions, FillId, MarketKey, OrderId, Side, TradeId,
UnixMillis,
};
#[dynosaur::dynosaur(pub DynTrading = dyn(box) Trading)]
pub trait Trading: Send + Sync {
fn place(
&self,
order: OrderRequest,
) -> impl Future<Output = Result<Order, ClassifiedError>> + Send;
fn cancel(&self, id: &OrderId) -> impl Future<Output = Result<(), ClassifiedError>> + Send;
fn cancel_all(
&self,
key: Option<&MarketKey>,
) -> impl Future<Output = Result<Vec<OrderId>, ClassifiedError>> + Send;
fn open_orders(
&self,
key: Option<&MarketKey>,
) -> impl Future<Output = Result<Vec<Order>, ClassifiedError>> + Send;
fn positions(&self) -> impl Future<Output = Result<Vec<Position>, ClassifiedError>> + Send;
fn balances(&self) -> impl Future<Output = Result<Vec<Balance>, ClassifiedError>> + Send;
fn events(&self) -> TradingEvents;
}
pub struct TradingEvents(
Pin<Box<dyn Stream<Item = Result<TradingEvent, ClassifiedError>> + Send + 'static>>,
);
impl TradingEvents {
pub fn new(
stream: impl Stream<Item = Result<TradingEvent, ClassifiedError>> + Send + 'static,
) -> Self {
Self(Box::pin(stream))
}
}
impl fmt::Debug for TradingEvents {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("TradingEvents(..)")
}
}
impl Stream for TradingEvents {
type Item = Result<TradingEvent, ClassifiedError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.0.as_mut().poll_next(cx)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum TimeInForce {
#[default]
GoodTilCancelled,
ImmediateOrCancel,
FillOrKill,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct OrderRequest {
pub key: MarketKey,
pub side: Side,
pub price: Decimal,
pub size: Decimal,
pub time_in_force: TimeInForce,
pub client_order_id: Option<ClientOrderId>,
pub post_only: bool,
pub reduce_only: bool,
pub extensions: Extensions,
}
impl OrderRequest {
pub fn new(key: MarketKey, side: Side, price: Decimal, size: Decimal) -> Self {
Self {
key,
side,
price,
size,
time_in_force: TimeInForce::default(),
client_order_id: None,
post_only: false,
reduce_only: false,
extensions: Extensions::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum KillReason {
Unmatched,
Unfillable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum OrderStatus {
Open,
Filled,
Cancelled,
Killed {
reason: KillReason,
},
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Order {
pub id: OrderId,
pub key: MarketKey,
pub side: Side,
pub price: Decimal,
pub size: Decimal,
pub filled: Decimal,
pub status: OrderStatus,
pub time_in_force: Option<TimeInForce>,
pub client_order_id: Option<ClientOrderId>,
pub created: Option<UnixMillis>,
pub extensions: Extensions,
}
impl Order {
pub fn new(
id: OrderId,
key: MarketKey,
side: Side,
price: Decimal,
size: Decimal,
status: OrderStatus,
) -> Self {
Self {
id,
key,
side,
price,
size,
filled: Decimal::ZERO,
status,
time_in_force: None,
client_order_id: None,
created: None,
extensions: Extensions::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Liquidity {
Maker,
Taker,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Fill {
pub id: FillId,
pub order_id: OrderId,
pub trade_id: Option<TradeId>,
pub key: MarketKey,
pub side: Side,
pub price: Decimal,
pub size: Decimal,
pub notional: Option<Decimal>,
pub fee: Option<Decimal>,
pub liquidity: Option<Liquidity>,
pub client_order_id: Option<ClientOrderId>,
pub time: UnixMillis,
pub extensions: Extensions,
}
impl Fill {
pub fn new(
id: FillId,
order_id: OrderId,
key: MarketKey,
side: Side,
price: Decimal,
size: Decimal,
time: UnixMillis,
) -> Self {
Self {
id,
order_id,
trade_id: None,
key,
side,
price,
size,
notional: None,
fee: None,
liquidity: None,
client_order_id: None,
time,
extensions: Extensions::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Position {
pub key: MarketKey,
pub size: Decimal,
pub average_price: Option<Decimal>,
pub extensions: Extensions,
}
impl Position {
pub fn new(key: MarketKey, size: Decimal) -> Self {
Self {
key,
size,
average_price: None,
extensions: Extensions::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct Balance {
pub asset: String,
pub total: Option<Decimal>,
pub available: Option<Decimal>,
pub extensions: Extensions,
}
impl Balance {
pub fn new(asset: impl Into<String>) -> Self {
Self {
asset: asset.into(),
total: None,
available: None,
extensions: Extensions::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum TradingEvent {
Ack(Order),
Fill(Fill),
Resync,
}