Skip to main content

nautilus_common/factories/
event.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 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
16//! Factory for generating order and account events.
17
18use nautilus_core::{UUID4, UnixNanos};
19use nautilus_model::{
20    enums::{AccountType, LiquiditySide},
21    events::{
22        AccountState, OrderAccepted, OrderCancelRejected, OrderCanceled, OrderDenied,
23        OrderEventAny, OrderExpired, OrderFilled, OrderModifyRejected, OrderRejected,
24        OrderSubmitted, OrderTriggered, OrderUpdated,
25    },
26    identifiers::{AccountId, PositionId, TradeId, TraderId, VenueOrderId},
27    orders::{Order, OrderAny},
28    types::{AccountBalance, Currency, MarginBalance, Money, Price, Quantity},
29};
30
31/// Factory for generating order and account events.
32///
33/// This struct holds the identity information needed to construct events and provides
34/// methods to generate all order event types. It is `Clone` and `Send`, allowing it
35/// to be used in async contexts.
36#[derive(Debug, Clone)]
37pub struct OrderEventFactory {
38    trader_id: TraderId,
39    account_id: AccountId,
40    account_type: AccountType,
41    base_currency: Option<Currency>,
42}
43
44impl OrderEventFactory {
45    /// Creates a new [`OrderEventFactory`] instance.
46    #[must_use]
47    pub fn new(
48        trader_id: TraderId,
49        account_id: AccountId,
50        account_type: AccountType,
51        base_currency: Option<Currency>,
52    ) -> Self {
53        Self {
54            trader_id,
55            account_id,
56            account_type,
57            base_currency,
58        }
59    }
60
61    /// Returns the trader ID.
62    #[must_use]
63    pub fn trader_id(&self) -> TraderId {
64        self.trader_id
65    }
66
67    /// Returns the account ID.
68    #[must_use]
69    pub fn account_id(&self) -> AccountId {
70        self.account_id
71    }
72
73    /// Generates an account state event.
74    #[must_use]
75    pub fn generate_account_state(
76        &self,
77        balances: Vec<AccountBalance>,
78        margins: Vec<MarginBalance>,
79        reported: bool,
80        ts_event: UnixNanos,
81        ts_init: UnixNanos,
82    ) -> AccountState {
83        AccountState::new(
84            self.account_id,
85            self.account_type,
86            balances,
87            margins,
88            reported,
89            UUID4::new(),
90            ts_event,
91            ts_init,
92            self.base_currency,
93        )
94    }
95
96    /// Generates an order denied event.
97    ///
98    /// The event timestamp `ts_event` is the same as the initialized timestamp `ts_init`.
99    #[must_use]
100    pub fn generate_order_denied(
101        &self,
102        order: &OrderAny,
103        reason: &str,
104        ts_init: UnixNanos,
105    ) -> OrderEventAny {
106        let event = OrderDenied::new(
107            self.trader_id,
108            order.strategy_id(),
109            order.instrument_id(),
110            order.client_order_id(),
111            reason.into(),
112            UUID4::new(),
113            ts_init,
114            ts_init,
115        );
116        OrderEventAny::Denied(event)
117    }
118
119    /// Generates an order submitted event.
120    ///
121    /// The event timestamp `ts_event` is the same as the initialized timestamp `ts_init`.
122    #[must_use]
123    pub fn generate_order_submitted(&self, order: &OrderAny, ts_init: UnixNanos) -> OrderEventAny {
124        let event = OrderSubmitted::new(
125            self.trader_id,
126            order.strategy_id(),
127            order.instrument_id(),
128            order.client_order_id(),
129            self.account_id,
130            UUID4::new(),
131            ts_init,
132            ts_init,
133        );
134        OrderEventAny::Submitted(event)
135    }
136
137    /// Generates an order rejected event.
138    #[must_use]
139    pub fn generate_order_rejected(
140        &self,
141        order: &OrderAny,
142        reason: &str,
143        ts_event: UnixNanos,
144        ts_init: UnixNanos,
145        due_post_only: bool,
146    ) -> OrderEventAny {
147        let event = OrderRejected::new(
148            self.trader_id,
149            order.strategy_id(),
150            order.instrument_id(),
151            order.client_order_id(),
152            self.account_id,
153            reason.into(),
154            UUID4::new(),
155            ts_event,
156            ts_init,
157            false,
158            due_post_only,
159        );
160        OrderEventAny::Rejected(event)
161    }
162
163    /// Generates an order accepted event.
164    #[must_use]
165    pub fn generate_order_accepted(
166        &self,
167        order: &OrderAny,
168        venue_order_id: VenueOrderId,
169        ts_event: UnixNanos,
170        ts_init: UnixNanos,
171    ) -> OrderEventAny {
172        let event = OrderAccepted::new(
173            self.trader_id,
174            order.strategy_id(),
175            order.instrument_id(),
176            order.client_order_id(),
177            venue_order_id,
178            self.account_id,
179            UUID4::new(),
180            ts_event,
181            ts_init,
182            false,
183        );
184        OrderEventAny::Accepted(event)
185    }
186
187    /// Generates an order modify rejected event.
188    #[must_use]
189    pub fn generate_order_modify_rejected(
190        &self,
191        order: &OrderAny,
192        venue_order_id: Option<VenueOrderId>,
193        reason: &str,
194        ts_event: UnixNanos,
195        ts_init: UnixNanos,
196    ) -> OrderEventAny {
197        let event = OrderModifyRejected::new(
198            self.trader_id,
199            order.strategy_id(),
200            order.instrument_id(),
201            order.client_order_id(),
202            reason.into(),
203            UUID4::new(),
204            ts_event,
205            ts_init,
206            false,
207            venue_order_id,
208            Some(self.account_id),
209        );
210        OrderEventAny::ModifyRejected(event)
211    }
212
213    /// Generates an order cancel rejected event.
214    #[must_use]
215    pub fn generate_order_cancel_rejected(
216        &self,
217        order: &OrderAny,
218        venue_order_id: Option<VenueOrderId>,
219        reason: &str,
220        ts_event: UnixNanos,
221        ts_init: UnixNanos,
222    ) -> OrderEventAny {
223        let event = OrderCancelRejected::new(
224            self.trader_id,
225            order.strategy_id(),
226            order.instrument_id(),
227            order.client_order_id(),
228            reason.into(),
229            UUID4::new(),
230            ts_event,
231            ts_init,
232            false,
233            venue_order_id,
234            Some(self.account_id),
235        );
236        OrderEventAny::CancelRejected(event)
237    }
238
239    /// Generates an order updated event.
240    #[expect(clippy::too_many_arguments)]
241    #[must_use]
242    pub fn generate_order_updated(
243        &self,
244        order: &OrderAny,
245        venue_order_id: VenueOrderId,
246        quantity: Quantity,
247        price: Option<Price>,
248        trigger_price: Option<Price>,
249        protection_price: Option<Price>,
250        ts_event: UnixNanos,
251        ts_init: UnixNanos,
252    ) -> OrderEventAny {
253        let event = OrderUpdated::new(
254            self.trader_id,
255            order.strategy_id(),
256            order.instrument_id(),
257            order.client_order_id(),
258            quantity,
259            UUID4::new(),
260            ts_event,
261            ts_init,
262            false,
263            Some(venue_order_id),
264            Some(self.account_id),
265            price,
266            trigger_price,
267            protection_price,
268            false, // is_quote_quantity
269        );
270        OrderEventAny::Updated(event)
271    }
272
273    /// Generates an order canceled event.
274    #[must_use]
275    pub fn generate_order_canceled(
276        &self,
277        order: &OrderAny,
278        venue_order_id: Option<VenueOrderId>,
279        ts_event: UnixNanos,
280        ts_init: UnixNanos,
281    ) -> OrderEventAny {
282        let event = OrderCanceled::new(
283            self.trader_id,
284            order.strategy_id(),
285            order.instrument_id(),
286            order.client_order_id(),
287            UUID4::new(),
288            ts_event,
289            ts_init,
290            false,
291            venue_order_id,
292            Some(self.account_id),
293        );
294        OrderEventAny::Canceled(event)
295    }
296
297    /// Generates an order triggered event.
298    #[must_use]
299    pub fn generate_order_triggered(
300        &self,
301        order: &OrderAny,
302        venue_order_id: Option<VenueOrderId>,
303        ts_event: UnixNanos,
304        ts_init: UnixNanos,
305    ) -> OrderEventAny {
306        let event = OrderTriggered::new(
307            self.trader_id,
308            order.strategy_id(),
309            order.instrument_id(),
310            order.client_order_id(),
311            UUID4::new(),
312            ts_event,
313            ts_init,
314            false,
315            venue_order_id,
316            Some(self.account_id),
317        );
318        OrderEventAny::Triggered(event)
319    }
320
321    /// Generates an order expired event.
322    #[must_use]
323    pub fn generate_order_expired(
324        &self,
325        order: &OrderAny,
326        venue_order_id: Option<VenueOrderId>,
327        ts_event: UnixNanos,
328        ts_init: UnixNanos,
329    ) -> OrderEventAny {
330        let event = OrderExpired::new(
331            self.trader_id,
332            order.strategy_id(),
333            order.instrument_id(),
334            order.client_order_id(),
335            UUID4::new(),
336            ts_event,
337            ts_init,
338            false,
339            venue_order_id,
340            Some(self.account_id),
341        );
342        OrderEventAny::Expired(event)
343    }
344
345    /// Generates an order filled event.
346    #[expect(clippy::too_many_arguments)]
347    #[must_use]
348    pub fn generate_order_filled(
349        &self,
350        order: &OrderAny,
351        venue_order_id: VenueOrderId,
352        venue_position_id: Option<PositionId>,
353        trade_id: TradeId,
354        last_qty: Quantity,
355        last_px: Price,
356        quote_currency: Currency,
357        commission: Option<Money>,
358        liquidity_side: LiquiditySide,
359        ts_event: UnixNanos,
360        ts_init: UnixNanos,
361    ) -> OrderEventAny {
362        let event = OrderFilled::new(
363            self.trader_id,
364            order.strategy_id(),
365            order.instrument_id(),
366            order.client_order_id(),
367            venue_order_id,
368            self.account_id,
369            trade_id,
370            order.order_side(),
371            order.order_type(),
372            last_qty,
373            last_px,
374            quote_currency,
375            liquidity_side,
376            UUID4::new(),
377            ts_event,
378            ts_init,
379            false,
380            venue_position_id,
381            commission,
382        );
383        OrderEventAny::Filled(event)
384    }
385}