nautilus_model/python/account/
margin.rs1use nautilus_core::python::to_pyvalue_err;
17use pyo3::{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(py),
38 CompareOp::Ne => self.ne(other).into_py(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.set_item(key.to_object(py), value).unwrap();
83 }
84 Ok(leverages.to_object(py))
85 }
86
87 #[pyo3(name = "leverage")]
88 fn py_leverage(&self, instrument_id: &InstrumentId) -> PyResult<f64> {
89 Ok(self.get_leverage(instrument_id))
90 }
91
92 #[pyo3(name = "set_leverage")]
93 fn py_set_leverage(&mut self, instrument_id: InstrumentId, leverage: f64) -> PyResult<()> {
94 self.set_leverage(instrument_id, leverage);
95 Ok(())
96 }
97
98 #[pyo3(name = "is_unleveraged")]
99 fn py_is_unleveraged(&self, instrument_id: InstrumentId) -> PyResult<bool> {
100 Ok(self.is_unleveraged(instrument_id))
101 }
102
103 #[pyo3(name = "initial_margins")]
104 fn py_initial_margins(&self, py: Python) -> PyResult<PyObject> {
105 let initial_margins = PyDict::new(py);
106 for (key, &value) in &self.initial_margins() {
107 initial_margins
108 .set_item(key.to_object(py), value.to_object(py))
109 .unwrap();
110 }
111 Ok(initial_margins.to_object(py))
112 }
113
114 #[pyo3(name = "maintenance_margins")]
115 fn py_maintenance_margins(&self, py: Python) -> PyResult<PyObject> {
116 let maintenance_margins = PyDict::new(py);
117 for (key, &value) in &self.maintenance_margins() {
118 maintenance_margins
119 .set_item(key.to_object(py), value.to_object(py))
120 .unwrap();
121 }
122 Ok(maintenance_margins.to_object(py))
123 }
124
125 #[pyo3(name = "update_initial_margin")]
126 fn py_update_initial_margin(
127 &mut self,
128 instrument_id: InstrumentId,
129 initial_margin: Money,
130 ) -> PyResult<()> {
131 self.update_initial_margin(instrument_id, initial_margin);
132 Ok(())
133 }
134
135 #[pyo3(name = "initial_margin")]
136 fn py_initial_margin(&self, instrument_id: InstrumentId) -> PyResult<Money> {
137 Ok(self.initial_margin(instrument_id))
138 }
139
140 #[pyo3(name = "update_maintenance_margin")]
141 fn py_update_maintenance_margin(
142 &mut self,
143 instrument_id: InstrumentId,
144 maintenance_margin: Money,
145 ) -> PyResult<()> {
146 self.update_maintenance_margin(instrument_id, maintenance_margin);
147 Ok(())
148 }
149
150 #[pyo3(name = "maintenance_margin")]
151 fn py_maintenance_margin(&self, instrument_id: InstrumentId) -> PyResult<Money> {
152 Ok(self.maintenance_margin(instrument_id))
153 }
154
155 #[pyo3(name = "calculate_initial_margin")]
156 #[pyo3(signature = (instrument, quantity, price, use_quote_for_inverse=None))]
157 pub fn py_calculate_initial_margin(
158 &mut self,
159 instrument: PyObject,
160 quantity: Quantity,
161 price: Price,
162 use_quote_for_inverse: Option<bool>,
163 py: Python,
164 ) -> PyResult<Money> {
165 let instrument_type = pyobject_to_instrument_any(py, instrument)?;
166 match instrument_type {
167 InstrumentAny::CryptoFuture(inst) => {
168 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
169 }
170 InstrumentAny::CryptoPerpetual(inst) => {
171 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
172 }
173 InstrumentAny::CurrencyPair(inst) => {
174 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
175 }
176 InstrumentAny::Equity(inst) => {
177 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
178 }
179 InstrumentAny::FuturesContract(inst) => {
180 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
181 }
182 InstrumentAny::OptionContract(inst) => {
183 Ok(self.calculate_initial_margin(inst, quantity, price, use_quote_for_inverse))
184 }
185 _ => Err(to_pyvalue_err("Unsupported instrument type")),
186 }
187 }
188
189 #[pyo3(name = "calculate_maintenance_margin")]
190 #[pyo3(signature = (instrument, quantity, price, use_quote_for_inverse=None))]
191 pub fn py_calculate_maintenance_margin(
192 &mut self,
193 instrument: PyObject,
194 quantity: Quantity,
195 price: Price,
196 use_quote_for_inverse: Option<bool>,
197 py: Python,
198 ) -> PyResult<Money> {
199 let instrument_type = pyobject_to_instrument_any(py, instrument)?;
200 match instrument_type {
201 InstrumentAny::CryptoFuture(inst) => {
202 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
203 }
204 InstrumentAny::CryptoPerpetual(inst) => {
205 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
206 }
207 InstrumentAny::CurrencyPair(inst) => {
208 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
209 }
210 InstrumentAny::Equity(inst) => {
211 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
212 }
213 InstrumentAny::FuturesContract(inst) => {
214 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
215 }
216 InstrumentAny::OptionContract(inst) => {
217 Ok(self.calculate_maintenance_margin(inst, quantity, price, use_quote_for_inverse))
218 }
219 _ => Err(to_pyvalue_err("Unsupported instrument type")),
220 }
221 }
222
223 #[pyo3(name = "to_dict")]
224 fn py_to_dict(&self, py: Python<'_>) -> PyResult<PyObject> {
225 let dict = PyDict::new(py);
226 dict.set_item("calculate_account_state", self.calculate_account_state)?;
227 let events_list: PyResult<Vec<PyObject>> =
228 self.events.iter().map(|item| item.py_to_dict(py)).collect();
229 dict.set_item("events", events_list.unwrap())?;
230 Ok(dict.into())
231 }
232}