nautilus_model/python/account/
margin.rs1use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyvalue_err};
17use pyo3::{IntoPyObjectExt, basic::CompareOp, prelude::*, types::PyDict};
18
19use crate::{
20 accounts::MarginAccount,
21 events::AccountState,
22 identifiers::{AccountId, InstrumentId},
23 instruments::InstrumentAny,
24 python::instruments::pyobject_to_instrument_any,
25 types::{Money, Price, Quantity},
26};
27
28#[pymethods]
29impl MarginAccount {
30 #[new]
31 fn py_new(event: AccountState, calculate_account_state: bool) -> Self {
32 Self::new(event, calculate_account_state)
33 }
34
35 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
36 match op {
37 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
38 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
39 _ => py.NotImplemented(),
40 }
41 }
42
43 #[getter]
44 fn id(&self) -> AccountId {
45 self.id
46 }
47
48 #[getter]
49 fn default_leverage(&self) -> f64 {
50 self.default_leverage
51 }
52
53 #[getter]
54 #[pyo3(name = "calculate_account_state")]
55 fn py_calculate_account_state(&self) -> bool {
56 self.calculate_account_state
57 }
58
59 fn __repr__(&self) -> String {
60 format!(
61 "{}(id={}, type={}, base={})",
62 stringify!(MarginAccount),
63 self.id,
64 self.account_type,
65 self.base_currency.map_or_else(
66 || "None".to_string(),
67 |base_currency| format!("{}", base_currency.code)
68 ),
69 )
70 }
71
72 #[pyo3(name = "set_default_leverage")]
73 fn py_set_default_leverage(&mut self, default_leverage: f64) -> PyResult<()> {
74 self.set_default_leverage(default_leverage);
75 Ok(())
76 }
77
78 #[pyo3(name = "leverages")]
79 fn py_leverages(&self, py: Python) -> PyResult<PyObject> {
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) -> PyResult<f64> {
91 Ok(self.get_leverage(instrument_id))
92 }
93
94 #[pyo3(name = "set_leverage")]
95 fn py_set_leverage(&mut self, instrument_id: InstrumentId, leverage: f64) -> PyResult<()> {
96 self.set_leverage(instrument_id, leverage);
97 Ok(())
98 }
99
100 #[pyo3(name = "is_unleveraged")]
101 fn py_is_unleveraged(&self, instrument_id: InstrumentId) -> PyResult<bool> {
102 Ok(self.is_unleveraged(instrument_id))
103 }
104
105 #[pyo3(name = "initial_margins")]
106 fn py_initial_margins(&self, py: Python) -> PyResult<PyObject> {
107 let initial_margins = PyDict::new(py);
108 for (key, &value) in &self.initial_margins() {
109 initial_margins
110 .set_item(key.into_py_any_unwrap(py), value.into_py_any_unwrap(py))
111 .unwrap();
112 }
113 initial_margins.into_py_any(py)
114 }
115
116 #[pyo3(name = "maintenance_margins")]
117 fn py_maintenance_margins(&self, py: Python) -> PyResult<PyObject> {
118 let maintenance_margins = PyDict::new(py);
119 for (key, &value) in &self.maintenance_margins() {
120 maintenance_margins
121 .set_item(key.into_py_any_unwrap(py), value.into_py_any_unwrap(py))
122 .unwrap();
123 }
124 maintenance_margins.into_py_any(py)
125 }
126
127 #[pyo3(name = "update_initial_margin")]
128 fn py_update_initial_margin(
129 &mut self,
130 instrument_id: InstrumentId,
131 initial_margin: Money,
132 ) -> PyResult<()> {
133 self.update_initial_margin(instrument_id, initial_margin);
134 Ok(())
135 }
136
137 #[pyo3(name = "initial_margin")]
138 fn py_initial_margin(&self, instrument_id: InstrumentId) -> PyResult<Money> {
139 Ok(self.initial_margin(instrument_id))
140 }
141
142 #[pyo3(name = "update_maintenance_margin")]
143 fn py_update_maintenance_margin(
144 &mut self,
145 instrument_id: InstrumentId,
146 maintenance_margin: Money,
147 ) -> PyResult<()> {
148 self.update_maintenance_margin(instrument_id, maintenance_margin);
149 Ok(())
150 }
151
152 #[pyo3(name = "maintenance_margin")]
153 fn py_maintenance_margin(&self, instrument_id: InstrumentId) -> PyResult<Money> {
154 Ok(self.maintenance_margin(instrument_id))
155 }
156
157 #[pyo3(name = "calculate_initial_margin")]
158 #[pyo3(signature = (instrument, quantity, price, use_quote_for_inverse=None))]
159 pub fn py_calculate_initial_margin(
160 &mut self,
161 instrument: PyObject,
162 quantity: Quantity,
163 price: Price,
164 use_quote_for_inverse: Option<bool>,
165 py: Python,
166 ) -> PyResult<Money> {
167 let instrument_type = pyobject_to_instrument_any(py, instrument)?;
168 match instrument_type {
169 InstrumentAny::CryptoFuture(inst) => {
170 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
171 }
172 InstrumentAny::CryptoPerpetual(inst) => {
173 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
174 }
175 InstrumentAny::CurrencyPair(inst) => {
176 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
177 }
178 InstrumentAny::Equity(inst) => {
179 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
180 }
181 InstrumentAny::FuturesContract(inst) => {
182 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
183 }
184 InstrumentAny::OptionContract(inst) => {
185 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
186 }
187 _ => Err(to_pyvalue_err("Unsupported instrument type")),
188 }
189 }
190
191 #[pyo3(name = "calculate_maintenance_margin")]
192 #[pyo3(signature = (instrument, quantity, price, use_quote_for_inverse=None))]
193 pub fn py_calculate_maintenance_margin(
194 &mut self,
195 instrument: PyObject,
196 quantity: Quantity,
197 price: Price,
198 use_quote_for_inverse: Option<bool>,
199 py: Python,
200 ) -> PyResult<Money> {
201 let instrument_type = pyobject_to_instrument_any(py, instrument)?;
202 match instrument_type {
203 InstrumentAny::CryptoFuture(inst) => {
204 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
205 }
206 InstrumentAny::CryptoPerpetual(inst) => {
207 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
208 }
209 InstrumentAny::CurrencyPair(inst) => {
210 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
211 }
212 InstrumentAny::Equity(inst) => {
213 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
214 }
215 InstrumentAny::FuturesContract(inst) => {
216 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
217 }
218 InstrumentAny::OptionContract(inst) => {
219 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
220 }
221 _ => Err(to_pyvalue_err("Unsupported instrument type")),
222 }
223 }
224
225 #[pyo3(name = "to_dict")]
226 fn py_to_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
227 let dict = PyDict::new(py);
228 dict.set_item("calculate_account_state", self.calculate_account_state)?;
229 let events_list: PyResult<Vec<PyObject>> =
230 self.events.iter().map(|item| item.py_to_dict(py)).collect();
231 dict.set_item("events", events_list.unwrap())?;
232 Ok(dict.into())
233 }
234}