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) {
67 match self {
68 Self::Margin(margin) => margin.apply(event),
69 Self::Cash(cash) => cash.apply(event),
70 }
71 }
72
73 pub fn balances(&self) -> AHashMap<Currency, AccountBalance> {
74 match self {
75 Self::Margin(margin) => margin.balances(),
76 Self::Cash(cash) => cash.balances(),
77 }
78 }
79
80 pub fn balances_locked(&self) -> AHashMap<Currency, Money> {
81 match self {
82 Self::Margin(margin) => margin.balances_locked(),
83 Self::Cash(cash) => cash.balances_locked(),
84 }
85 }
86
87 pub fn base_currency(&self) -> Option<Currency> {
88 match self {
89 Self::Margin(margin) => margin.base_currency(),
90 Self::Cash(cash) => cash.base_currency(),
91 }
92 }
93
94 pub fn from_events(events: Vec<AccountState>) -> anyhow::Result<Self> {
102 if events.is_empty() {
103 anyhow::bail!("No order events provided to create `AccountAny`");
104 }
105
106 let init_event = events.first().unwrap();
107 let mut account = Self::from(init_event.clone());
108 for event in events.iter().skip(1) {
109 account.apply(event.clone());
110 }
111 Ok(account)
112 }
113
114 pub fn calculate_pnls(
118 &self,
119 instrument: InstrumentAny,
120 fill: OrderFilled,
121 position: Option<Position>,
122 ) -> anyhow::Result<Vec<Money>> {
123 match self {
124 Self::Margin(margin) => margin.calculate_pnls(instrument, fill, position),
125 Self::Cash(cash) => cash.calculate_pnls(instrument, fill, position),
126 }
127 }
128
129 pub fn calculate_commission(
133 &self,
134 instrument: InstrumentAny,
135 last_qty: Quantity,
136 last_px: Price,
137 liquidity_side: LiquiditySide,
138 use_quote_for_inverse: Option<bool>,
139 ) -> anyhow::Result<Money> {
140 match self {
141 Self::Margin(margin) => margin.calculate_commission(
142 instrument,
143 last_qty,
144 last_px,
145 liquidity_side,
146 use_quote_for_inverse,
147 ),
148 Self::Cash(cash) => cash.calculate_commission(
149 instrument,
150 last_qty,
151 last_px,
152 liquidity_side,
153 use_quote_for_inverse,
154 ),
155 }
156 }
157
158 pub fn balance(&self, currency: Option<Currency>) -> Option<&AccountBalance> {
159 match self {
160 Self::Margin(margin) => margin.balance(currency),
161 Self::Cash(cash) => cash.balance(currency),
162 }
163 }
164}
165
166impl From<AccountState> for AccountAny {
167 fn from(event: AccountState) -> Self {
168 match event.account_type {
169 AccountType::Margin => Self::Margin(MarginAccount::new(event, false)),
170 AccountType::Cash => Self::Cash(CashAccount::new(event, false, false)),
171 AccountType::Betting => panic!("Betting account not implemented"),
172 AccountType::Wallet => panic!("Wallet account not implemented"),
173 }
174 }
175}
176
177impl PartialEq for AccountAny {
178 fn eq(&self, other: &Self) -> bool {
179 self.id() == other.id()
180 }
181}