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