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