nautilus_model/accounts/
any.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16use std::collections::HashMap;
17
18use enum_dispatch::enum_dispatch;
19use serde::{Deserialize, Serialize};
20
21use crate::{
22    accounts::{Account, CashAccount, MarginAccount},
23    enums::{AccountType, LiquiditySide},
24    events::{AccountState, OrderFilled},
25    identifiers::AccountId,
26    instruments::InstrumentAny,
27    position::Position,
28    types::{AccountBalance, Currency, Money, Price, Quantity},
29};
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
32#[enum_dispatch(Account)]
33pub enum AccountAny {
34    Margin(MarginAccount),
35    Cash(CashAccount),
36}
37
38impl AccountAny {
39    #[must_use]
40    pub fn id(&self) -> AccountId {
41        match self {
42            AccountAny::Margin(margin) => margin.id,
43            AccountAny::Cash(cash) => cash.id,
44        }
45    }
46
47    pub fn last_event(&self) -> Option<AccountState> {
48        match self {
49            AccountAny::Margin(margin) => margin.last_event(),
50            AccountAny::Cash(cash) => cash.last_event(),
51        }
52    }
53
54    pub fn events(&self) -> Vec<AccountState> {
55        match self {
56            AccountAny::Margin(margin) => margin.events(),
57            AccountAny::Cash(cash) => cash.events(),
58        }
59    }
60
61    pub fn apply(&mut self, event: AccountState) {
62        match self {
63            AccountAny::Margin(margin) => margin.apply(event),
64            AccountAny::Cash(cash) => cash.apply(event),
65        }
66    }
67
68    pub fn balances(&self) -> HashMap<Currency, AccountBalance> {
69        match self {
70            AccountAny::Margin(margin) => margin.balances(),
71            AccountAny::Cash(cash) => cash.balances(),
72        }
73    }
74
75    pub fn balances_locked(&self) -> HashMap<Currency, Money> {
76        match self {
77            AccountAny::Margin(margin) => margin.balances_locked(),
78            AccountAny::Cash(cash) => cash.balances_locked(),
79        }
80    }
81
82    pub fn base_currency(&self) -> Option<Currency> {
83        match self {
84            AccountAny::Margin(margin) => margin.base_currency(),
85            AccountAny::Cash(cash) => cash.base_currency(),
86        }
87    }
88
89    pub fn from_events(events: Vec<AccountState>) -> anyhow::Result<Self> {
90        if events.is_empty() {
91            anyhow::bail!("No order events provided to create `AccountAny`");
92        }
93
94        let init_event = events.first().unwrap();
95        let mut account = Self::from(init_event.clone());
96        for event in events.iter().skip(1) {
97            account.apply(event.clone());
98        }
99        Ok(account)
100    }
101
102    pub fn calculate_pnls(
103        &self,
104        instrument: InstrumentAny,
105        fill: OrderFilled,
106        position: Option<Position>,
107    ) -> anyhow::Result<Vec<Money>> {
108        match self {
109            AccountAny::Margin(margin) => margin.calculate_pnls(instrument, fill, position),
110            AccountAny::Cash(cash) => cash.calculate_pnls(instrument, fill, position),
111        }
112    }
113
114    pub fn calculate_commission(
115        &self,
116        instrument: InstrumentAny,
117        last_qty: Quantity,
118        last_px: Price,
119        liquidity_side: LiquiditySide,
120        use_quote_for_inverse: Option<bool>,
121    ) -> anyhow::Result<Money> {
122        match self {
123            AccountAny::Margin(margin) => margin.calculate_commission(
124                instrument,
125                last_qty,
126                last_px,
127                liquidity_side,
128                use_quote_for_inverse,
129            ),
130            AccountAny::Cash(cash) => cash.calculate_commission(
131                instrument,
132                last_qty,
133                last_px,
134                liquidity_side,
135                use_quote_for_inverse,
136            ),
137        }
138    }
139
140    pub fn balance(&self, currency: Option<Currency>) -> Option<&AccountBalance> {
141        match self {
142            AccountAny::Margin(margin) => margin.balance(currency),
143            AccountAny::Cash(cash) => cash.balance(currency),
144        }
145    }
146}
147
148impl From<AccountState> for AccountAny {
149    fn from(event: AccountState) -> Self {
150        match event.account_type {
151            AccountType::Margin => AccountAny::Margin(MarginAccount::new(event, false)),
152            AccountType::Cash => AccountAny::Cash(CashAccount::new(event, false)),
153            AccountType::Betting => todo!("Betting account not implemented"),
154        }
155    }
156}
157
158impl PartialEq for AccountAny {
159    fn eq(&self, other: &Self) -> bool {
160        self.id() == other.id()
161    }
162}