nautilus_model/events/account/
state.rs1use std::fmt::{Display, Formatter};
17
18use nautilus_core::{UnixNanos, UUID4};
19use serde::{Deserialize, Serialize};
20
21use crate::{
22 enums::AccountType,
23 identifiers::AccountId,
24 types::{AccountBalance, Currency, MarginBalance},
25};
26
27#[repr(C)]
29#[derive(Debug, Clone, Serialize, Deserialize)]
30#[cfg_attr(
31 feature = "python",
32 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
33)]
34pub struct AccountState {
35 pub account_id: AccountId,
37 pub account_type: AccountType,
39 pub base_currency: Option<Currency>,
41 pub balances: Vec<AccountBalance>,
43 pub margins: Vec<MarginBalance>,
45 pub is_reported: bool,
48 pub event_id: UUID4,
50 pub ts_event: UnixNanos,
52 pub ts_init: UnixNanos,
54}
55
56impl AccountState {
57 #[allow(clippy::too_many_arguments)]
59 pub fn new(
60 account_id: AccountId,
61 account_type: AccountType,
62 balances: Vec<AccountBalance>,
63 margins: Vec<MarginBalance>,
64 is_reported: bool,
65 event_id: UUID4,
66 ts_event: UnixNanos,
67 ts_init: UnixNanos,
68 base_currency: Option<Currency>,
69 ) -> Self {
70 Self {
71 account_id,
72 account_type,
73 base_currency,
74 balances,
75 margins,
76 is_reported,
77 event_id,
78 ts_event,
79 ts_init,
80 }
81 }
82}
83
84impl Display for AccountState {
85 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
86 write!(
87 f,
88 "{}(account_id={}, account_type={}, base_currency={}, is_reported={}, balances=[{}], margins=[{}], event_id={})",
89 stringify!(AccountState),
90 self.account_id,
91 self.account_type,
92 self.base_currency.map_or_else(|| "None".to_string(), |base_currency | format!("{}", base_currency.code)),
93 self.is_reported,
94 self.balances.iter().map(|b| format!("{b}")).collect::<Vec<String>>().join(","),
95 self.margins.iter().map(|m| format!("{m}")).collect::<Vec<String>>().join(","),
96 self.event_id
97 )
98 }
99}
100
101impl PartialEq for AccountState {
102 fn eq(&self, other: &Self) -> bool {
103 self.account_id == other.account_id
104 && self.account_type == other.account_type
105 && self.event_id == other.event_id
106 }
107}
108
109#[cfg(test)]
113mod tests {
114 use rstest::rstest;
115
116 use crate::events::{
117 account::stubs::{cash_account_state, margin_account_state},
118 AccountState,
119 };
120
121 #[rstest]
122 fn test_equality() {
123 let cash_account_state_1 = cash_account_state();
124 let cash_account_state_2 = cash_account_state();
125 assert_eq!(cash_account_state_1, cash_account_state_2);
126 }
127
128 #[rstest]
129 fn test_display_cash_account_state(cash_account_state: AccountState) {
130 let display = format!("{cash_account_state}");
131 assert_eq!(
132 display,
133 "AccountState(account_id=SIM-001, account_type=CASH, base_currency=USD, is_reported=true, \
134 balances=[AccountBalance(total=1525000.00 USD, locked=25000.00 USD, free=1500000.00 USD)], \
135 margins=[], event_id=16578139-a945-4b65-b46c-bc131a15d8e7)"
136 );
137 }
138
139 #[rstest]
140 fn test_display_margin_account_state(margin_account_state: AccountState) {
141 let display = format!("{margin_account_state}");
142 assert_eq!(
143 display,
144 "AccountState(account_id=SIM-001, account_type=MARGIN, base_currency=USD, is_reported=true, \
145 balances=[AccountBalance(total=1525000.00 USD, locked=25000.00 USD, free=1500000.00 USD)], \
146 margins=[MarginBalance(initial=5000.00 USD, maintenance=20000.00 USD, instrument_id=BTCUSDT.COINBASE)], \
147 event_id=16578139-a945-4b65-b46c-bc131a15d8e7)"
148 );
149 }
150}