nautilus_execution/client/
mod.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 implementations for trading venue connectivity.
17
18use std::{
19    fmt::Debug,
20    ops::{Deref, DerefMut},
21};
22
23use async_trait::async_trait;
24use nautilus_common::messages::execution::{
25    BatchCancelOrders, CancelAllOrders, CancelOrder, GenerateFillReports,
26    GenerateOrderStatusReport, GenerateOrderStatusReports, GeneratePositionStatusReports,
27    ModifyOrder, QueryAccount, QueryOrder, SubmitOrder, SubmitOrderList,
28};
29use nautilus_core::UnixNanos;
30use nautilus_model::{
31    accounts::AccountAny,
32    enums::OmsType,
33    identifiers::{AccountId, ClientId, Venue},
34    reports::{ExecutionMassStatus, FillReport, OrderStatusReport, PositionStatusReport},
35    types::{AccountBalance, MarginBalance},
36};
37
38pub mod base;
39
40/// Defines the interface for an execution client managing order operations.
41///
42/// # Thread Safety
43///
44/// Client instances are not intended to be sent across threads. The `?Send` bound
45/// allows implementations to hold non-Send state for any Python interop.
46#[async_trait(?Send)]
47pub trait ExecutionClient {
48    fn is_connected(&self) -> bool;
49    fn client_id(&self) -> ClientId;
50    fn account_id(&self) -> AccountId;
51    fn venue(&self) -> Venue;
52    fn oms_type(&self) -> OmsType;
53    fn get_account(&self) -> Option<AccountAny>;
54
55    /// Generates and publishes the account state event.
56    ///
57    /// # Errors
58    ///
59    /// Returns an error if generating the account state fails.
60    fn generate_account_state(
61        &self,
62        balances: Vec<AccountBalance>,
63        margins: Vec<MarginBalance>,
64        reported: bool,
65        ts_event: UnixNanos,
66    ) -> anyhow::Result<()>;
67
68    /// Starts the execution client.
69    ///
70    /// # Errors
71    ///
72    /// Returns an error if the client fails to start.
73    fn start(&mut self) -> anyhow::Result<()>;
74
75    /// Stops the execution client.
76    ///
77    /// # Errors
78    ///
79    /// Returns an error if the client fails to stop.
80    fn stop(&mut self) -> anyhow::Result<()>;
81
82    /// Connects the client to the execution venue.
83    ///
84    /// # Errors
85    ///
86    /// Returns an error if connection fails.
87    async fn connect(&mut self) -> anyhow::Result<()> {
88        Ok(())
89    }
90
91    /// Disconnects the client from the execution venue.
92    ///
93    /// # Errors
94    ///
95    /// Returns an error if disconnection fails.
96    async fn disconnect(&mut self) -> anyhow::Result<()> {
97        Ok(())
98    }
99
100    /// Submits a single order command to the execution venue.
101    ///
102    /// # Errors
103    ///
104    /// Returns an error if submission fails.
105    fn submit_order(&self, cmd: &SubmitOrder) -> anyhow::Result<()> {
106        log_not_implemented(cmd);
107        Ok(())
108    }
109
110    /// Submits a list of orders to the execution venue.
111    ///
112    /// # Errors
113    ///
114    /// Returns an error if submission fails.
115    fn submit_order_list(&self, cmd: &SubmitOrderList) -> anyhow::Result<()> {
116        log_not_implemented(cmd);
117        Ok(())
118    }
119
120    /// Modifies an existing order.
121    ///
122    /// # Errors
123    ///
124    /// Returns an error if modification fails.
125    fn modify_order(&self, cmd: &ModifyOrder) -> anyhow::Result<()> {
126        log_not_implemented(cmd);
127        Ok(())
128    }
129
130    /// Cancels a specific order.
131    ///
132    /// # Errors
133    ///
134    /// Returns an error if cancellation fails.
135    fn cancel_order(&self, cmd: &CancelOrder) -> anyhow::Result<()> {
136        log_not_implemented(cmd);
137        Ok(())
138    }
139
140    /// Cancels all orders.
141    ///
142    /// # Errors
143    ///
144    /// Returns an error if cancellation fails.
145    fn cancel_all_orders(&self, cmd: &CancelAllOrders) -> anyhow::Result<()> {
146        log_not_implemented(cmd);
147        Ok(())
148    }
149
150    /// Cancels a batch of orders.
151    ///
152    /// # Errors
153    ///
154    /// Returns an error if batch cancellation fails.
155    fn batch_cancel_orders(&self, cmd: &BatchCancelOrders) -> anyhow::Result<()> {
156        log_not_implemented(cmd);
157        Ok(())
158    }
159
160    /// Queries the status of an account.
161    ///
162    /// # Errors
163    ///
164    /// Returns an error if the query fails.
165    fn query_account(&self, cmd: &QueryAccount) -> anyhow::Result<()> {
166        log_not_implemented(cmd);
167        Ok(())
168    }
169
170    /// Queries the status of an order.
171    ///
172    /// # Errors
173    ///
174    /// Returns an error if the query fails.
175    fn query_order(&self, cmd: &QueryOrder) -> anyhow::Result<()> {
176        log_not_implemented(cmd);
177        Ok(())
178    }
179
180    /// Generates a single order status report.
181    ///
182    /// # Errors
183    ///
184    /// Returns an error if report generation fails.
185    async fn generate_order_status_report(
186        &self,
187        cmd: &GenerateOrderStatusReport,
188    ) -> anyhow::Result<Option<OrderStatusReport>> {
189        log_not_implemented(cmd);
190        Ok(None)
191    }
192
193    /// Generates multiple order status reports.
194    ///
195    /// # Errors
196    ///
197    /// Returns an error if report generation fails.
198    async fn generate_order_status_reports(
199        &self,
200        cmd: &GenerateOrderStatusReports,
201    ) -> anyhow::Result<Vec<OrderStatusReport>> {
202        log_not_implemented(cmd);
203        Ok(Vec::new())
204    }
205
206    /// Generates fill reports based on execution results.
207    ///
208    /// # Errors
209    ///
210    /// Returns an error if fill report generation fails.
211    async fn generate_fill_reports(
212        &self,
213        cmd: GenerateFillReports,
214    ) -> anyhow::Result<Vec<FillReport>> {
215        log_not_implemented(&cmd);
216        Ok(Vec::new())
217    }
218
219    /// Generates position status reports.
220    ///
221    /// # Errors
222    ///
223    /// Returns an error if generation fails.
224    async fn generate_position_status_reports(
225        &self,
226        cmd: &GeneratePositionStatusReports,
227    ) -> anyhow::Result<Vec<PositionStatusReport>> {
228        log_not_implemented(cmd);
229        Ok(Vec::new())
230    }
231
232    /// Generates mass status for executions.
233    ///
234    /// # Errors
235    ///
236    /// Returns an error if status generation fails.
237    async fn generate_mass_status(
238        &self,
239        lookback_mins: Option<u64>,
240    ) -> anyhow::Result<Option<ExecutionMassStatus>> {
241        log_not_implemented(&lookback_mins);
242        Ok(None)
243    }
244}
245
246#[inline(always)]
247fn log_not_implemented<T: Debug>(cmd: &T) {
248    log::warn!("{cmd:?} – handler not implemented");
249}
250
251/// Wraps an [`ExecutionClient`], managing its lifecycle and providing access to the client.
252pub struct ExecutionClientAdapter {
253    pub(crate) client: Box<dyn ExecutionClient>,
254    pub client_id: ClientId,
255    pub venue: Venue,
256    pub account_id: AccountId,
257    pub oms_type: OmsType,
258}
259
260impl Deref for ExecutionClientAdapter {
261    type Target = Box<dyn ExecutionClient>;
262
263    fn deref(&self) -> &Self::Target {
264        &self.client
265    }
266}
267
268impl DerefMut for ExecutionClientAdapter {
269    fn deref_mut(&mut self) -> &mut Self::Target {
270        &mut self.client
271    }
272}
273
274impl Debug for ExecutionClientAdapter {
275    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276        f.debug_struct(stringify!(ExecutionClientAdapter))
277            .field("client_id", &self.client_id)
278            .field("venue", &self.venue)
279            .field("account_id", &self.account_id)
280            .field("oms_type", &self.oms_type)
281            .finish()
282    }
283}
284
285impl ExecutionClientAdapter {
286    /// Creates a new [`ExecutionClientAdapter`] with the given client.
287    #[must_use]
288    pub fn new(client: Box<dyn ExecutionClient>) -> Self {
289        let client_id = client.client_id();
290        let venue = client.venue();
291        let account_id = client.account_id();
292        let oms_type = client.oms_type();
293
294        Self {
295            client,
296            client_id,
297            venue,
298            account_id,
299            oms_type,
300        }
301    }
302
303    /// Connects the execution client to the venue.
304    ///
305    /// # Errors
306    ///
307    /// Returns an error if connection fails.
308    pub async fn connect(&mut self) -> anyhow::Result<()> {
309        self.client.connect().await
310    }
311
312    /// Disconnects the execution client from the venue.
313    ///
314    /// # Errors
315    ///
316    /// Returns an error if disconnection fails.
317    pub async fn disconnect(&mut self) -> anyhow::Result<()> {
318        self.client.disconnect().await
319    }
320
321    /// Generates a single order status report.
322    ///
323    /// # Errors
324    ///
325    /// Returns an error if report generation fails.
326    pub async fn generate_order_status_report(
327        &self,
328        cmd: &GenerateOrderStatusReport,
329    ) -> anyhow::Result<Option<OrderStatusReport>> {
330        self.client.generate_order_status_report(cmd).await
331    }
332
333    /// Generates multiple order status reports.
334    ///
335    /// # Errors
336    ///
337    /// Returns an error if report generation fails.
338    pub async fn generate_order_status_reports(
339        &self,
340        cmd: &GenerateOrderStatusReports,
341    ) -> anyhow::Result<Vec<OrderStatusReport>> {
342        self.client.generate_order_status_reports(cmd).await
343    }
344
345    /// Generates fill reports based on execution results.
346    ///
347    /// # Errors
348    ///
349    /// Returns an error if fill report generation fails.
350    pub async fn generate_fill_reports(
351        &self,
352        cmd: GenerateFillReports,
353    ) -> anyhow::Result<Vec<FillReport>> {
354        self.client.generate_fill_reports(cmd).await
355    }
356
357    /// Generates position status reports.
358    ///
359    /// # Errors
360    ///
361    /// Returns an error if generation fails.
362    pub async fn generate_position_status_reports(
363        &self,
364        cmd: &GeneratePositionStatusReports,
365    ) -> anyhow::Result<Vec<PositionStatusReport>> {
366        self.client.generate_position_status_reports(cmd).await
367    }
368
369    /// Generates mass status for executions.
370    ///
371    /// # Errors
372    ///
373    /// Returns an error if status generation fails.
374    pub async fn generate_mass_status(
375        &self,
376        lookback_mins: Option<u64>,
377    ) -> anyhow::Result<Option<ExecutionMassStatus>> {
378        self.client.generate_mass_status(lookback_mins).await
379    }
380}