Skip to main content

rustrade_execution/order/
mod.rs

1use crate::order::{
2    id::StrategyId,
3    request::{OrderRequestCancel, OrderRequestOpen, RequestCancel, RequestOpen},
4    state::UnindexedOrderState,
5};
6use derive_more::{Constructor, Display};
7use id::ClientOrderId;
8use rust_decimal::Decimal;
9use rustrade_instrument::{
10    Side,
11    asset::{AssetIndex, name::AssetNameExchange},
12    exchange::{ExchangeId, ExchangeIndex},
13    instrument::{InstrumentIndex, name::InstrumentNameExchange},
14};
15use serde::{Deserialize, Serialize};
16use state::{ActiveOrderState, Cancelled, InactiveOrderState, Open, OpenInFlight, OrderState};
17
18/// `Order` related identifiers.
19pub mod id;
20
21/// `Order` states.
22///
23/// eg/ `OpenInFlight`, `Open`, `Rejected`, `Expired`, etc.
24pub mod state;
25
26/// Order open and cancel request types.
27///
28/// ie/ `OrderRequestOpen` & `OrderRequestCancel`.
29pub mod request;
30
31/// Convenient type alias for an [`Order`] keyed with [`ExchangeId`] and [`InstrumentNameExchange`].
32pub type UnindexedOrder = Order<ExchangeId, InstrumentNameExchange, UnindexedOrderState>;
33
34/// Convenient type alias for an [`OrderKey`] keyed with [`ExchangeId`]
35/// and [`InstrumentNameExchange`].
36pub type UnindexedOrderKey = OrderKey<ExchangeId, InstrumentNameExchange>;
37
38/// Convenient type alias for an [`OrderSnapshot`] keyed with [`ExchangeId`], [`AssetNameExchange`],
39/// and [`InstrumentNameExchange`].
40pub type UnindexedOrderSnapshot = Order<
41    ExchangeId,
42    InstrumentNameExchange,
43    OrderState<AssetNameExchange, InstrumentNameExchange>,
44>;
45
46/// Convenient type alias for an [`Order`] [`OrderState`] snapshot.
47pub type OrderSnapshot<
48    ExchangeKey = ExchangeIndex,
49    AssetKey = AssetIndex,
50    InstrumentKey = InstrumentIndex,
51> = Order<ExchangeKey, InstrumentKey, OrderState<AssetKey, InstrumentKey>>;
52
53#[derive(
54    Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Constructor,
55)]
56
57pub struct OrderEvent<State, ExchangeKey = ExchangeIndex, InstrumentKey = InstrumentIndex> {
58    pub key: OrderKey<ExchangeKey, InstrumentKey>,
59    pub state: State,
60}
61
62#[derive(
63    Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Constructor,
64)]
65pub struct OrderKey<ExchangeKey = ExchangeIndex, InstrumentKey = InstrumentIndex> {
66    pub exchange: ExchangeKey,
67    pub instrument: InstrumentKey,
68    pub strategy: StrategyId,
69    pub cid: ClientOrderId,
70}
71
72#[derive(
73    Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Constructor,
74)]
75pub struct Order<ExchangeKey = ExchangeIndex, InstrumentKey = InstrumentIndex, State = OrderState> {
76    pub key: OrderKey<ExchangeKey, InstrumentKey>,
77    pub side: Side,
78    pub price: Decimal,
79    pub quantity: Decimal,
80    pub kind: OrderKind,
81    pub time_in_force: TimeInForce,
82    pub state: State,
83}
84
85impl<ExchangeKey, AssetKey, InstrumentKey>
86    Order<ExchangeKey, InstrumentKey, OrderState<AssetKey, InstrumentKey>>
87{
88    pub fn to_active(&self) -> Option<Order<ExchangeKey, InstrumentKey, ActiveOrderState>>
89    where
90        ExchangeKey: Clone,
91        InstrumentKey: Clone,
92    {
93        let OrderState::Active(state) = &self.state else {
94            return None;
95        };
96
97        Some(Order {
98            key: self.key.clone(),
99            side: self.side,
100            price: self.price,
101            quantity: self.quantity,
102            kind: self.kind,
103            time_in_force: self.time_in_force,
104            state: state.clone(),
105        })
106    }
107
108    pub fn to_inactive(
109        &self,
110    ) -> Option<Order<ExchangeKey, InstrumentKey, InactiveOrderState<AssetKey, InstrumentKey>>>
111    where
112        ExchangeKey: Clone,
113        AssetKey: Clone,
114        InstrumentKey: Clone,
115    {
116        let OrderState::Inactive(state) = &self.state else {
117            return None;
118        };
119
120        Some(Order {
121            key: self.key.clone(),
122            side: self.side,
123            price: self.price,
124            quantity: self.quantity,
125            kind: self.kind,
126            time_in_force: self.time_in_force,
127            state: state.clone(),
128        })
129    }
130}
131
132impl<ExchangeKey, InstrumentKey> Order<ExchangeKey, InstrumentKey, ActiveOrderState>
133where
134    ExchangeKey: Clone,
135    InstrumentKey: Clone,
136{
137    pub fn to_request_cancel(&self) -> Option<OrderRequestCancel<ExchangeKey, InstrumentKey>> {
138        let Order { key, state, .. } = self;
139
140        let request_cancel = match state {
141            ActiveOrderState::OpenInFlight(_) => RequestCancel { id: None },
142            ActiveOrderState::Open(open) => RequestCancel {
143                id: Some(open.id.clone()),
144            },
145            _ => return None,
146        };
147
148        Some(OrderRequestCancel {
149            key: key.clone(),
150            state: request_cancel,
151        })
152    }
153}
154
155#[derive(
156    Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Display,
157)]
158pub enum OrderKind {
159    Market,
160    Limit,
161}
162
163#[derive(
164    Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, Display,
165)]
166pub enum TimeInForce {
167    GoodUntilCancelled { post_only: bool },
168    GoodUntilEndOfDay,
169    FillOrKill,
170    ImmediateOrCancel,
171}
172
173impl<ExchangeKey, InstrumentKey> From<&OrderRequestOpen<ExchangeKey, InstrumentKey>>
174    for Order<ExchangeKey, InstrumentKey, ActiveOrderState>
175where
176    ExchangeKey: Clone,
177    InstrumentKey: Clone,
178{
179    fn from(value: &OrderRequestOpen<ExchangeKey, InstrumentKey>) -> Self {
180        let OrderRequestOpen {
181            key,
182            state:
183                RequestOpen {
184                    side,
185                    price,
186                    quantity,
187                    kind,
188                    time_in_force,
189                    position_id: _,
190                    reduce_only: _, // used by adapters (e.g., Alpaca) to derive position_intent
191                },
192        } = value;
193
194        Self {
195            key: key.clone(),
196            side: *side,
197            price: *price,
198            quantity: *quantity,
199            kind: *kind,
200            time_in_force: *time_in_force,
201            state: ActiveOrderState::OpenInFlight(OpenInFlight),
202        }
203    }
204}
205
206impl<ExchangeKey, InstrumentKey> From<Order<ExchangeKey, InstrumentKey, Open>>
207    for Order<ExchangeKey, InstrumentKey, ActiveOrderState>
208{
209    fn from(value: Order<ExchangeKey, InstrumentKey, Open>) -> Self {
210        let Order {
211            key,
212            side,
213            price,
214            quantity,
215            kind,
216            time_in_force,
217            state,
218        } = value;
219
220        Self {
221            key,
222            side,
223            price,
224            quantity,
225            kind,
226            time_in_force,
227            state: ActiveOrderState::Open(state),
228        }
229    }
230}
231
232impl<ExchangeKey, AssetKey, InstrumentKey> From<Order<ExchangeKey, InstrumentKey, Open>>
233    for Order<ExchangeKey, InstrumentKey, OrderState<AssetKey, InstrumentKey>>
234{
235    fn from(value: Order<ExchangeKey, InstrumentKey, Open>) -> Self {
236        let Order {
237            key,
238            side,
239            price,
240            quantity,
241            kind,
242            time_in_force,
243            state,
244        } = value;
245
246        Self {
247            key,
248            side,
249            price,
250            quantity,
251            kind,
252            time_in_force,
253            state: OrderState::Active(ActiveOrderState::Open(state)),
254        }
255    }
256}
257
258impl<ExchangeKey, AssetKey, InstrumentKey> From<Order<ExchangeKey, InstrumentKey, Cancelled>>
259    for Order<ExchangeKey, InstrumentKey, OrderState<AssetKey, InstrumentKey>>
260{
261    fn from(value: Order<ExchangeKey, InstrumentKey, Cancelled>) -> Self {
262        let Order {
263            key,
264            side,
265            price,
266            quantity,
267            kind,
268            time_in_force,
269            state,
270        } = value;
271
272        Self {
273            key,
274            side,
275            price,
276            quantity,
277            kind,
278            time_in_force,
279            state: OrderState::Inactive(InactiveOrderState::Cancelled(state)),
280        }
281    }
282}