nautilus_model/accounts/
any.rs1use ahash::AHashMap;
23use enum_dispatch::enum_dispatch;
24use serde::{Deserialize, Serialize};
25
26use crate::{
27 accounts::{Account, CashAccount, MarginAccount},
28 enums::{AccountType, LiquiditySide},
29 events::{AccountState, OrderFilled},
30 identifiers::AccountId,
31 instruments::InstrumentAny,
32 position::Position,
33 types::{AccountBalance, Currency, Money, Price, Quantity},
34};
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37#[enum_dispatch(Account)]
38pub enum AccountAny {
39 Margin(MarginAccount),
40 Cash(CashAccount),
41}
42
43impl AccountAny {
44 #[must_use]
45 pub fn id(&self) -> AccountId {
46 match self {
47 Self::Margin(margin) => margin.id,
48 Self::Cash(cash) => cash.id,
49 }
50 }
51
52 pub fn last_event(&self) -> Option<AccountState> {
53 match self {
54 Self::Margin(margin) => margin.last_event(),
55 Self::Cash(cash) => cash.last_event(),
56 }
57 }
58
59 pub fn events(&self) -> Vec<AccountState> {
60 match self {
61 Self::Margin(margin) => margin.events(),
62 Self::Cash(cash) => cash.events(),
63 }
64 }
65
66 pub fn apply(&mut self, event: AccountState) -> anyhow::Result<()> {
73 match self {
74 Self::Margin(margin) => margin.apply(event),
75 Self::Cash(cash) => cash.apply(event),
76 }
77 }
78
79 pub fn balances(&self) -> AHashMap<Currency, AccountBalance> {
80 match self {
81 Self::Margin(margin) => margin.balances(),
82 Self::Cash(cash) => cash.balances(),
83 }
84 }
85
86 pub fn balances_locked(&self) -> AHashMap<Currency, Money> {
87 match self {
88 Self::Margin(margin) => margin.balances_locked(),
89 Self::Cash(cash) => cash.balances_locked(),
90 }
91 }
92
93 pub fn base_currency(&self) -> Option<Currency> {
94 match self {
95 Self::Margin(margin) => margin.base_currency(),
96 Self::Cash(cash) => cash.base_currency(),
97 }
98 }
99
100 pub fn from_events(events: Vec<AccountState>) -> anyhow::Result<Self> {
108 if events.is_empty() {
109 anyhow::bail!("No order events provided to create `AccountAny`");
110 }
111
112 let init_event = events.first().unwrap();
113 let mut account = Self::from(init_event.clone());
114 for event in events.iter().skip(1) {
115 account.apply(event.clone())?;
116 }
117 Ok(account)
118 }
119
120 pub fn calculate_pnls(
124 &self,
125 instrument: InstrumentAny,
126 fill: OrderFilled,
127 position: Option<Position>,
128 ) -> anyhow::Result<Vec<Money>> {
129 match self {
130 Self::Margin(margin) => margin.calculate_pnls(instrument, fill, position),
131 Self::Cash(cash) => cash.calculate_pnls(instrument, fill, position),
132 }
133 }
134
135 pub fn calculate_commission(
139 &self,
140 instrument: InstrumentAny,
141 last_qty: Quantity,
142 last_px: Price,
143 liquidity_side: LiquiditySide,
144 use_quote_for_inverse: Option<bool>,
145 ) -> anyhow::Result<Money> {
146 match self {
147 Self::Margin(margin) => margin.calculate_commission(
148 instrument,
149 last_qty,
150 last_px,
151 liquidity_side,
152 use_quote_for_inverse,
153 ),
154 Self::Cash(cash) => cash.calculate_commission(
155 instrument,
156 last_qty,
157 last_px,
158 liquidity_side,
159 use_quote_for_inverse,
160 ),
161 }
162 }
163
164 pub fn balance(&self, currency: Option<Currency>) -> Option<&AccountBalance> {
165 match self {
166 Self::Margin(margin) => margin.balance(currency),
167 Self::Cash(cash) => cash.balance(currency),
168 }
169 }
170}
171
172impl AccountAny {
173 pub fn try_from_state(event: AccountState) -> Result<Self, &'static str> {
179 match event.account_type {
180 AccountType::Margin => Ok(Self::Margin(MarginAccount::new(event, false))),
181 AccountType::Cash => Ok(Self::Cash(CashAccount::new(event, false, false))),
182 AccountType::Betting => Err("Betting accounts are not yet supported in Rust, \
183 use Python for betting workflows"),
184 AccountType::Wallet => Err("Wallet accounts are not yet implemented in Rust"),
185 }
186 }
187}
188
189impl From<AccountState> for AccountAny {
190 fn from(event: AccountState) -> Self {
197 Self::try_from_state(event).expect("Unsupported account type")
198 }
199}
200
201impl PartialEq for AccountAny {
202 fn eq(&self, other: &Self) -> bool {
203 self.id() == other.id()
204 }
205}