nautilus_model/python/account/
wallet.rs1use indexmap::IndexMap;
17use nautilus_core::{
18 UnixNanos,
19 python::{IntoPyObjectNautilusExt, to_pyruntime_err, to_pyvalue_err},
20};
21use pyo3::{basic::CompareOp, prelude::*, types::PyDict};
22
23use crate::{
24 accounts::{Account, WalletAccount},
25 enums::{AccountType, LiquiditySide, OrderSide},
26 events::{AccountState, OrderFilled},
27 identifiers::AccountId,
28 position::Position,
29 python::instruments::pyobject_to_instrument_any,
30 types::{AccountBalance, Currency, Money, Price, Quantity},
31};
32
33#[pymethods]
34#[pyo3_stub_gen::derive::gen_stub_pymethods]
35impl WalletAccount {
36 #[new]
38 #[pyo3(signature = (event, calculate_account_state))]
39 pub fn py_new(event: AccountState, calculate_account_state: bool) -> PyResult<Self> {
40 Self::new_checked(event, calculate_account_state).map_err(to_pyvalue_err)
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 fn __repr__(&self) -> String {
52 format!(
53 "{}(id={}, type={}, base={})",
54 stringify!(WalletAccount),
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) -> IndexMap<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) -> IndexMap<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
134 #[pyo3(name = "balances_locked")]
135 fn py_balances_locked(&self) -> IndexMap<Currency, Money> {
136 self.balances_locked()
137 }
138
139 #[pyo3(name = "balance")]
140 #[pyo3(signature = (currency=None))]
141 fn py_balance(&self, currency: Option<Currency>) -> Option<AccountBalance> {
142 Account::balance(self, currency).copied()
143 }
144
145 #[pyo3(name = "balances")]
146 fn py_balances(&self) -> IndexMap<Currency, AccountBalance> {
147 Account::balances(self)
148 }
149
150 #[pyo3(name = "starting_balances")]
151 fn py_starting_balances(&self) -> IndexMap<Currency, Money> {
152 Account::starting_balances(self)
153 }
154
155 #[pyo3(name = "currencies")]
156 fn py_currencies(&self) -> Vec<Currency> {
157 Account::currencies(self)
158 }
159
160 #[pyo3(name = "is_cash_account")]
161 fn py_is_cash_account(&self) -> bool {
162 Account::is_cash_account(self)
163 }
164
165 #[pyo3(name = "is_margin_account")]
166 fn py_is_margin_account(&self) -> bool {
167 Account::is_margin_account(self)
168 }
169
170 #[pyo3(name = "purge_account_events")]
171 fn py_purge_account_events(&mut self, ts_now: u64, lookback_secs: u64) {
172 Account::purge_account_events(self, UnixNanos::from(ts_now), lookback_secs);
173 }
174
175 #[pyo3(name = "apply")]
176 fn py_apply(&mut self, event: AccountState) -> PyResult<()> {
177 self.apply(event).map_err(to_pyruntime_err)
178 }
179
180 #[pyo3(name = "calculate_balance_locked")]
181 #[pyo3(signature = (instrument, side, quantity, price, use_quote_for_inverse=None))]
182 fn py_calculate_balance_locked(
183 &mut self,
184 instrument: Py<PyAny>,
185 side: OrderSide,
186 quantity: Quantity,
187 price: Price,
188 use_quote_for_inverse: Option<bool>,
189 py: Python,
190 ) -> PyResult<Money> {
191 let instrument = pyobject_to_instrument_any(py, instrument)?;
192 self.calculate_balance_locked(&instrument, side, quantity, price, use_quote_for_inverse)
193 .map_err(to_pyvalue_err)
194 }
195
196 #[pyo3(name = "calculate_commission")]
197 #[pyo3(signature = (instrument, last_qty, last_px, liquidity_side, use_quote_for_inverse=None))]
198 fn py_calculate_commission(
199 &self,
200 instrument: Py<PyAny>,
201 last_qty: Quantity,
202 last_px: Price,
203 liquidity_side: LiquiditySide,
204 use_quote_for_inverse: Option<bool>,
205 py: Python,
206 ) -> PyResult<Money> {
207 if liquidity_side == LiquiditySide::NoLiquiditySide {
208 return Err(to_pyvalue_err("Invalid liquidity side"));
209 }
210 let instrument = pyobject_to_instrument_any(py, instrument)?;
211 self.calculate_commission(
212 &instrument,
213 last_qty,
214 last_px,
215 liquidity_side,
216 use_quote_for_inverse,
217 )
218 .map_err(to_pyvalue_err)
219 }
220
221 #[pyo3(name = "calculate_pnls")]
222 #[pyo3(signature = (instrument, fill, position=None))]
223 fn py_calculate_pnls(
224 &self,
225 instrument: Py<PyAny>,
226 fill: &OrderFilled,
227 position: Option<Position>,
228 py: Python,
229 ) -> PyResult<Vec<Money>> {
230 let instrument = pyobject_to_instrument_any(py, instrument)?;
231 self.calculate_pnls(&instrument, fill, position)
232 .map_err(to_pyvalue_err)
233 }
234
235 #[pyo3(name = "to_dict")]
236 fn py_to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
237 let dict = PyDict::new(py);
238 dict.set_item("type", "WalletAccount")?;
239 dict.set_item("calculate_account_state", self.calculate_account_state)?;
240 let events_list: PyResult<Vec<Py<PyAny>>> =
241 self.events.iter().map(|item| item.py_to_dict(py)).collect();
242 dict.set_item("events", events_list?)?;
243 Ok(dict.into())
244 }
245}