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