Skip to main content

nautilus_execution/order_manager/
handlers.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//! Order management handler traits and implementations.
17//!
18//! These handlers enable the [`OrderManager`](super::manager::OrderManager) to dispatch order commands to
19//! components like the [`OrderEmulator`] for processing emulated orders.
20
21use nautilus_common::messages::execution::SubmitOrder;
22use nautilus_core::WeakCell;
23use nautilus_model::{orders::OrderAny, types::Quantity};
24
25use crate::order_emulator::emulator::OrderEmulator;
26
27pub trait SubmitOrderHandler {
28    fn handle_submit_order(&self, command: SubmitOrder);
29}
30
31/// Uses [`WeakCell`] to avoid circular references between components.
32#[derive(Clone, Debug)]
33pub enum SubmitOrderHandlerAny {
34    OrderEmulator(WeakCell<OrderEmulator>),
35}
36
37impl SubmitOrderHandler for SubmitOrderHandlerAny {
38    fn handle_submit_order(&self, command: SubmitOrder) {
39        match self {
40            Self::OrderEmulator(emulator_weak) => {
41                if let Some(emulator) = emulator_weak.upgrade() {
42                    emulator.borrow_mut().handle_submit_order(command);
43                }
44            }
45        }
46    }
47}
48
49pub trait CancelOrderHandler {
50    fn handle_cancel_order(&self, order: &OrderAny);
51}
52
53/// Uses [`WeakCell`] to avoid circular references between components.
54#[derive(Clone, Debug)]
55pub enum CancelOrderHandlerAny {
56    OrderEmulator(WeakCell<OrderEmulator>),
57}
58
59impl CancelOrderHandler for CancelOrderHandlerAny {
60    fn handle_cancel_order(&self, order: &OrderAny) {
61        match self {
62            Self::OrderEmulator(emulator_weak) => {
63                if let Some(emulator) = emulator_weak.upgrade() {
64                    emulator.borrow_mut().cancel_order(order);
65                }
66            }
67        }
68    }
69}
70
71pub trait ModifyOrderHandler {
72    fn handle_modify_order(&self, order: &OrderAny, new_quantity: Quantity);
73}
74
75/// Uses [`WeakCell`] to avoid circular references between components.
76#[derive(Clone, Debug)]
77pub enum ModifyOrderHandlerAny {
78    OrderEmulator(WeakCell<OrderEmulator>),
79}
80
81impl ModifyOrderHandler for ModifyOrderHandlerAny {
82    fn handle_modify_order(&self, order: &OrderAny, new_quantity: Quantity) {
83        match self {
84            Self::OrderEmulator(emulator_weak) => {
85                if let Some(emulator) = emulator_weak.upgrade() {
86                    let mut order_clone = order.clone();
87                    emulator
88                        .borrow_mut()
89                        .update_order(&mut order_clone, new_quantity);
90                }
91            }
92        }
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use std::{cell::RefCell, rc::Rc};
99
100    use nautilus_common::{cache::Cache, clock::TestClock};
101    use nautilus_core::{UUID4, WeakCell};
102    use nautilus_model::{
103        enums::{OrderSide, OrderType, TriggerType},
104        identifiers::{StrategyId, TraderId},
105        instruments::{Instrument, stubs::audusd_sim},
106        orders::{Order, OrderTestBuilder},
107        types::{Price, Quantity},
108    };
109    use rstest::rstest;
110
111    use super::*;
112    use crate::order_emulator::emulator::OrderEmulator;
113
114    fn create_test_emulator() -> Rc<RefCell<OrderEmulator>> {
115        let clock = Rc::new(RefCell::new(TestClock::new()));
116        let cache = Rc::new(RefCell::new(Cache::new(None, None)));
117
118        Rc::new(RefCell::new(OrderEmulator::new(clock, cache)))
119    }
120
121    fn create_test_stop_order(instrument: &dyn Instrument) -> OrderAny {
122        OrderTestBuilder::new(OrderType::StopMarket)
123            .instrument_id(instrument.id())
124            .side(OrderSide::Buy)
125            .trigger_price(Price::from("1.00050"))
126            .quantity(Quantity::from(100_000))
127            .emulation_trigger(TriggerType::BidAsk)
128            .build()
129    }
130
131    #[rstest]
132    fn test_submit_order_handler_constructs() {
133        let emulator = create_test_emulator();
134        let weak_emulator = WeakCell::from(Rc::downgrade(&emulator));
135        let handler = SubmitOrderHandlerAny::OrderEmulator(weak_emulator);
136
137        assert!(matches!(handler, SubmitOrderHandlerAny::OrderEmulator(_)));
138    }
139
140    #[rstest]
141    fn test_cancel_order_handler_constructs() {
142        let emulator = create_test_emulator();
143        let weak_emulator = WeakCell::from(Rc::downgrade(&emulator));
144        let handler = CancelOrderHandlerAny::OrderEmulator(weak_emulator);
145
146        assert!(matches!(handler, CancelOrderHandlerAny::OrderEmulator(_)));
147    }
148
149    #[rstest]
150    fn test_modify_order_handler_constructs() {
151        let emulator = create_test_emulator();
152        let weak_emulator = WeakCell::from(Rc::downgrade(&emulator));
153        let handler = ModifyOrderHandlerAny::OrderEmulator(weak_emulator);
154
155        assert!(matches!(handler, ModifyOrderHandlerAny::OrderEmulator(_)));
156    }
157
158    #[rstest]
159    fn test_cancel_order_handler_dispatch_does_not_panic() {
160        let emulator = create_test_emulator();
161        let weak_emulator = WeakCell::from(Rc::downgrade(&emulator));
162        let handler = CancelOrderHandlerAny::OrderEmulator(weak_emulator);
163        let instrument = audusd_sim();
164        let order = create_test_stop_order(&instrument);
165
166        handler.handle_cancel_order(&order);
167    }
168
169    #[rstest]
170    fn test_modify_order_handler_dispatch_does_not_panic() {
171        let emulator = create_test_emulator();
172        let weak_emulator = WeakCell::from(Rc::downgrade(&emulator));
173        let handler = ModifyOrderHandlerAny::OrderEmulator(weak_emulator);
174        let instrument = audusd_sim();
175        let order = create_test_stop_order(&instrument);
176        let new_quantity = Quantity::from(50_000);
177
178        handler.handle_modify_order(&order, new_quantity);
179    }
180
181    #[rstest]
182    fn test_handler_with_dropped_emulator_does_not_panic() {
183        let emulator = create_test_emulator();
184        let weak_emulator = WeakCell::from(Rc::downgrade(&emulator));
185        let handler = SubmitOrderHandlerAny::OrderEmulator(weak_emulator);
186        let instrument = audusd_sim();
187        let order = create_test_stop_order(&instrument);
188        let command = SubmitOrder::new(
189            TraderId::from("TESTER-001"),
190            None,
191            StrategyId::from("STRATEGY-001"),
192            instrument.id(),
193            order.client_order_id(),
194            order.init_event().clone(),
195            None,
196            None,
197            None,
198            UUID4::new(),
199            0.into(),
200        );
201        drop(emulator);
202
203        // WeakCell returns None when emulator is dropped
204        handler.handle_submit_order(command);
205    }
206}