Skip to main content

nautilus_common/clients/
execution.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//! Execution client trait definition.
17
18use async_trait::async_trait;
19use nautilus_core::UnixNanos;
20use nautilus_model::{
21    accounts::AccountAny,
22    enums::{LiquiditySide, OmsType},
23    identifiers::{
24        AccountId, ClientId, ClientOrderId, InstrumentId, StrategyId, Venue, VenueOrderId,
25    },
26    instruments::InstrumentAny,
27    reports::{ExecutionMassStatus, FillReport, OrderStatusReport, PositionStatusReport},
28    types::{AccountBalance, MarginBalance, Money, Price, Quantity},
29};
30
31use super::log_not_implemented;
32use crate::messages::execution::{
33    BatchCancelOrders, CancelAllOrders, CancelOrder, GenerateFillReports,
34    GenerateOrderStatusReport, GenerateOrderStatusReports, GeneratePositionStatusReports,
35    ModifyOrder, QueryAccount, QueryOrder, SubmitOrder, SubmitOrderList,
36};
37
38/// Defines the interface for an execution client managing order operations.
39///
40/// # Thread Safety
41///
42/// Client instances are not intended to be sent across threads. The `?Send` bound
43/// allows implementations to hold non-Send state for any Python interop.
44#[async_trait(?Send)]
45pub trait ExecutionClient {
46    fn is_connected(&self) -> bool;
47    fn client_id(&self) -> ClientId;
48    fn account_id(&self) -> AccountId;
49    fn venue(&self) -> Venue;
50    fn oms_type(&self) -> OmsType;
51    fn get_account(&self) -> Option<AccountAny>;
52
53    /// Returns whether this client can execute orders for the given instrument venue.
54    ///
55    /// Single-venue clients should use the default behavior. Routing brokers can
56    /// override this when their client venue identifies the broker rather than
57    /// the instrument's exchange venue.
58    fn handles_order_venue(&self, venue: Venue) -> bool {
59        self.venue() == venue
60    }
61
62    /// Generates and publishes the account state event.
63    ///
64    /// # Errors
65    ///
66    /// Returns an error if generating the account state fails.
67    fn generate_account_state(
68        &self,
69        balances: Vec<AccountBalance>,
70        margins: Vec<MarginBalance>,
71        reported: bool,
72        ts_event: UnixNanos,
73    ) -> anyhow::Result<()>;
74
75    /// Starts the execution client.
76    ///
77    /// # Errors
78    ///
79    /// Returns an error if the client fails to start.
80    fn start(&mut self) -> anyhow::Result<()>;
81
82    /// Stops the execution client.
83    ///
84    /// Implementations must be idempotent: the engine and node teardown paths
85    /// (e.g. backtest `end` -> `reset` -> `dispose`) may call `stop()` more
86    /// than once per run. Guard with an internal `is_stopped` check or
87    /// equivalent so repeated calls are safe.
88    ///
89    /// # Errors
90    ///
91    /// Returns an error if the client fails to stop.
92    fn stop(&mut self) -> anyhow::Result<()>;
93
94    /// Resets the execution client to its initial state.
95    ///
96    /// The default implementation is a no-op. Adapters with reconnectable state
97    /// (caches, sequence counters, in-flight orders) should override this.
98    ///
99    /// # Errors
100    ///
101    /// Returns an error if the client fails to reset.
102    fn reset(&mut self) -> anyhow::Result<()> {
103        Ok(())
104    }
105
106    /// Disposes of client resources and cleans up.
107    ///
108    /// The default implementation is a no-op. Adapters that hold async tasks,
109    /// background threads, or external handles should override this.
110    ///
111    /// # Errors
112    ///
113    /// Returns an error if the client fails to dispose.
114    fn dispose(&mut self) -> anyhow::Result<()> {
115        Ok(())
116    }
117
118    /// Connects the client to the execution venue.
119    ///
120    /// # Errors
121    ///
122    /// Returns an error if connection fails.
123    async fn connect(&mut self) -> anyhow::Result<()> {
124        Ok(())
125    }
126
127    /// Disconnects the client from the execution venue.
128    ///
129    /// # Errors
130    ///
131    /// Returns an error if disconnection fails.
132    async fn disconnect(&mut self) -> anyhow::Result<()> {
133        Ok(())
134    }
135
136    /// Submits a single order command to the execution venue.
137    ///
138    /// # Errors
139    ///
140    /// Returns an error if submission fails.
141    fn submit_order(&self, cmd: SubmitOrder) -> anyhow::Result<()> {
142        log_not_implemented(&cmd);
143        Ok(())
144    }
145
146    /// Submits a list of orders to the execution venue.
147    ///
148    /// # Errors
149    ///
150    /// Returns an error if submission fails.
151    fn submit_order_list(&self, cmd: SubmitOrderList) -> anyhow::Result<()> {
152        log_not_implemented(&cmd);
153        Ok(())
154    }
155
156    /// Modifies an existing order.
157    ///
158    /// # Errors
159    ///
160    /// Returns an error if modification fails.
161    fn modify_order(&self, cmd: ModifyOrder) -> anyhow::Result<()> {
162        log_not_implemented(&cmd);
163        Ok(())
164    }
165
166    /// Cancels a specific order.
167    ///
168    /// # Errors
169    ///
170    /// Returns an error if cancellation fails.
171    fn cancel_order(&self, cmd: CancelOrder) -> anyhow::Result<()> {
172        log_not_implemented(&cmd);
173        Ok(())
174    }
175
176    /// Cancels all orders.
177    ///
178    /// # Errors
179    ///
180    /// Returns an error if cancellation fails.
181    fn cancel_all_orders(&self, cmd: CancelAllOrders) -> anyhow::Result<()> {
182        log_not_implemented(&cmd);
183        Ok(())
184    }
185
186    /// Cancels a batch of orders.
187    ///
188    /// # Errors
189    ///
190    /// Returns an error if batch cancellation fails.
191    fn batch_cancel_orders(&self, cmd: BatchCancelOrders) -> anyhow::Result<()> {
192        log_not_implemented(&cmd);
193        Ok(())
194    }
195
196    /// Queries the status of an account.
197    ///
198    /// # Errors
199    ///
200    /// Returns an error if the query fails.
201    fn query_account(&self, cmd: QueryAccount) -> anyhow::Result<()> {
202        log_not_implemented(&cmd);
203        Ok(())
204    }
205
206    /// Queries the status of an order.
207    ///
208    /// # Errors
209    ///
210    /// Returns an error if the query fails.
211    fn query_order(&self, cmd: QueryOrder) -> anyhow::Result<()> {
212        log_not_implemented(&cmd);
213        Ok(())
214    }
215
216    /// Generates a single order status report.
217    ///
218    /// # Errors
219    ///
220    /// Returns an error if report generation fails.
221    async fn generate_order_status_report(
222        &self,
223        cmd: &GenerateOrderStatusReport,
224    ) -> anyhow::Result<Option<OrderStatusReport>> {
225        log_not_implemented(cmd);
226        Ok(None)
227    }
228
229    /// Generates multiple order status reports.
230    ///
231    /// # Errors
232    ///
233    /// Returns an error if report generation fails.
234    async fn generate_order_status_reports(
235        &self,
236        cmd: &GenerateOrderStatusReports,
237    ) -> anyhow::Result<Vec<OrderStatusReport>> {
238        log_not_implemented(cmd);
239        Ok(Vec::new())
240    }
241
242    /// Generates fill reports based on execution results.
243    ///
244    /// # Errors
245    ///
246    /// Returns an error if fill report generation fails.
247    async fn generate_fill_reports(
248        &self,
249        cmd: GenerateFillReports,
250    ) -> anyhow::Result<Vec<FillReport>> {
251        log_not_implemented(&cmd);
252        Ok(Vec::new())
253    }
254
255    /// Generates position status reports.
256    ///
257    /// # Errors
258    ///
259    /// Returns an error if generation fails.
260    async fn generate_position_status_reports(
261        &self,
262        cmd: &GeneratePositionStatusReports,
263    ) -> anyhow::Result<Vec<PositionStatusReport>> {
264        log_not_implemented(cmd);
265        Ok(Vec::new())
266    }
267
268    /// Generates mass status for executions.
269    ///
270    /// # Errors
271    ///
272    /// Returns an error if status generation fails.
273    async fn generate_mass_status(
274        &self,
275        lookback_mins: Option<u64>,
276    ) -> anyhow::Result<Option<ExecutionMassStatus>> {
277        log_not_implemented(&lookback_mins);
278        Ok(None)
279    }
280
281    /// Registers an external order for tracking by the execution client.
282    ///
283    /// This is called after reconciliation creates an external order, allowing the
284    /// execution client to track it for subsequent events (e.g., cancellations).
285    fn register_external_order(
286        &self,
287        _client_order_id: ClientOrderId,
288        _venue_order_id: VenueOrderId,
289        _instrument_id: InstrumentId,
290        _strategy_id: StrategyId,
291        _ts_init: UnixNanos,
292    ) {
293        // Default no-op implementation
294    }
295
296    /// Handles an instrument update received via the message bus.
297    ///
298    /// Exec clients that need live instrument updates (e.g. for internal maps)
299    /// can override this to process instruments for their venue.
300    fn on_instrument(&mut self, _instrument: InstrumentAny) {
301        // Default no-op
302    }
303
304    /// Calculates the commission for a reconciliation fill.
305    ///
306    /// Override this method to provide venue-specific commission logic
307    /// for inferred fills generated during reconciliation.
308    ///
309    /// Returns `None` by default, signaling callers to use their own
310    /// generic commission formula.
311    #[expect(unused_variables)]
312    fn calculate_commission(
313        &self,
314        instrument: &InstrumentAny,
315        last_qty: Quantity,
316        last_px: Price,
317        liquidity_side: LiquiditySide,
318    ) -> Option<Money> {
319        None
320    }
321}