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