Skip to main content

rustrade_execution/exchange/mock/
request.rs

1use crate::{
2    UnindexedAccountSnapshot,
3    balance::AssetBalance,
4    order::{
5        Order,
6        request::{OrderRequestCancel, OrderRequestOpen, UnindexedOrderResponseCancel},
7        state::{Open, UnindexedOrderState},
8    },
9    trade::Trade,
10};
11use chrono::{DateTime, Utc};
12use rust_decimal::Decimal;
13use rustrade_instrument::{
14    asset::name::AssetNameExchange, exchange::ExchangeId, instrument::name::InstrumentNameExchange,
15};
16use tokio::sync::oneshot;
17
18/// Market price snapshot passed alongside an order request so the
19/// [`FillModel`](crate::fill::FillModel) can compute a realistic fill price.
20///
21/// All fields are optional. When a field is `None` the fill model falls back
22/// to the next available price (see each [`FillModel`](crate::fill::FillModel)
23/// implementation for exact semantics).
24///
25/// The standard [`MockExecution`](crate::client::mock::MockExecution) client
26/// populates all fields as `None` (it has no market-data subscription).
27/// Downstream consumers that want realistic slippage should construct a
28/// custom execution wrapper that injects current bid/ask/last prices before
29/// forwarding the request.
30#[derive(Debug, Clone, Copy, Default)]
31pub struct MarketPrices {
32    pub best_bid: Option<Decimal>,
33    pub best_ask: Option<Decimal>,
34    pub last_price: Option<Decimal>,
35}
36
37#[derive(Debug)]
38pub struct MockExchangeRequest {
39    pub time_request: DateTime<Utc>,
40    pub kind: MockExchangeRequestKind,
41}
42
43impl MockExchangeRequest {
44    pub fn new(time_request: DateTime<Utc>, kind: MockExchangeRequestKind) -> Self {
45        Self { time_request, kind }
46    }
47
48    pub fn fetch_account_snapshot(
49        time_request: DateTime<Utc>,
50        response_tx: oneshot::Sender<UnindexedAccountSnapshot>,
51    ) -> Self {
52        Self::new(
53            time_request,
54            MockExchangeRequestKind::FetchAccountSnapshot { response_tx },
55        )
56    }
57
58    pub fn fetch_balances(
59        time_request: DateTime<Utc>,
60        assets: Vec<AssetNameExchange>,
61        response_tx: oneshot::Sender<Vec<AssetBalance<AssetNameExchange>>>,
62    ) -> Self {
63        Self::new(
64            time_request,
65            MockExchangeRequestKind::FetchBalances {
66                response_tx,
67                assets,
68            },
69        )
70    }
71
72    pub fn fetch_orders_open(
73        time_request: DateTime<Utc>,
74        instruments: Vec<InstrumentNameExchange>,
75        response_tx: oneshot::Sender<Vec<Order<ExchangeId, InstrumentNameExchange, Open>>>,
76    ) -> Self {
77        Self::new(
78            time_request,
79            MockExchangeRequestKind::FetchOrdersOpen {
80                response_tx,
81                instruments,
82            },
83        )
84    }
85
86    pub fn fetch_trades(
87        time_request: DateTime<Utc>,
88        response_tx: oneshot::Sender<Vec<Trade<AssetNameExchange, InstrumentNameExchange>>>,
89        time_since: DateTime<Utc>,
90    ) -> Self {
91        Self::new(
92            time_request,
93            MockExchangeRequestKind::FetchTrades {
94                response_tx,
95                time_since,
96            },
97        )
98    }
99
100    pub fn cancel_order(
101        time_request: DateTime<Utc>,
102        response_tx: oneshot::Sender<UnindexedOrderResponseCancel>,
103        request: OrderRequestCancel<ExchangeId, InstrumentNameExchange>,
104    ) -> Self {
105        Self::new(
106            time_request,
107            MockExchangeRequestKind::CancelOrder {
108                response_tx,
109                request,
110            },
111        )
112    }
113
114    pub fn open_order(
115        time_request: DateTime<Utc>,
116        response_tx: oneshot::Sender<
117            Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>,
118        >,
119        request: OrderRequestOpen<ExchangeId, InstrumentNameExchange>,
120        market_prices: MarketPrices,
121    ) -> Self {
122        Self::new(
123            time_request,
124            MockExchangeRequestKind::OpenOrder {
125                response_tx,
126                request,
127                market_prices,
128            },
129        )
130    }
131}
132
133#[derive(Debug)]
134pub enum MockExchangeRequestKind {
135    FetchAccountSnapshot {
136        response_tx: oneshot::Sender<UnindexedAccountSnapshot>,
137    },
138    FetchBalances {
139        assets: Vec<AssetNameExchange>,
140        response_tx: oneshot::Sender<Vec<AssetBalance<AssetNameExchange>>>,
141    },
142    FetchOrdersOpen {
143        instruments: Vec<InstrumentNameExchange>,
144        response_tx: oneshot::Sender<Vec<Order<ExchangeId, InstrumentNameExchange, Open>>>,
145    },
146    FetchTrades {
147        response_tx: oneshot::Sender<Vec<Trade<AssetNameExchange, InstrumentNameExchange>>>,
148        time_since: DateTime<Utc>,
149    },
150    CancelOrder {
151        response_tx: oneshot::Sender<UnindexedOrderResponseCancel>,
152        request: OrderRequestCancel<ExchangeId, InstrumentNameExchange>,
153    },
154    OpenOrder {
155        response_tx:
156            oneshot::Sender<Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>>,
157        request: OrderRequestOpen<ExchangeId, InstrumentNameExchange>,
158        market_prices: MarketPrices,
159    },
160}