Skip to main content

nautilus_sandbox/python/
config.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//! Python bindings for sandbox configuration.
17
18use nautilus_execution::{
19    models::{fee::FeeModelAny, fill::FillModelAny, latency::LatencyModelAny},
20    python::{
21        fee::{fee_model_any_to_pyobject, pyobject_to_fee_model_any},
22        fill::{fill_model_any_to_pyobject, pyobject_to_fill_model_any},
23        latency::{latency_model_any_to_pyobject, pyobject_to_latency_model_any},
24    },
25};
26use nautilus_model::{
27    enums::{AccountType, BookType, OmsType},
28    identifiers::{AccountId, Venue},
29    types::{Currency, Money},
30};
31use pyo3::{Py, PyAny, Python, prelude::*};
32use rust_decimal::Decimal;
33
34use crate::config::SandboxExecutionClientConfig;
35
36#[pymethods]
37#[pyo3_stub_gen::derive::gen_stub_pymethods]
38impl SandboxExecutionClientConfig {
39    /// Configuration for `SandboxExecutionClient` instances.
40    #[new]
41    #[pyo3(signature = (venue, starting_balances, account_id=None, base_currency=None, oms_type=None, account_type=None, default_leverage=None, book_type=None, frozen_account=false, bar_execution=true, trade_execution=true, reject_stop_orders=true, support_gtd_orders=true, support_contingent_orders=true, use_position_ids=true, use_random_ids=false, use_reduce_only=true, fee_model=None, fill_model=None, queue_position=false, liquidity_consumption=false, bar_adaptive_high_low_ordering=false, use_market_order_acks=false, oto_full_trigger=false, price_protection_points=None, latency_model=None))]
42    #[expect(clippy::too_many_arguments)]
43    fn py_new(
44        venue: Venue,
45        starting_balances: Vec<Money>,
46        account_id: Option<AccountId>,
47        base_currency: Option<Currency>,
48        oms_type: Option<OmsType>,
49        account_type: Option<AccountType>,
50        default_leverage: Option<Decimal>,
51        book_type: Option<BookType>,
52        frozen_account: bool,
53        bar_execution: bool,
54        trade_execution: bool,
55        reject_stop_orders: bool,
56        support_gtd_orders: bool,
57        support_contingent_orders: bool,
58        use_position_ids: bool,
59        use_random_ids: bool,
60        use_reduce_only: bool,
61        fee_model: Option<Py<PyAny>>,
62        fill_model: Option<Py<PyAny>>,
63        queue_position: bool,
64        liquidity_consumption: bool,
65        bar_adaptive_high_low_ordering: bool,
66        use_market_order_acks: bool,
67        oto_full_trigger: bool,
68        price_protection_points: Option<u32>,
69        latency_model: Option<Py<PyAny>>,
70    ) -> PyResult<Self> {
71        // Generate the default account ID from the venue
72        let account_id =
73            account_id.unwrap_or_else(|| AccountId::from(format!("{venue}-SANDBOX-001").as_str()));
74        let fee_model: Option<FeeModelAny> = fee_model
75            .map(|obj| Python::attach(|py| pyobject_to_fee_model_any(obj.bind(py))))
76            .transpose()?;
77        let fill_model: Option<FillModelAny> = fill_model
78            .map(|obj| Python::attach(|py| pyobject_to_fill_model_any(obj.bind(py))))
79            .transpose()?;
80        let latency_model: Option<LatencyModelAny> = latency_model
81            .map(|obj| Python::attach(|py| pyobject_to_latency_model_any(obj.bind(py))))
82            .transpose()?;
83
84        Ok(Self {
85            account_id,
86            venue,
87            starting_balances,
88            base_currency,
89            oms_type: oms_type.unwrap_or(OmsType::Netting),
90            account_type: account_type.unwrap_or(AccountType::Margin),
91            default_leverage: default_leverage.unwrap_or(Decimal::ONE),
92            leverages: ahash::AHashMap::new(),
93            book_type: book_type.unwrap_or(BookType::L1_MBP),
94            fee_model,
95            fill_model,
96            latency_model,
97            frozen_account,
98            bar_execution,
99            trade_execution,
100            reject_stop_orders,
101            support_gtd_orders,
102            support_contingent_orders,
103            use_position_ids,
104            use_random_ids,
105            use_reduce_only,
106            queue_position,
107            liquidity_consumption,
108            bar_adaptive_high_low_ordering,
109            use_market_order_acks,
110            oto_full_trigger,
111            price_protection_points: price_protection_points.unwrap_or(0),
112        })
113    }
114
115    #[getter]
116    fn account_id(&self) -> AccountId {
117        self.account_id
118    }
119
120    #[getter]
121    fn venue(&self) -> Venue {
122        self.venue
123    }
124
125    #[getter]
126    fn starting_balances(&self) -> Vec<Money> {
127        self.starting_balances.clone()
128    }
129
130    #[getter]
131    fn base_currency(&self) -> Option<Currency> {
132        self.base_currency
133    }
134
135    #[getter]
136    fn oms_type(&self) -> OmsType {
137        self.oms_type
138    }
139
140    #[getter]
141    fn account_type(&self) -> AccountType {
142        self.account_type
143    }
144
145    #[getter]
146    fn default_leverage(&self) -> Decimal {
147        self.default_leverage
148    }
149
150    #[getter]
151    fn book_type(&self) -> BookType {
152        self.book_type
153    }
154
155    #[getter]
156    fn fee_model(&self, py: Python<'_>) -> PyResult<Option<Py<PyAny>>> {
157        self.fee_model
158            .as_ref()
159            .map(|model| fee_model_any_to_pyobject(py, model))
160            .transpose()
161    }
162
163    #[getter]
164    fn fill_model(&self, py: Python<'_>) -> PyResult<Option<Py<PyAny>>> {
165        self.fill_model
166            .as_ref()
167            .map(|model| fill_model_any_to_pyobject(py, model))
168            .transpose()
169    }
170
171    #[getter]
172    fn latency_model(&self, py: Python<'_>) -> PyResult<Option<Py<PyAny>>> {
173        self.latency_model
174            .as_ref()
175            .map(|model| latency_model_any_to_pyobject(py, model))
176            .transpose()
177    }
178
179    #[getter]
180    fn frozen_account(&self) -> bool {
181        self.frozen_account
182    }
183
184    #[getter]
185    fn bar_execution(&self) -> bool {
186        self.bar_execution
187    }
188
189    #[getter]
190    fn trade_execution(&self) -> bool {
191        self.trade_execution
192    }
193
194    #[getter]
195    fn reject_stop_orders(&self) -> bool {
196        self.reject_stop_orders
197    }
198
199    #[getter]
200    fn support_gtd_orders(&self) -> bool {
201        self.support_gtd_orders
202    }
203
204    #[getter]
205    fn support_contingent_orders(&self) -> bool {
206        self.support_contingent_orders
207    }
208
209    #[getter]
210    fn use_position_ids(&self) -> bool {
211        self.use_position_ids
212    }
213
214    #[getter]
215    fn use_random_ids(&self) -> bool {
216        self.use_random_ids
217    }
218
219    #[getter]
220    fn use_reduce_only(&self) -> bool {
221        self.use_reduce_only
222    }
223
224    #[getter]
225    fn queue_position(&self) -> bool {
226        self.queue_position
227    }
228
229    #[getter]
230    fn liquidity_consumption(&self) -> bool {
231        self.liquidity_consumption
232    }
233
234    #[getter]
235    fn bar_adaptive_high_low_ordering(&self) -> bool {
236        self.bar_adaptive_high_low_ordering
237    }
238
239    #[getter]
240    fn use_market_order_acks(&self) -> bool {
241        self.use_market_order_acks
242    }
243
244    #[getter]
245    fn oto_full_trigger(&self) -> bool {
246        self.oto_full_trigger
247    }
248
249    #[getter]
250    fn price_protection_points(&self) -> u32 {
251        self.price_protection_points
252    }
253}