nautilus_execution/reports/
order.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 nautilus_core::{UUID4, UnixNanos};
19use nautilus_model::{
20    enums::{
21        ContingencyType, OrderSide, OrderStatus, OrderType, TimeInForce, TrailingOffsetType,
22        TriggerType,
23    },
24    identifiers::{AccountId, ClientOrderId, InstrumentId, OrderListId, PositionId, VenueOrderId},
25    types::{Price, Quantity},
26};
27use rust_decimal::Decimal;
28use serde::{Deserialize, Serialize};
29
30/// Represents an order status at a point in time.
31#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
32#[serde(tag = "type")]
33#[cfg_attr(
34    feature = "python",
35    pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.execution")
36)]
37pub struct OrderStatusReport {
38    /// The account ID associated with the position.
39    pub account_id: AccountId,
40    /// The instrument ID associated with the event.
41    pub instrument_id: InstrumentId,
42    /// The client order ID.
43    pub client_order_id: Option<ClientOrderId>,
44    /// The venue assigned order ID.
45    pub venue_order_id: VenueOrderId,
46    /// The order side.
47    pub order_side: OrderSide,
48    /// The order type.
49    pub order_type: OrderType,
50    /// The order time in force.
51    pub time_in_force: TimeInForce,
52    /// The order status.
53    pub order_status: OrderStatus,
54    /// The order quantity.
55    pub quantity: Quantity,
56    /// The order total filled quantity.
57    pub filled_qty: Quantity,
58    /// The unique identifier for the event.
59    pub report_id: UUID4,
60    /// UNIX timestamp (nanoseconds) when the order was accepted.
61    pub ts_accepted: UnixNanos,
62    /// UNIX timestamp (nanoseconds) when the last event occurred.
63    pub ts_last: UnixNanos,
64    /// UNIX timestamp (nanoseconds) when the event was initialized.
65    pub ts_init: UnixNanos,
66    /// The order list ID associated with the order.
67    pub order_list_id: Option<OrderListId>,
68    /// The position ID associated with the order (assigned by the venue).
69    pub venue_position_id: Option<PositionId>,
70    /// The orders contingency type.
71    pub contingency_type: ContingencyType,
72    /// The order expiration (UNIX epoch nanoseconds), zero for no expiration.
73    pub expire_time: Option<UnixNanos>,
74    /// The order price (LIMIT).
75    pub price: Option<Price>,
76    /// The order trigger price (STOP).
77    pub trigger_price: Option<Price>,
78    /// The trigger type for the order.
79    pub trigger_type: Option<TriggerType>,
80    /// The trailing offset for the orders limit price.
81    pub limit_offset: Option<Decimal>,
82    /// The trailing offset for the orders trigger price (STOP).
83    pub trailing_offset: Option<Decimal>,
84    /// The trailing offset type.
85    pub trailing_offset_type: TrailingOffsetType,
86    /// The order average fill price.
87    pub avg_px: Option<f64>,
88    /// The quantity of the `LIMIT` order to display on the public book (iceberg).
89    pub display_qty: Option<Quantity>,
90    /// If the order will only provide liquidity (make a market).
91    pub post_only: bool,
92    /// If the order carries the 'reduce-only' execution instruction.
93    pub reduce_only: bool,
94    /// The reason for order cancellation.
95    pub cancel_reason: Option<String>,
96    /// UNIX timestamp (nanoseconds) when the order was triggered.
97    pub ts_triggered: Option<UnixNanos>,
98}
99
100impl OrderStatusReport {
101    /// Creates a new [`OrderStatusReport`] instance with required fields.
102    #[allow(clippy::too_many_arguments)]
103    #[must_use]
104    pub fn new(
105        account_id: AccountId,
106        instrument_id: InstrumentId,
107        client_order_id: Option<ClientOrderId>,
108        venue_order_id: VenueOrderId,
109        order_side: OrderSide,
110        order_type: OrderType,
111        time_in_force: TimeInForce,
112        order_status: OrderStatus,
113        quantity: Quantity,
114        filled_qty: Quantity,
115        ts_accepted: UnixNanos,
116        ts_last: UnixNanos,
117        ts_init: UnixNanos,
118        report_id: Option<UUID4>,
119    ) -> Self {
120        Self {
121            account_id,
122            instrument_id,
123            client_order_id,
124            venue_order_id,
125            order_side,
126            order_type,
127            time_in_force,
128            order_status,
129            quantity,
130            filled_qty,
131            report_id: report_id.unwrap_or_default(),
132            ts_accepted,
133            ts_last,
134            ts_init,
135            order_list_id: None,
136            venue_position_id: None,
137            contingency_type: ContingencyType::default(),
138            expire_time: None,
139            price: None,
140            trigger_price: None,
141            trigger_type: None,
142            limit_offset: None,
143            trailing_offset: None,
144            trailing_offset_type: TrailingOffsetType::default(),
145            avg_px: None,
146            display_qty: None,
147            post_only: false,
148            reduce_only: false,
149            cancel_reason: None,
150            ts_triggered: None,
151        }
152    }
153
154    /// Sets the client order ID.
155    #[must_use]
156    pub const fn with_client_order_id(mut self, client_order_id: ClientOrderId) -> Self {
157        self.client_order_id = Some(client_order_id);
158        self
159    }
160
161    /// Sets the order list ID.
162    #[must_use]
163    pub const fn with_order_list_id(mut self, order_list_id: OrderListId) -> Self {
164        self.order_list_id = Some(order_list_id);
165        self
166    }
167
168    /// Sets the venue position ID.
169    #[must_use]
170    pub const fn with_venue_position_id(mut self, venue_position_id: PositionId) -> Self {
171        self.venue_position_id = Some(venue_position_id);
172        self
173    }
174
175    /// Sets the price.
176    #[must_use]
177    pub const fn with_price(mut self, price: Price) -> Self {
178        self.price = Some(price);
179        self
180    }
181
182    /// Sets the average price.
183    #[must_use]
184    pub const fn with_avg_px(mut self, avg_px: f64) -> Self {
185        self.avg_px = Some(avg_px);
186        self
187    }
188
189    /// Sets the trigger price.
190    #[must_use]
191    pub const fn with_trigger_price(mut self, trigger_price: Price) -> Self {
192        self.trigger_price = Some(trigger_price);
193        self
194    }
195
196    /// Sets the trigger type.
197    #[must_use]
198    pub const fn with_trigger_type(mut self, trigger_type: TriggerType) -> Self {
199        self.trigger_type = Some(trigger_type);
200        self
201    }
202
203    /// Sets the limit offset.
204    #[must_use]
205    pub const fn with_limit_offset(mut self, limit_offset: Decimal) -> Self {
206        self.limit_offset = Some(limit_offset);
207        self
208    }
209
210    /// Sets the trailing offset.
211    #[must_use]
212    pub const fn with_trailing_offset(mut self, trailing_offset: Decimal) -> Self {
213        self.trailing_offset = Some(trailing_offset);
214        self
215    }
216
217    /// Sets the trailing offset type.
218    #[must_use]
219    pub const fn with_trailing_offset_type(
220        mut self,
221        trailing_offset_type: TrailingOffsetType,
222    ) -> Self {
223        self.trailing_offset_type = trailing_offset_type;
224        self
225    }
226
227    /// Sets the display quantity.
228    #[must_use]
229    pub const fn with_display_qty(mut self, display_qty: Quantity) -> Self {
230        self.display_qty = Some(display_qty);
231        self
232    }
233
234    /// Sets the expire time.
235    #[must_use]
236    pub const fn with_expire_time(mut self, expire_time: UnixNanos) -> Self {
237        self.expire_time = Some(expire_time);
238        self
239    }
240
241    /// Sets `post_only` flag.
242    #[must_use]
243    pub const fn with_post_only(mut self, post_only: bool) -> Self {
244        self.post_only = post_only;
245        self
246    }
247
248    /// Sets `reduce_only` flag.
249    #[must_use]
250    pub const fn with_reduce_only(mut self, reduce_only: bool) -> Self {
251        self.reduce_only = reduce_only;
252        self
253    }
254
255    /// Sets cancel reason.
256    #[must_use]
257    pub fn with_cancel_reason(mut self, cancel_reason: String) -> Self {
258        self.cancel_reason = Some(cancel_reason);
259        self
260    }
261
262    /// Sets the triggered timestamp.
263    #[must_use]
264    pub const fn with_ts_triggered(mut self, ts_triggered: UnixNanos) -> Self {
265        self.ts_triggered = Some(ts_triggered);
266        self
267    }
268
269    /// Sets the contingency type.
270    #[must_use]
271    pub const fn with_contingency_type(mut self, contingency_type: ContingencyType) -> Self {
272        self.contingency_type = contingency_type;
273        self
274    }
275}
276
277impl Display for OrderStatusReport {
278    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
279        write!(
280            f,
281            "OrderStatusReport(\
282                account_id={}, \
283                instrument_id={}, \
284                venue_order_id={}, \
285                order_side={}, \
286                order_type={}, \
287                time_in_force={}, \
288                order_status={}, \
289                quantity={}, \
290                filled_qty={}, \
291                report_id={}, \
292                ts_accepted={}, \
293                ts_last={}, \
294                ts_init={}, \
295                client_order_id={:?}, \
296                order_list_id={:?}, \
297                venue_position_id={:?}, \
298                contingency_type={}, \
299                expire_time={:?}, \
300                price={:?}, \
301                trigger_price={:?}, \
302                trigger_type={:?}, \
303                limit_offset={:?}, \
304                trailing_offset={:?}, \
305                trailing_offset_type={}, \
306                avg_px={:?}, \
307                display_qty={:?}, \
308                post_only={}, \
309                reduce_only={}, \
310                cancel_reason={:?}, \
311                ts_triggered={:?}\
312            )",
313            self.account_id,
314            self.instrument_id,
315            self.venue_order_id,
316            self.order_side,
317            self.order_type,
318            self.time_in_force,
319            self.order_status,
320            self.quantity,
321            self.filled_qty,
322            self.report_id,
323            self.ts_accepted,
324            self.ts_last,
325            self.ts_init,
326            self.client_order_id,
327            self.order_list_id,
328            self.venue_position_id,
329            self.contingency_type,
330            self.expire_time,
331            self.price,
332            self.trigger_price,
333            self.trigger_type,
334            self.limit_offset,
335            self.trailing_offset,
336            self.trailing_offset_type,
337            self.avg_px,
338            self.display_qty,
339            self.post_only,
340            self.reduce_only,
341            self.cancel_reason,
342            self.ts_triggered,
343        )
344    }
345}