1use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyvalue_err};
17use pyo3::{IntoPyObjectExt, basic::CompareOp, prelude::*, types::PyDict};
18use rust_decimal::Decimal;
19
20use crate::{
21 accounts::MarginAccount,
22 events::AccountState,
23 identifiers::{AccountId, InstrumentId},
24 instruments::InstrumentAny,
25 python::instruments::pyobject_to_instrument_any,
26 types::{Money, Price, Quantity},
27};
28
29#[pymethods]
30impl MarginAccount {
31 #[new]
32 fn py_new(event: AccountState, calculate_account_state: bool) -> Self {
33 Self::new(event, calculate_account_state)
34 }
35
36 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
37 match op {
38 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
39 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
40 _ => py.NotImplemented(),
41 }
42 }
43
44 #[getter]
45 fn id(&self) -> AccountId {
46 self.id
47 }
48
49 #[getter]
50 fn default_leverage(&self) -> Decimal {
51 self.default_leverage
52 }
53
54 #[getter]
55 #[pyo3(name = "calculate_account_state")]
56 fn py_calculate_account_state(&self) -> bool {
57 self.calculate_account_state
58 }
59
60 fn __repr__(&self) -> String {
61 format!(
62 "{}(id={}, type={}, base={})",
63 stringify!(MarginAccount),
64 self.id,
65 self.account_type,
66 self.base_currency.map_or_else(
67 || "None".to_string(),
68 |base_currency| format!("{}", base_currency.code)
69 ),
70 )
71 }
72
73 #[pyo3(name = "set_default_leverage")]
74 fn py_set_default_leverage(&mut self, default_leverage: Decimal) {
75 self.set_default_leverage(default_leverage);
76 }
77
78 #[pyo3(name = "leverages")]
79 fn py_leverages(&self, py: Python) -> PyResult<Py<PyAny>> {
80 let leverages = PyDict::new(py);
81 for (key, &value) in &self.leverages {
82 leverages
83 .set_item(key.into_py_any_unwrap(py), value)
84 .unwrap();
85 }
86 leverages.into_py_any(py)
87 }
88
89 #[pyo3(name = "leverage")]
90 fn py_leverage(&self, instrument_id: &InstrumentId) -> Decimal {
91 self.get_leverage(instrument_id)
92 }
93
94 #[pyo3(name = "set_leverage")]
95 fn py_set_leverage(&mut self, instrument_id: InstrumentId, leverage: Decimal) {
96 self.set_leverage(instrument_id, leverage);
97 }
98
99 #[pyo3(name = "is_unleveraged")]
100 fn py_is_unleveraged(&self, instrument_id: InstrumentId) -> PyResult<bool> {
101 Ok(self.is_unleveraged(instrument_id))
102 }
103
104 #[pyo3(name = "initial_margins")]
105 fn py_initial_margins(&self, py: Python) -> PyResult<Py<PyAny>> {
106 let initial_margins = PyDict::new(py);
107 for (key, &value) in &self.initial_margins() {
108 initial_margins
109 .set_item(key.into_py_any_unwrap(py), value.into_py_any_unwrap(py))
110 .unwrap();
111 }
112 initial_margins.into_py_any(py)
113 }
114
115 #[pyo3(name = "maintenance_margins")]
116 fn py_maintenance_margins(&self, py: Python) -> PyResult<Py<PyAny>> {
117 let maintenance_margins = PyDict::new(py);
118 for (key, &value) in &self.maintenance_margins() {
119 maintenance_margins
120 .set_item(key.into_py_any_unwrap(py), value.into_py_any_unwrap(py))
121 .unwrap();
122 }
123 maintenance_margins.into_py_any(py)
124 }
125
126 #[pyo3(name = "update_initial_margin")]
127 fn py_update_initial_margin(
128 &mut self,
129 instrument_id: InstrumentId,
130 initial_margin: Money,
131 ) -> PyResult<()> {
132 self.update_initial_margin(instrument_id, initial_margin);
133 Ok(())
134 }
135
136 #[pyo3(name = "initial_margin")]
137 fn py_initial_margin(&self, instrument_id: InstrumentId) -> PyResult<Money> {
138 Ok(self.initial_margin(instrument_id))
139 }
140
141 #[pyo3(name = "update_maintenance_margin")]
142 fn py_update_maintenance_margin(
143 &mut self,
144 instrument_id: InstrumentId,
145 maintenance_margin: Money,
146 ) -> PyResult<()> {
147 self.update_maintenance_margin(instrument_id, maintenance_margin);
148 Ok(())
149 }
150
151 #[pyo3(name = "maintenance_margin")]
152 fn py_maintenance_margin(&self, instrument_id: InstrumentId) -> PyResult<Money> {
153 Ok(self.maintenance_margin(instrument_id))
154 }
155
156 #[pyo3(name = "calculate_initial_margin")]
157 #[pyo3(signature = (instrument, quantity, price, use_quote_for_inverse=None))]
158 pub fn py_calculate_initial_margin(
165 &mut self,
166 instrument: Py<PyAny>,
167 quantity: Quantity,
168 price: Price,
169 use_quote_for_inverse: Option<bool>,
170 py: Python,
171 ) -> PyResult<Money> {
172 let instrument_type = pyobject_to_instrument_any(py, instrument)?;
173 match instrument_type {
174 InstrumentAny::Betting(inst) => self
175 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
176 .map_err(to_pyvalue_err),
177 InstrumentAny::BinaryOption(inst) => self
178 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
179 .map_err(to_pyvalue_err),
180 InstrumentAny::Cfd(inst) => self
181 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
182 .map_err(to_pyvalue_err),
183 InstrumentAny::Commodity(inst) => self
184 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
185 .map_err(to_pyvalue_err),
186 InstrumentAny::CryptoFuture(inst) => self
187 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
188 .map_err(to_pyvalue_err),
189 InstrumentAny::CryptoOption(inst) => self
190 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
191 .map_err(to_pyvalue_err),
192 InstrumentAny::CryptoPerpetual(inst) => self
193 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
194 .map_err(to_pyvalue_err),
195 InstrumentAny::CurrencyPair(inst) => self
196 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
197 .map_err(to_pyvalue_err),
198 InstrumentAny::Equity(inst) => self
199 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
200 .map_err(to_pyvalue_err),
201 InstrumentAny::FuturesContract(inst) => self
202 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
203 .map_err(to_pyvalue_err),
204 InstrumentAny::FuturesSpread(inst) => self
205 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
206 .map_err(to_pyvalue_err),
207 InstrumentAny::IndexInstrument(inst) => self
208 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
209 .map_err(to_pyvalue_err),
210 InstrumentAny::OptionContract(inst) => self
211 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
212 .map_err(to_pyvalue_err),
213 InstrumentAny::OptionSpread(inst) => self
214 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
215 .map_err(to_pyvalue_err),
216 InstrumentAny::PerpetualContract(inst) => self
217 .calculate_initial_margin(&inst, quantity, price, use_quote_for_inverse)
218 .map_err(to_pyvalue_err),
219 }
220 }
221
222 #[pyo3(name = "calculate_maintenance_margin")]
229 #[pyo3(signature = (instrument, quantity, price, use_quote_for_inverse=None))]
230 pub fn py_calculate_maintenance_margin(
231 &mut self,
232 instrument: Py<PyAny>,
233 quantity: Quantity,
234 price: Price,
235 use_quote_for_inverse: Option<bool>,
236 py: Python,
237 ) -> PyResult<Money> {
238 let instrument_type = pyobject_to_instrument_any(py, instrument)?;
239 match instrument_type {
240 InstrumentAny::Betting(inst) => self
241 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
242 .map_err(to_pyvalue_err),
243 InstrumentAny::BinaryOption(inst) => self
244 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
245 .map_err(to_pyvalue_err),
246 InstrumentAny::Cfd(inst) => self
247 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
248 .map_err(to_pyvalue_err),
249 InstrumentAny::Commodity(inst) => self
250 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
251 .map_err(to_pyvalue_err),
252 InstrumentAny::CryptoFuture(inst) => self
253 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
254 .map_err(to_pyvalue_err),
255 InstrumentAny::CryptoOption(inst) => self
256 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
257 .map_err(to_pyvalue_err),
258 InstrumentAny::CryptoPerpetual(inst) => self
259 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
260 .map_err(to_pyvalue_err),
261 InstrumentAny::CurrencyPair(inst) => self
262 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
263 .map_err(to_pyvalue_err),
264 InstrumentAny::Equity(inst) => self
265 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
266 .map_err(to_pyvalue_err),
267 InstrumentAny::FuturesContract(inst) => self
268 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
269 .map_err(to_pyvalue_err),
270 InstrumentAny::FuturesSpread(inst) => self
271 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
272 .map_err(to_pyvalue_err),
273 InstrumentAny::IndexInstrument(inst) => self
274 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
275 .map_err(to_pyvalue_err),
276 InstrumentAny::OptionContract(inst) => self
277 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
278 .map_err(to_pyvalue_err),
279 InstrumentAny::OptionSpread(inst) => self
280 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
281 .map_err(to_pyvalue_err),
282 InstrumentAny::PerpetualContract(inst) => self
283 .calculate_maintenance_margin(&inst, quantity, price, use_quote_for_inverse)
284 .map_err(to_pyvalue_err),
285 }
286 }
287
288 #[pyo3(name = "to_dict")]
289 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
290 let dict = PyDict::new(py);
291 dict.set_item("calculate_account_state", self.calculate_account_state)?;
292 let events_list: PyResult<Vec<Py<PyAny>>> =
293 self.events.iter().map(|item| item.py_to_dict(py)).collect();
294 dict.set_item("events", events_list.unwrap())?;
295 Ok(dict.into())
296 }
297}