nautilus_model/events/account/
state.rs1use std::fmt::{Display, Formatter};
17
18use nautilus_core::{UUID4, UnixNanos};
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(
93 || "None".to_string(),
94 |base_currency| format!("{}", base_currency.code)
95 ),
96 self.is_reported,
97 self.balances
98 .iter()
99 .map(|b| format!("{b}"))
100 .collect::<Vec<String>>()
101 .join(","),
102 self.margins
103 .iter()
104 .map(|m| format!("{m}"))
105 .collect::<Vec<String>>()
106 .join(","),
107 self.event_id
108 )
109 }
110}
111
112impl PartialEq for AccountState {
113 fn eq(&self, other: &Self) -> bool {
114 self.account_id == other.account_id
115 && self.account_type == other.account_type
116 && self.event_id == other.event_id
117 }
118}
119
120#[cfg(test)]
124mod tests {
125 use rstest::rstest;
126
127 use crate::events::{
128 AccountState,
129 account::stubs::{cash_account_state, margin_account_state},
130 };
131
132 #[rstest]
133 fn test_equality() {
134 let cash_account_state_1 = cash_account_state();
135 let cash_account_state_2 = cash_account_state();
136 assert_eq!(cash_account_state_1, cash_account_state_2);
137 }
138
139 #[rstest]
140 fn test_display_cash_account_state(cash_account_state: AccountState) {
141 let display = format!("{cash_account_state}");
142 assert_eq!(
143 display,
144 "AccountState(account_id=SIM-001, account_type=CASH, base_currency=USD, is_reported=true, \
145 balances=[AccountBalance(total=1525000.00 USD, locked=25000.00 USD, free=1500000.00 USD)], \
146 margins=[], event_id=16578139-a945-4b65-b46c-bc131a15d8e7)"
147 );
148 }
149
150 #[rstest]
151 fn test_display_margin_account_state(margin_account_state: AccountState) {
152 let display = format!("{margin_account_state}");
153 assert_eq!(
154 display,
155 "AccountState(account_id=SIM-001, account_type=MARGIN, base_currency=USD, is_reported=true, \
156 balances=[AccountBalance(total=1525000.00 USD, locked=25000.00 USD, free=1500000.00 USD)], \
157 margins=[MarginBalance(initial=5000.00 USD, maintenance=20000.00 USD, instrument_id=BTCUSDT.COINBASE)], \
158 event_id=16578139-a945-4b65-b46c-bc131a15d8e7)"
159 );
160 }
161}