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    #[allow(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        );
269        OrderEventAny::Updated(event)
270    }
271
272    /// Generates an order canceled event.
273    #[must_use]
274    pub fn generate_order_canceled(
275        &self,
276        order: &OrderAny,
277        venue_order_id: Option<VenueOrderId>,
278        ts_event: UnixNanos,
279        ts_init: UnixNanos,
280    ) -> OrderEventAny {
281        let event = OrderCanceled::new(
282            self.trader_id,
283            order.strategy_id(),
284            order.instrument_id(),
285            order.client_order_id(),
286            UUID4::new(),
287            ts_event,
288            ts_init,
289            false,
290            venue_order_id,
291            Some(self.account_id),
292        );
293        OrderEventAny::Canceled(event)
294    }
295
296    /// Generates an order triggered event.
297    #[must_use]
298    pub fn generate_order_triggered(
299        &self,
300        order: &OrderAny,
301        venue_order_id: Option<VenueOrderId>,
302        ts_event: UnixNanos,
303        ts_init: UnixNanos,
304    ) -> OrderEventAny {
305        let event = OrderTriggered::new(
306            self.trader_id,
307            order.strategy_id(),
308            order.instrument_id(),
309            order.client_order_id(),
310            UUID4::new(),
311            ts_event,
312            ts_init,
313            false,
314            venue_order_id,
315            Some(self.account_id),
316        );
317        OrderEventAny::Triggered(event)
318    }
319
320    /// Generates an order expired event.
321    #[must_use]
322    pub fn generate_order_expired(
323        &self,
324        order: &OrderAny,
325        venue_order_id: Option<VenueOrderId>,
326        ts_event: UnixNanos,
327        ts_init: UnixNanos,
328    ) -> OrderEventAny {
329        let event = OrderExpired::new(
330            self.trader_id,
331            order.strategy_id(),
332            order.instrument_id(),
333            order.client_order_id(),
334            UUID4::new(),
335            ts_event,
336            ts_init,
337            false,
338            venue_order_id,
339            Some(self.account_id),
340        );
341        OrderEventAny::Expired(event)
342    }
343
344    /// Generates an order filled event.
345    #[allow(clippy::too_many_arguments)]
346    #[must_use]
347    pub fn generate_order_filled(
348        &self,
349        order: &OrderAny,
350        venue_order_id: VenueOrderId,
351        venue_position_id: Option<PositionId>,
352        trade_id: TradeId,
353        last_qty: Quantity,
354        last_px: Price,
355        quote_currency: Currency,
356        commission: Option<Money>,
357        liquidity_side: LiquiditySide,
358        ts_event: UnixNanos,
359        ts_init: UnixNanos,
360    ) -> OrderEventAny {
361        let event = OrderFilled::new(
362            self.trader_id,
363            order.strategy_id(),
364            order.instrument_id(),
365            order.client_order_id(),
366            venue_order_id,
367            self.account_id,
368            trade_id,
369            order.order_side(),
370            order.order_type(),
371            last_qty,
372            last_px,
373            quote_currency,
374            liquidity_side,
375            UUID4::new(),
376            ts_event,
377            ts_init,
378            false,
379            venue_position_id,
380            commission,
381        );
382        OrderEventAny::Filled(event)
383    }
384}