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::{Params, 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    /// Sets the account ID.
74    pub const fn set_account_id(&mut self, account_id: AccountId) {
75        self.account_id = account_id;
76    }
77
78    /// Generates an account state event.
79    #[must_use]
80    pub fn generate_account_state(
81        &self,
82        balances: Vec<AccountBalance>,
83        margins: Vec<MarginBalance>,
84        reported: bool,
85        ts_event: UnixNanos,
86        ts_init: UnixNanos,
87        info: Option<Params>,
88    ) -> AccountState {
89        AccountState::new(
90            self.account_id,
91            self.account_type,
92            balances,
93            margins,
94            reported,
95            UUID4::new(),
96            ts_event,
97            ts_init,
98            self.base_currency,
99        )
100        .with_info(info)
101    }
102
103    /// Generates an order denied event.
104    ///
105    /// The event timestamp `ts_event` is the same as the initialized timestamp `ts_init`.
106    #[must_use]
107    pub fn generate_order_denied(
108        &self,
109        order: &OrderAny,
110        reason: &str,
111        ts_init: UnixNanos,
112    ) -> OrderEventAny {
113        let event = OrderDenied::new(
114            self.trader_id,
115            order.strategy_id(),
116            order.instrument_id(),
117            order.client_order_id(),
118            reason.into(),
119            UUID4::new(),
120            ts_init,
121            ts_init,
122        );
123        OrderEventAny::Denied(event)
124    }
125
126    /// Generates an order submitted event.
127    ///
128    /// The event timestamp `ts_event` is the same as the initialized timestamp `ts_init`.
129    #[must_use]
130    pub fn generate_order_submitted(&self, order: &OrderAny, ts_init: UnixNanos) -> OrderEventAny {
131        let event = OrderSubmitted::new(
132            self.trader_id,
133            order.strategy_id(),
134            order.instrument_id(),
135            order.client_order_id(),
136            self.account_id,
137            UUID4::new(),
138            ts_init,
139            ts_init,
140        );
141        OrderEventAny::Submitted(event)
142    }
143
144    /// Generates an order rejected event.
145    #[must_use]
146    pub fn generate_order_rejected(
147        &self,
148        order: &OrderAny,
149        reason: &str,
150        ts_event: UnixNanos,
151        ts_init: UnixNanos,
152        due_post_only: bool,
153    ) -> OrderEventAny {
154        let event = OrderRejected::new(
155            self.trader_id,
156            order.strategy_id(),
157            order.instrument_id(),
158            order.client_order_id(),
159            self.account_id,
160            reason.into(),
161            UUID4::new(),
162            ts_event,
163            ts_init,
164            false,
165            due_post_only,
166        );
167        OrderEventAny::Rejected(event)
168    }
169
170    /// Generates an order accepted event.
171    #[must_use]
172    pub fn generate_order_accepted(
173        &self,
174        order: &OrderAny,
175        venue_order_id: VenueOrderId,
176        ts_event: UnixNanos,
177        ts_init: UnixNanos,
178    ) -> OrderEventAny {
179        let event = OrderAccepted::new(
180            self.trader_id,
181            order.strategy_id(),
182            order.instrument_id(),
183            order.client_order_id(),
184            venue_order_id,
185            self.account_id,
186            UUID4::new(),
187            ts_event,
188            ts_init,
189            false,
190        );
191        OrderEventAny::Accepted(event)
192    }
193
194    /// Generates an order modify rejected event.
195    #[must_use]
196    pub fn generate_order_modify_rejected(
197        &self,
198        order: &OrderAny,
199        venue_order_id: Option<VenueOrderId>,
200        reason: &str,
201        ts_event: UnixNanos,
202        ts_init: UnixNanos,
203    ) -> OrderEventAny {
204        let event = OrderModifyRejected::new(
205            self.trader_id,
206            order.strategy_id(),
207            order.instrument_id(),
208            order.client_order_id(),
209            reason.into(),
210            UUID4::new(),
211            ts_event,
212            ts_init,
213            false,
214            venue_order_id,
215            Some(self.account_id),
216        );
217        OrderEventAny::ModifyRejected(event)
218    }
219
220    /// Generates an order cancel rejected event.
221    #[must_use]
222    pub fn generate_order_cancel_rejected(
223        &self,
224        order: &OrderAny,
225        venue_order_id: Option<VenueOrderId>,
226        reason: &str,
227        ts_event: UnixNanos,
228        ts_init: UnixNanos,
229    ) -> OrderEventAny {
230        let event = OrderCancelRejected::new(
231            self.trader_id,
232            order.strategy_id(),
233            order.instrument_id(),
234            order.client_order_id(),
235            reason.into(),
236            UUID4::new(),
237            ts_event,
238            ts_init,
239            false,
240            venue_order_id,
241            Some(self.account_id),
242        );
243        OrderEventAny::CancelRejected(event)
244    }
245
246    /// Generates an order updated event.
247    #[expect(clippy::too_many_arguments)]
248    #[must_use]
249    pub fn generate_order_updated(
250        &self,
251        order: &OrderAny,
252        venue_order_id: VenueOrderId,
253        quantity: Quantity,
254        price: Option<Price>,
255        trigger_price: Option<Price>,
256        protection_price: Option<Price>,
257        ts_event: UnixNanos,
258        ts_init: UnixNanos,
259    ) -> OrderEventAny {
260        let event = OrderUpdated::new(
261            self.trader_id,
262            order.strategy_id(),
263            order.instrument_id(),
264            order.client_order_id(),
265            quantity,
266            UUID4::new(),
267            ts_event,
268            ts_init,
269            false,
270            Some(venue_order_id),
271            Some(self.account_id),
272            price,
273            trigger_price,
274            protection_price,
275            false, // is_quote_quantity
276        );
277        OrderEventAny::Updated(event)
278    }
279
280    /// Generates an order canceled event.
281    #[must_use]
282    pub fn generate_order_canceled(
283        &self,
284        order: &OrderAny,
285        venue_order_id: Option<VenueOrderId>,
286        ts_event: UnixNanos,
287        ts_init: UnixNanos,
288    ) -> OrderEventAny {
289        let event = OrderCanceled::new(
290            self.trader_id,
291            order.strategy_id(),
292            order.instrument_id(),
293            order.client_order_id(),
294            UUID4::new(),
295            ts_event,
296            ts_init,
297            false,
298            venue_order_id,
299            Some(self.account_id),
300        );
301        OrderEventAny::Canceled(event)
302    }
303
304    /// Generates an order triggered event.
305    #[must_use]
306    pub fn generate_order_triggered(
307        &self,
308        order: &OrderAny,
309        venue_order_id: Option<VenueOrderId>,
310        ts_event: UnixNanos,
311        ts_init: UnixNanos,
312    ) -> OrderEventAny {
313        let event = OrderTriggered::new(
314            self.trader_id,
315            order.strategy_id(),
316            order.instrument_id(),
317            order.client_order_id(),
318            UUID4::new(),
319            ts_event,
320            ts_init,
321            false,
322            venue_order_id,
323            Some(self.account_id),
324        );
325        OrderEventAny::Triggered(event)
326    }
327
328    /// Generates an order expired event.
329    #[must_use]
330    pub fn generate_order_expired(
331        &self,
332        order: &OrderAny,
333        venue_order_id: Option<VenueOrderId>,
334        ts_event: UnixNanos,
335        ts_init: UnixNanos,
336    ) -> OrderEventAny {
337        let event = OrderExpired::new(
338            self.trader_id,
339            order.strategy_id(),
340            order.instrument_id(),
341            order.client_order_id(),
342            UUID4::new(),
343            ts_event,
344            ts_init,
345            false,
346            venue_order_id,
347            Some(self.account_id),
348        );
349        OrderEventAny::Expired(event)
350    }
351
352    /// Generates an order filled event.
353    #[expect(clippy::too_many_arguments)]
354    #[must_use]
355    pub fn generate_order_filled(
356        &self,
357        order: &OrderAny,
358        venue_order_id: VenueOrderId,
359        venue_position_id: Option<PositionId>,
360        trade_id: TradeId,
361        last_qty: Quantity,
362        last_px: Price,
363        quote_currency: Currency,
364        commission: Option<Money>,
365        liquidity_side: LiquiditySide,
366        ts_event: UnixNanos,
367        ts_init: UnixNanos,
368    ) -> OrderEventAny {
369        let event = OrderFilled::new(
370            self.trader_id,
371            order.strategy_id(),
372            order.instrument_id(),
373            order.client_order_id(),
374            venue_order_id,
375            self.account_id,
376            trade_id,
377            order.order_side(),
378            order.order_type(),
379            last_qty,
380            last_px,
381            quote_currency,
382            liquidity_side,
383            UUID4::new(),
384            ts_event,
385            ts_init,
386            false,
387            venue_position_id,
388            commission,
389            None,
390        );
391        OrderEventAny::Filled(event)
392    }
393}