nautilus_model/orders/
any.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::fmt::Display;
17
18use enum_dispatch::enum_dispatch;
19use serde::{Deserialize, Serialize};
20
21use super::{
22    Order, limit::LimitOrder, limit_if_touched::LimitIfTouchedOrder, market::MarketOrder,
23    market_if_touched::MarketIfTouchedOrder, market_to_limit::MarketToLimitOrder,
24    stop_limit::StopLimitOrder, stop_market::StopMarketOrder,
25    trailing_stop_limit::TrailingStopLimitOrder, trailing_stop_market::TrailingStopMarketOrder,
26};
27use crate::{events::OrderEventAny, types::Price};
28
29#[derive(Clone, Debug, Serialize, Deserialize)]
30#[enum_dispatch(Order)]
31pub enum OrderAny {
32    Limit(LimitOrder),
33    LimitIfTouched(LimitIfTouchedOrder),
34    Market(MarketOrder),
35    MarketIfTouched(MarketIfTouchedOrder),
36    MarketToLimit(MarketToLimitOrder),
37    StopLimit(StopLimitOrder),
38    StopMarket(StopMarketOrder),
39    TrailingStopLimit(TrailingStopLimitOrder),
40    TrailingStopMarket(TrailingStopMarketOrder),
41}
42
43impl OrderAny {
44    pub fn from_events(events: Vec<OrderEventAny>) -> anyhow::Result<Self> {
45        if events.is_empty() {
46            anyhow::bail!("No order events provided to create OrderAny");
47        }
48
49        // Pop the first event
50        let init_event = events.first().unwrap();
51        match init_event {
52            OrderEventAny::Initialized(init) => {
53                let mut order = Self::from(init.clone());
54                // Apply the rest of the events
55                for event in events.into_iter().skip(1) {
56                    // Apply event to order
57                    println!("Applying event: {event:?}"); // TODO: Development
58                    order.apply(event).unwrap();
59                }
60                Ok(order)
61            }
62            _ => {
63                anyhow::bail!("First event must be `OrderInitialized`");
64            }
65        }
66    }
67}
68
69impl PartialEq for OrderAny {
70    fn eq(&self, other: &Self) -> bool {
71        self.client_order_id() == other.client_order_id()
72    }
73}
74
75// TODO: fix equality
76impl Eq for OrderAny {}
77
78impl Display for OrderAny {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        write!(
81            f,
82            "{}",
83            match self {
84                Self::Limit(order) => order.to_string(),
85                Self::LimitIfTouched(order) => format!("{order:?}"), // TODO: Implement
86                Self::Market(order) => order.to_string(),
87                Self::MarketIfTouched(order) => format!("{order:?}"), // TODO: Implement
88                Self::MarketToLimit(order) => format!("{order:?}"),   // TODO: Implement
89                Self::StopLimit(order) => order.to_string(),
90                Self::StopMarket(order) => format!("{order:?}"), // TODO: Implement
91                Self::TrailingStopLimit(order) => format!("{order:?}"), // TODO: Implement
92                Self::TrailingStopMarket(order) => format!("{order:?}"), // TODO: Implement
93            }
94        )
95    }
96}
97
98impl From<OrderAny> for PassiveOrderAny {
99    fn from(order: OrderAny) -> PassiveOrderAny {
100        match order {
101            OrderAny::Limit(_) => PassiveOrderAny::Limit(order.into()),
102            OrderAny::LimitIfTouched(_) => PassiveOrderAny::Stop(order.into()),
103            OrderAny::MarketIfTouched(_) => PassiveOrderAny::Stop(order.into()),
104            OrderAny::StopLimit(_) => PassiveOrderAny::Stop(order.into()),
105            OrderAny::StopMarket(_) => PassiveOrderAny::Stop(order.into()),
106            OrderAny::TrailingStopLimit(_) => PassiveOrderAny::Stop(order.into()),
107            OrderAny::TrailingStopMarket(_) => PassiveOrderAny::Stop(order.into()),
108            OrderAny::MarketToLimit(_) => PassiveOrderAny::Limit(order.into()),
109            _ => panic!("WIP: Implement trait bound to require `HasPrice`"),
110        }
111    }
112}
113
114impl From<PassiveOrderAny> for OrderAny {
115    fn from(order: PassiveOrderAny) -> OrderAny {
116        match order {
117            PassiveOrderAny::Limit(order) => order.into(),
118            PassiveOrderAny::Stop(order) => order.into(),
119        }
120    }
121}
122
123impl From<OrderAny> for StopOrderAny {
124    fn from(order: OrderAny) -> StopOrderAny {
125        match order {
126            OrderAny::LimitIfTouched(order) => StopOrderAny::LimitIfTouched(order),
127            OrderAny::MarketIfTouched(order) => StopOrderAny::MarketIfTouched(order),
128            OrderAny::StopLimit(order) => StopOrderAny::StopLimit(order),
129            OrderAny::StopMarket(order) => StopOrderAny::StopMarket(order),
130            OrderAny::TrailingStopLimit(order) => StopOrderAny::TrailingStopLimit(order),
131            OrderAny::TrailingStopMarket(order) => StopOrderAny::TrailingStopMarket(order),
132            _ => panic!("WIP: Implement trait bound to require `HasStopPrice`"),
133        }
134    }
135}
136
137impl From<StopOrderAny> for OrderAny {
138    fn from(order: StopOrderAny) -> OrderAny {
139        match order {
140            StopOrderAny::LimitIfTouched(order) => OrderAny::LimitIfTouched(order),
141            StopOrderAny::MarketIfTouched(order) => OrderAny::MarketIfTouched(order),
142            StopOrderAny::StopLimit(order) => OrderAny::StopLimit(order),
143            StopOrderAny::StopMarket(order) => OrderAny::StopMarket(order),
144            StopOrderAny::TrailingStopLimit(order) => OrderAny::TrailingStopLimit(order),
145            StopOrderAny::TrailingStopMarket(order) => OrderAny::TrailingStopMarket(order),
146        }
147    }
148}
149
150impl From<OrderAny> for LimitOrderAny {
151    fn from(order: OrderAny) -> LimitOrderAny {
152        match order {
153            OrderAny::Limit(order) => LimitOrderAny::Limit(order),
154            OrderAny::MarketToLimit(order) => LimitOrderAny::MarketToLimit(order),
155            OrderAny::StopLimit(order) => LimitOrderAny::StopLimit(order),
156            OrderAny::TrailingStopLimit(order) => LimitOrderAny::TrailingStopLimit(order),
157            _ => panic!("WIP: Implement trait bound to require `HasLimitPrice`"),
158        }
159    }
160}
161
162impl From<LimitOrderAny> for OrderAny {
163    fn from(order: LimitOrderAny) -> OrderAny {
164        match order {
165            LimitOrderAny::Limit(order) => OrderAny::Limit(order),
166            LimitOrderAny::MarketToLimit(order) => OrderAny::MarketToLimit(order),
167            LimitOrderAny::StopLimit(order) => OrderAny::StopLimit(order),
168            LimitOrderAny::TrailingStopLimit(order) => OrderAny::TrailingStopLimit(order),
169        }
170    }
171}
172
173#[derive(Clone, Debug)]
174#[enum_dispatch(Order)]
175pub enum PassiveOrderAny {
176    Limit(LimitOrderAny),
177    Stop(StopOrderAny),
178}
179
180impl PassiveOrderAny {
181    #[must_use]
182    pub fn to_any(&self) -> OrderAny {
183        match self {
184            Self::Limit(order) => order.clone().into(),
185            Self::Stop(order) => order.clone().into(),
186        }
187    }
188}
189
190// TODO: Derive equality
191impl PartialEq for PassiveOrderAny {
192    fn eq(&self, rhs: &Self) -> bool {
193        match self {
194            Self::Limit(order) => order.client_order_id() == rhs.client_order_id(),
195            Self::Stop(order) => order.client_order_id() == rhs.client_order_id(),
196        }
197    }
198}
199
200#[derive(Clone, Debug)]
201#[enum_dispatch(Order)]
202pub enum LimitOrderAny {
203    Limit(LimitOrder),
204    MarketToLimit(MarketToLimitOrder),
205    StopLimit(StopLimitOrder),
206    TrailingStopLimit(TrailingStopLimitOrder),
207}
208
209impl LimitOrderAny {
210    #[must_use]
211    pub fn limit_px(&self) -> Price {
212        match self {
213            Self::Limit(order) => order.price,
214            Self::MarketToLimit(order) => order.price.expect("No price for order"), // TBD
215            Self::StopLimit(order) => order.price,
216            Self::TrailingStopLimit(order) => order.price,
217        }
218    }
219}
220
221impl PartialEq for LimitOrderAny {
222    fn eq(&self, rhs: &Self) -> bool {
223        match self {
224            Self::Limit(order) => order.client_order_id == rhs.client_order_id(),
225            Self::MarketToLimit(order) => order.client_order_id == rhs.client_order_id(),
226            Self::StopLimit(order) => order.client_order_id == rhs.client_order_id(),
227            Self::TrailingStopLimit(order) => order.client_order_id == rhs.client_order_id(),
228        }
229    }
230}
231
232#[derive(Clone, Debug)]
233#[enum_dispatch(Order)]
234pub enum StopOrderAny {
235    LimitIfTouched(LimitIfTouchedOrder),
236    MarketIfTouched(MarketIfTouchedOrder),
237    StopLimit(StopLimitOrder),
238    StopMarket(StopMarketOrder),
239    TrailingStopLimit(TrailingStopLimitOrder),
240    TrailingStopMarket(TrailingStopMarketOrder),
241}
242
243impl StopOrderAny {
244    #[must_use]
245    pub fn stop_px(&self) -> Price {
246        match self {
247            Self::LimitIfTouched(order) => order.trigger_price,
248            Self::MarketIfTouched(order) => order.trigger_price,
249            Self::StopLimit(order) => order.trigger_price,
250            Self::StopMarket(order) => order.trigger_price,
251            Self::TrailingStopLimit(order) => order.trigger_price,
252            Self::TrailingStopMarket(order) => order.trigger_price,
253        }
254    }
255}
256
257// TODO: Derive equality
258impl PartialEq for StopOrderAny {
259    fn eq(&self, rhs: &Self) -> bool {
260        match self {
261            Self::LimitIfTouched(order) => order.client_order_id == rhs.client_order_id(),
262            Self::StopLimit(order) => order.client_order_id == rhs.client_order_id(),
263            Self::StopMarket(order) => order.client_order_id == rhs.client_order_id(),
264            Self::MarketIfTouched(order) => order.client_order_id == rhs.client_order_id(),
265            Self::TrailingStopLimit(order) => order.client_order_id == rhs.client_order_id(),
266            Self::TrailingStopMarket(order) => order.client_order_id == rhs.client_order_id(),
267        }
268    }
269}