nautilus_model/events/order/
updated.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::{Debug, Display};
17
18use derive_builder::Builder;
19use nautilus_core::{UUID4, UnixNanos, serialization::from_bool_as_u8};
20use rust_decimal::Decimal;
21use serde::{Deserialize, Serialize};
22use ustr::Ustr;
23
24use crate::{
25    enums::{
26        ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType,
27        TriggerType,
28    },
29    events::OrderEvent,
30    identifiers::{
31        AccountId, ClientOrderId, ExecAlgorithmId, InstrumentId, OrderListId, PositionId,
32        StrategyId, TradeId, TraderId, VenueOrderId,
33    },
34    types::{Currency, Money, Price, Quantity},
35};
36
37#[repr(C)]
38#[derive(Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, Builder)]
39#[builder(default)]
40#[serde(tag = "type")]
41#[cfg_attr(
42    feature = "python",
43    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
44)]
45pub struct OrderUpdated {
46    /// The trader ID associated with the event.
47    pub trader_id: TraderId,
48    /// The strategy ID associated with the event.
49    pub strategy_id: StrategyId,
50    /// The instrument ID associated with the event.
51    pub instrument_id: InstrumentId,
52    /// The client order ID associated with the event.
53    pub client_order_id: ClientOrderId,
54    /// The venue order ID associated with the event.
55    pub venue_order_id: Option<VenueOrderId>,
56    /// The account ID associated with the event.
57    pub account_id: Option<AccountId>,
58    /// The order quantity.
59    pub quantity: Quantity,
60    /// The order price (LIMIT).
61    pub price: Option<Price>,
62    /// The order trigger price (STOP).
63    pub trigger_price: Option<Price>,
64    /// The unique identifier for the event.
65    pub event_id: UUID4,
66    /// UNIX timestamp (nanoseconds) when the event occurred.
67    pub ts_event: UnixNanos,
68    /// UNIX timestamp (nanoseconds) when the event was initialized.
69    pub ts_init: UnixNanos,
70    /// If the event was generated during reconciliation.
71    #[serde(deserialize_with = "from_bool_as_u8")]
72    pub reconciliation: u8, // TODO: Change to bool once Cython removed
73}
74
75impl OrderUpdated {
76    /// Creates a new [`OrderUpdated`] instance.
77    #[allow(clippy::too_many_arguments)]
78    pub fn new(
79        trader_id: TraderId,
80        strategy_id: StrategyId,
81        instrument_id: InstrumentId,
82        client_order_id: ClientOrderId,
83        quantity: Quantity,
84        event_id: UUID4,
85        ts_event: UnixNanos,
86        ts_init: UnixNanos,
87        reconciliation: bool,
88        venue_order_id: Option<VenueOrderId>,
89        account_id: Option<AccountId>,
90        price: Option<Price>,
91        trigger_price: Option<Price>,
92    ) -> Self {
93        Self {
94            trader_id,
95            strategy_id,
96            instrument_id,
97            client_order_id,
98            quantity,
99            event_id,
100            ts_event,
101            ts_init,
102            reconciliation: u8::from(reconciliation),
103            venue_order_id,
104            account_id,
105            price,
106            trigger_price,
107        }
108    }
109}
110
111impl Debug for OrderUpdated {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        write!(
114            f,
115            "{}(trader_id={}, strategy_id={}, instrument_id={}, client_order_id={}, \
116            venue_order_id={}, account_id={}, quantity={}, price={}, trigger_price={}, event_id={}, ts_event={}, ts_init={})",
117            stringify!(OrderUpdated),
118            self.trader_id,
119            self.strategy_id,
120            self.instrument_id,
121            self.client_order_id,
122            self.venue_order_id
123                .map_or("None".to_string(), |venue_order_id| format!(
124                    "{venue_order_id}"
125                )),
126            self.account_id
127                .map_or("None".to_string(), |account_id| format!("{account_id}")),
128            self.quantity,
129            self.price
130                .map_or("None".to_string(), |price| price.to_formatted_string()),
131            self.trigger_price
132                .map_or("None".to_string(), |trigger_price| trigger_price
133                    .to_formatted_string()),
134            self.event_id,
135            self.ts_event,
136            self.ts_init
137        )
138    }
139}
140
141impl Display for OrderUpdated {
142    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143        write!(
144            f,
145            "{}(instrument_id={}, client_order_id={}, venue_order_id={}, account_id={}, quantity={}, price={}, trigger_price={}, ts_event={})",
146            stringify!(OrderUpdated),
147            self.instrument_id,
148            self.client_order_id,
149            self.venue_order_id
150                .map_or("None".to_string(), |venue_order_id| format!(
151                    "{venue_order_id}"
152                )),
153            self.account_id
154                .map_or("None".to_string(), |account_id| format!("{account_id}")),
155            self.quantity.to_formatted_string(),
156            self.price
157                .map_or("None".to_string(), |price| price.to_formatted_string()),
158            self.trigger_price
159                .map_or("None".to_string(), |trigger_price| trigger_price
160                    .to_formatted_string()),
161            self.ts_event
162        )
163    }
164}
165
166impl OrderEvent for OrderUpdated {
167    fn id(&self) -> UUID4 {
168        self.event_id
169    }
170
171    fn kind(&self) -> &str {
172        stringify!(OrderUpdated)
173    }
174
175    fn order_type(&self) -> Option<OrderType> {
176        None
177    }
178
179    fn order_side(&self) -> Option<OrderSide> {
180        None
181    }
182
183    fn trader_id(&self) -> TraderId {
184        self.trader_id
185    }
186
187    fn strategy_id(&self) -> StrategyId {
188        self.strategy_id
189    }
190
191    fn instrument_id(&self) -> InstrumentId {
192        self.instrument_id
193    }
194
195    fn trade_id(&self) -> Option<TradeId> {
196        None
197    }
198
199    fn currency(&self) -> Option<Currency> {
200        None
201    }
202
203    fn client_order_id(&self) -> ClientOrderId {
204        self.client_order_id
205    }
206
207    fn reason(&self) -> Option<Ustr> {
208        None
209    }
210
211    fn quantity(&self) -> Option<Quantity> {
212        Some(self.quantity)
213    }
214
215    fn time_in_force(&self) -> Option<TimeInForce> {
216        None
217    }
218
219    fn liquidity_side(&self) -> Option<LiquiditySide> {
220        None
221    }
222
223    fn post_only(&self) -> Option<bool> {
224        None
225    }
226
227    fn reduce_only(&self) -> Option<bool> {
228        None
229    }
230
231    fn quote_quantity(&self) -> Option<bool> {
232        None
233    }
234
235    fn reconciliation(&self) -> bool {
236        false
237    }
238
239    fn price(&self) -> Option<Price> {
240        self.price
241    }
242
243    fn last_px(&self) -> Option<Price> {
244        None
245    }
246
247    fn last_qty(&self) -> Option<Quantity> {
248        None
249    }
250
251    fn trigger_price(&self) -> Option<Price> {
252        self.trigger_price
253    }
254
255    fn trigger_type(&self) -> Option<TriggerType> {
256        None
257    }
258
259    fn limit_offset(&self) -> Option<Decimal> {
260        None
261    }
262
263    fn trailing_offset(&self) -> Option<Decimal> {
264        None
265    }
266
267    fn trailing_offset_type(&self) -> Option<TrailingOffsetType> {
268        None
269    }
270
271    fn expire_time(&self) -> Option<UnixNanos> {
272        None
273    }
274
275    fn display_qty(&self) -> Option<Quantity> {
276        None
277    }
278
279    fn emulation_trigger(&self) -> Option<TriggerType> {
280        None
281    }
282
283    fn trigger_instrument_id(&self) -> Option<InstrumentId> {
284        None
285    }
286
287    fn contingency_type(&self) -> Option<ContingencyType> {
288        None
289    }
290
291    fn order_list_id(&self) -> Option<OrderListId> {
292        None
293    }
294
295    fn linked_order_ids(&self) -> Option<Vec<ClientOrderId>> {
296        None
297    }
298
299    fn parent_order_id(&self) -> Option<ClientOrderId> {
300        None
301    }
302
303    fn exec_algorithm_id(&self) -> Option<ExecAlgorithmId> {
304        None
305    }
306
307    fn exec_spawn_id(&self) -> Option<ClientOrderId> {
308        None
309    }
310
311    fn venue_order_id(&self) -> Option<VenueOrderId> {
312        self.venue_order_id
313    }
314
315    fn account_id(&self) -> Option<AccountId> {
316        self.account_id
317    }
318
319    fn position_id(&self) -> Option<PositionId> {
320        None
321    }
322
323    fn commission(&self) -> Option<Money> {
324        None
325    }
326
327    fn ts_event(&self) -> UnixNanos {
328        self.ts_event
329    }
330
331    fn ts_init(&self) -> UnixNanos {
332        self.ts_init
333    }
334}
335
336////////////////////////////////////////////////////////////////////////////////
337// Tests
338////////////////////////////////////////////////////////////////////////////////
339#[cfg(test)]
340mod tests {
341    use rstest::rstest;
342
343    use crate::events::order::{stubs::*, updated::OrderUpdated};
344
345    #[rstest]
346    fn test_order_updated_display(order_updated: OrderUpdated) {
347        let display = format!("{order_updated}");
348        assert_eq!(
349            display,
350            "OrderUpdated(instrument_id=BTCUSDT.COINBASE, client_order_id=O-19700101-000000-001-001-1, venue_order_id=001, account_id=SIM-001, quantity=100, price=22_000, trigger_price=None, ts_event=0)"
351        );
352    }
353}