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}