1use ahash::AHashMap;
19use nautilus_execution::matching_engine::config::OrderMatchingEngineConfig;
20use nautilus_model::{
21 enums::{AccountType, BookType, OmsType},
22 identifiers::{AccountId, InstrumentId, TraderId, Venue},
23 types::{Currency, Money},
24};
25use rust_decimal::Decimal;
26use serde::{Deserialize, Serialize};
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30#[cfg_attr(
31 feature = "python",
32 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.sandbox")
33)]
34pub struct SandboxExecutionClientConfig {
35 pub trader_id: TraderId,
37 pub account_id: AccountId,
39 pub venue: Venue,
41 pub starting_balances: Vec<Money>,
43 pub base_currency: Option<Currency>,
45 pub oms_type: OmsType,
47 pub account_type: AccountType,
49 pub default_leverage: Decimal,
51 pub leverages: AHashMap<InstrumentId, Decimal>,
53 pub book_type: BookType,
55 pub frozen_account: bool,
57 pub bar_execution: bool,
59 pub trade_execution: bool,
61 pub reject_stop_orders: bool,
63 pub support_gtd_orders: bool,
65 pub support_contingent_orders: bool,
67 pub use_position_ids: bool,
69 pub use_random_ids: bool,
71 pub use_reduce_only: bool,
73}
74
75impl SandboxExecutionClientConfig {
76 #[must_use]
78 pub fn new(
79 trader_id: TraderId,
80 account_id: AccountId,
81 venue: Venue,
82 starting_balances: Vec<Money>,
83 ) -> Self {
84 Self {
85 trader_id,
86 account_id,
87 venue,
88 starting_balances,
89 base_currency: None,
90 oms_type: OmsType::Netting,
91 account_type: AccountType::Margin,
92 default_leverage: Decimal::ONE,
93 leverages: AHashMap::new(),
94 book_type: BookType::L1_MBP,
95 frozen_account: false,
96 bar_execution: true,
97 trade_execution: false,
98 reject_stop_orders: true,
99 support_gtd_orders: true,
100 support_contingent_orders: true,
101 use_position_ids: true,
102 use_random_ids: false,
103 use_reduce_only: true,
104 }
105 }
106
107 #[must_use]
109 pub fn to_matching_engine_config(&self) -> OrderMatchingEngineConfig {
110 OrderMatchingEngineConfig::new(
111 self.bar_execution,
112 self.trade_execution,
113 false, self.reject_stop_orders,
115 self.support_gtd_orders,
116 self.support_contingent_orders,
117 self.use_position_ids,
118 self.use_random_ids,
119 self.use_reduce_only,
120 false, )
122 }
123
124 #[must_use]
126 pub fn with_base_currency(mut self, currency: Currency) -> Self {
127 self.base_currency = Some(currency);
128 self
129 }
130
131 #[must_use]
133 pub fn with_oms_type(mut self, oms_type: OmsType) -> Self {
134 self.oms_type = oms_type;
135 self
136 }
137
138 #[must_use]
140 pub fn with_account_type(mut self, account_type: AccountType) -> Self {
141 self.account_type = account_type;
142 self
143 }
144
145 #[must_use]
147 pub fn with_default_leverage(mut self, leverage: Decimal) -> Self {
148 self.default_leverage = leverage;
149 self
150 }
151
152 #[must_use]
154 pub fn with_book_type(mut self, book_type: BookType) -> Self {
155 self.book_type = book_type;
156 self
157 }
158
159 #[must_use]
161 pub fn with_frozen_account(mut self, frozen: bool) -> Self {
162 self.frozen_account = frozen;
163 self
164 }
165
166 #[must_use]
168 pub fn with_bar_execution(mut self, enabled: bool) -> Self {
169 self.bar_execution = enabled;
170 self
171 }
172
173 #[must_use]
175 pub fn with_trade_execution(mut self, enabled: bool) -> Self {
176 self.trade_execution = enabled;
177 self
178 }
179}
180
181impl Default for SandboxExecutionClientConfig {
182 fn default() -> Self {
183 Self {
184 trader_id: TraderId::from("SANDBOX-001"),
185 account_id: AccountId::from("SANDBOX-001"),
186 venue: Venue::new("SANDBOX"),
187 starting_balances: Vec::new(),
188 base_currency: None,
189 oms_type: OmsType::Netting,
190 account_type: AccountType::Margin,
191 default_leverage: Decimal::ONE,
192 leverages: AHashMap::new(),
193 book_type: BookType::L1_MBP,
194 frozen_account: false,
195 bar_execution: true,
196 trade_execution: false,
197 reject_stop_orders: true,
198 support_gtd_orders: true,
199 support_contingent_orders: true,
200 use_position_ids: true,
201 use_random_ids: false,
202 use_reduce_only: true,
203 }
204 }
205}
206
207#[cfg(feature = "python")]
208mod pyo3_impl {
209 use nautilus_model::{
210 enums::{AccountType, BookType, OmsType},
211 identifiers::{AccountId, TraderId, Venue},
212 types::{Currency, Money},
213 };
214 use pyo3::prelude::*;
215 use rust_decimal::Decimal;
216
217 use super::SandboxExecutionClientConfig;
218
219 #[pymethods]
220 impl SandboxExecutionClientConfig {
221 #[new]
222 #[pyo3(signature = (venue, starting_balances, trader_id=None, 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=false, reject_stop_orders=true, support_gtd_orders=true, support_contingent_orders=true, use_position_ids=true, use_random_ids=false, use_reduce_only=true))]
223 #[allow(clippy::too_many_arguments)]
224 fn py_new(
225 venue: Venue,
226 starting_balances: Vec<Money>,
227 trader_id: Option<TraderId>,
228 account_id: Option<AccountId>,
229 base_currency: Option<Currency>,
230 oms_type: Option<OmsType>,
231 account_type: Option<AccountType>,
232 default_leverage: Option<Decimal>,
233 book_type: Option<BookType>,
234 frozen_account: bool,
235 bar_execution: bool,
236 trade_execution: bool,
237 reject_stop_orders: bool,
238 support_gtd_orders: bool,
239 support_contingent_orders: bool,
240 use_position_ids: bool,
241 use_random_ids: bool,
242 use_reduce_only: bool,
243 ) -> Self {
244 let trader_id =
246 trader_id.unwrap_or_else(|| TraderId::from(format!("{venue}-001").as_str()));
247 let account_id = account_id
248 .unwrap_or_else(|| AccountId::from(format!("{venue}-SANDBOX-001").as_str()));
249
250 Self {
251 trader_id,
252 account_id,
253 venue,
254 starting_balances,
255 base_currency,
256 oms_type: oms_type.unwrap_or(OmsType::Netting),
257 account_type: account_type.unwrap_or(AccountType::Margin),
258 default_leverage: default_leverage.unwrap_or(Decimal::ONE),
259 leverages: ahash::AHashMap::new(),
260 book_type: book_type.unwrap_or(BookType::L1_MBP),
261 frozen_account,
262 bar_execution,
263 trade_execution,
264 reject_stop_orders,
265 support_gtd_orders,
266 support_contingent_orders,
267 use_position_ids,
268 use_random_ids,
269 use_reduce_only,
270 }
271 }
272
273 #[getter]
274 fn trader_id(&self) -> TraderId {
275 self.trader_id
276 }
277
278 #[getter]
279 fn account_id(&self) -> AccountId {
280 self.account_id
281 }
282
283 #[getter]
284 fn venue(&self) -> Venue {
285 self.venue
286 }
287
288 #[getter]
289 fn starting_balances(&self) -> Vec<Money> {
290 self.starting_balances.clone()
291 }
292
293 #[getter]
294 fn base_currency(&self) -> Option<Currency> {
295 self.base_currency
296 }
297
298 #[getter]
299 fn oms_type(&self) -> OmsType {
300 self.oms_type
301 }
302
303 #[getter]
304 fn account_type(&self) -> AccountType {
305 self.account_type
306 }
307
308 #[getter]
309 fn default_leverage(&self) -> Decimal {
310 self.default_leverage
311 }
312
313 #[getter]
314 fn book_type(&self) -> BookType {
315 self.book_type
316 }
317
318 #[getter]
319 fn frozen_account(&self) -> bool {
320 self.frozen_account
321 }
322
323 #[getter]
324 fn bar_execution(&self) -> bool {
325 self.bar_execution
326 }
327
328 #[getter]
329 fn trade_execution(&self) -> bool {
330 self.trade_execution
331 }
332
333 #[getter]
334 fn reject_stop_orders(&self) -> bool {
335 self.reject_stop_orders
336 }
337
338 #[getter]
339 fn support_gtd_orders(&self) -> bool {
340 self.support_gtd_orders
341 }
342
343 #[getter]
344 fn support_contingent_orders(&self) -> bool {
345 self.support_contingent_orders
346 }
347
348 #[getter]
349 fn use_position_ids(&self) -> bool {
350 self.use_position_ids
351 }
352
353 #[getter]
354 fn use_random_ids(&self) -> bool {
355 self.use_random_ids
356 }
357
358 #[getter]
359 fn use_reduce_only(&self) -> bool {
360 self.use_reduce_only
361 }
362 }
363}