nautilus_model/python/account/
cash.rs1use nautilus_core::python::{IntoPyObjectNautilusExt, to_pyvalue_err};
17use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
18
19use crate::{
20 accounts::{Account, CashAccount},
21 enums::{AccountType, LiquiditySide, OrderSide},
22 events::{AccountState, OrderFilled},
23 identifiers::AccountId,
24 position::Position,
25 python::instruments::pyobject_to_instrument_any,
26 types::{Currency, Money, Price, Quantity},
27};
28
29#[pymethods]
30impl CashAccount {
31 #[new]
32 #[pyo3(signature = (event, calculate_account_state, allow_borrowing = false))]
33 pub fn py_new(
34 event: AccountState,
35 calculate_account_state: bool,
36 allow_borrowing: bool,
37 ) -> Self {
38 Self::new(event, calculate_account_state, allow_borrowing)
39 }
40
41 fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py<PyAny> {
42 match op {
43 CompareOp::Eq => self.eq(other).into_py_any_unwrap(py),
44 CompareOp::Ne => self.ne(other).into_py_any_unwrap(py),
45 _ => py.NotImplemented(),
46 }
47 }
48
49 #[getter]
50 fn id(&self) -> AccountId {
51 self.id
52 }
53
54 #[getter]
55 fn allow_borrowing(&self) -> bool {
56 self.allow_borrowing
57 }
58
59 fn __repr__(&self) -> String {
60 format!(
61 "{}(id={}, type={}, base={})",
62 stringify!(CashAccount),
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 #[getter]
73 #[pyo3(name = "id")]
74 fn py_id(&self) -> AccountId {
75 self.id
76 }
77
78 #[getter]
79 #[pyo3(name = "account_type")]
80 fn py_account_type(&self) -> AccountType {
81 self.account_type
82 }
83
84 #[getter]
85 #[pyo3(name = "base_currency")]
86 fn py_base_currency(&self) -> Option<Currency> {
87 self.base_currency
88 }
89
90 #[getter]
91 #[pyo3(name = "last_event")]
92 fn py_last_event(&self) -> Option<AccountState> {
93 self.last_event()
94 }
95
96 #[getter]
97 #[pyo3(name = "event_count")]
98 fn py_event_count(&self) -> usize {
99 self.event_count()
100 }
101
102 #[getter]
103 #[pyo3(name = "events")]
104 fn py_events(&self) -> Vec<AccountState> {
105 self.events()
106 }
107
108 #[getter]
109 #[pyo3(name = "calculate_account_state")]
110 fn py_calculate_account_state(&self) -> bool {
111 self.calculate_account_state
112 }
113
114 #[pyo3(name = "balance_total")]
115 #[pyo3(signature = (currency=None))]
116 fn py_balance_total(&self, currency: Option<Currency>) -> Option<Money> {
117 self.balance_total(currency)
118 }
119
120 #[pyo3(name = "balances_total")]
121 fn py_balances_total(&self) -> std::collections::HashMap<Currency, Money> {
122 self.balances_total().into_iter().collect()
123 }
124
125 #[pyo3(name = "balance_free")]
126 #[pyo3(signature = (currency=None))]
127 fn py_balance_free(&self, currency: Option<Currency>) -> Option<Money> {
128 self.balance_free(currency)
129 }
130
131 #[pyo3(name = "balances_free")]
132 fn py_balances_free(&self) -> std::collections::HashMap<Currency, Money> {
133 self.balances_free().into_iter().collect()
134 }
135
136 #[pyo3(name = "balance_locked")]
137 #[pyo3(signature = (currency=None))]
138 fn py_balance_locked(&self, currency: Option<Currency>) -> Option<Money> {
139 self.balance_locked(currency)
140 }
141 #[pyo3(name = "balances_locked")]
142 fn py_balances_locked(&self) -> std::collections::HashMap<Currency, Money> {
143 self.balances_locked().into_iter().collect()
144 }
145
146 #[pyo3(name = "apply")]
147 fn py_apply(&mut self, event: AccountState) {
148 self.apply(event);
149 }
150
151 #[pyo3(name = "calculate_balance_locked")]
152 #[pyo3(signature = (instrument, side, quantity, price, use_quote_for_inverse=None))]
153 fn py_calculate_balance_locked(
154 &mut self,
155 instrument: Py<PyAny>,
156 side: OrderSide,
157 quantity: Quantity,
158 price: Price,
159 use_quote_for_inverse: Option<bool>,
160 py: Python,
161 ) -> PyResult<Money> {
162 let instrument = pyobject_to_instrument_any(py, instrument)?;
163 self.calculate_balance_locked(instrument, side, quantity, price, use_quote_for_inverse)
164 .map_err(to_pyvalue_err)
165 }
166
167 #[pyo3(name = "calculate_commission")]
168 #[pyo3(signature = (instrument, last_qty, last_px, liquidity_side, use_quote_for_inverse=None))]
169 fn py_calculate_commission(
170 &self,
171 instrument: Py<PyAny>,
172 last_qty: Quantity,
173 last_px: Price,
174 liquidity_side: LiquiditySide,
175 use_quote_for_inverse: Option<bool>,
176 py: Python,
177 ) -> PyResult<Money> {
178 if liquidity_side == LiquiditySide::NoLiquiditySide {
179 return Err(to_pyvalue_err("Invalid liquidity side"));
180 }
181 let instrument = pyobject_to_instrument_any(py, instrument)?;
182 self.calculate_commission(
183 instrument,
184 last_qty,
185 last_px,
186 liquidity_side,
187 use_quote_for_inverse,
188 )
189 .map_err(to_pyvalue_err)
190 }
191
192 #[pyo3(name = "calculate_pnls")]
193 #[pyo3(signature = (instrument, fill, position=None))]
194 fn py_calculate_pnls(
195 &self,
196 instrument: Py<PyAny>,
197 fill: OrderFilled,
198 position: Option<Position>,
199 py: Python,
200 ) -> PyResult<Vec<Money>> {
201 let instrument = pyobject_to_instrument_any(py, instrument)?;
202 self.calculate_pnls(instrument, fill, position)
203 .map_err(to_pyvalue_err)
204 }
205
206 #[pyo3(name = "to_dict")]
207 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
208 let dict = PyDict::new(py);
209 dict.set_item("calculate_account_state", self.calculate_account_state)?;
210 let events_list: PyResult<Vec<Py<PyAny>>> =
211 self.events.iter().map(|item| item.py_to_dict(py)).collect();
212 dict.set_item("events", events_list.unwrap())?;
213 Ok(dict.into())
214 }
215}