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 serde::{Deserialize, Serialize};
19
20use crate::{
21    accounts::{base::Account, cash::CashAccount, margin::MarginAccount},
22    enums::{AccountType, LiquiditySide},
23    events::{AccountState, OrderFilled},
24    identifiers::AccountId,
25    instruments::InstrumentAny,
26    position::Position,
27    types::{AccountBalance, Currency, Money, Price, Quantity},
28};
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub enum AccountAny {
31    Margin(MarginAccount),
32    Cash(CashAccount),
33}
34
35impl AccountAny {
36    #[must_use]
37    pub fn id(&self) -> AccountId {
38        match self {
39            AccountAny::Margin(margin) => margin.id,
40            AccountAny::Cash(cash) => cash.id,
41        }
42    }
43
44    pub fn last_event(&self) -> Option<AccountState> {
45        match self {
46            AccountAny::Margin(margin) => margin.last_event(),
47            AccountAny::Cash(cash) => cash.last_event(),
48        }
49    }
50
51    pub fn events(&self) -> Vec<AccountState> {
52        match self {
53            AccountAny::Margin(margin) => margin.events(),
54            AccountAny::Cash(cash) => cash.events(),
55        }
56    }
57
58    pub fn apply(&mut self, event: AccountState) {
59        match self {
60            AccountAny::Margin(margin) => margin.apply(event),
61            AccountAny::Cash(cash) => cash.apply(event),
62        }
63    }
64
65    pub fn balances(&self) -> HashMap<Currency, AccountBalance> {
66        match self {
67            AccountAny::Margin(margin) => margin.balances(),
68            AccountAny::Cash(cash) => cash.balances(),
69        }
70    }
71
72    pub fn balances_locked(&self) -> HashMap<Currency, Money> {
73        match self {
74            AccountAny::Margin(margin) => margin.balances_locked(),
75            AccountAny::Cash(cash) => cash.balances_locked(),
76        }
77    }
78
79    pub fn base_currency(&self) -> Option<Currency> {
80        match self {
81            AccountAny::Margin(margin) => margin.base_currency(),
82            AccountAny::Cash(cash) => cash.base_currency(),
83        }
84    }
85
86    pub fn from_events(events: Vec<AccountState>) -> anyhow::Result<Self> {
87        if events.is_empty() {
88            anyhow::bail!("No order events provided to create `AccountAny`");
89        }
90
91        let init_event = events.first().unwrap();
92        let mut account = Self::from(init_event.clone());
93        for event in events.iter().skip(1) {
94            account.apply(event.clone());
95        }
96        Ok(account)
97    }
98
99    pub fn calculate_pnls(
100        &self,
101        instrument: InstrumentAny,
102        fill: OrderFilled,
103        position: Option<Position>,
104    ) -> anyhow::Result<Vec<Money>> {
105        match self {
106            AccountAny::Margin(margin) => margin.calculate_pnls(instrument, fill, position),
107            AccountAny::Cash(cash) => cash.calculate_pnls(instrument, fill, position),
108        }
109    }
110
111    pub fn calculate_commission(
112        &self,
113        instrument: InstrumentAny,
114        last_qty: Quantity,
115        last_px: Price,
116        liquidity_side: LiquiditySide,
117        use_quote_for_inverse: Option<bool>,
118    ) -> anyhow::Result<Money> {
119        match self {
120            AccountAny::Margin(margin) => margin.calculate_commission(
121                instrument,
122                last_qty,
123                last_px,
124                liquidity_side,
125                use_quote_for_inverse,
126            ),
127            AccountAny::Cash(cash) => cash.calculate_commission(
128                instrument,
129                last_qty,
130                last_px,
131                liquidity_side,
132                use_quote_for_inverse,
133            ),
134        }
135    }
136
137    pub fn balance(&self, currency: Option<Currency>) -> Option<&AccountBalance> {
138        match self {
139            AccountAny::Margin(margin) => margin.balance(currency),
140            AccountAny::Cash(cash) => cash.balance(currency),
141        }
142    }
143}
144
145impl From<AccountState> for AccountAny {
146    fn from(event: AccountState) -> Self {
147        match event.account_type {
148            AccountType::Margin => AccountAny::Margin(MarginAccount::new(event, false)),
149            AccountType::Cash => AccountAny::Cash(CashAccount::new(event, false)),
150            AccountType::Betting => todo!("Betting account not implemented"),
151        }
152    }
153}
154
155impl PartialEq for AccountAny {
156    fn eq(&self, other: &Self) -> bool {
157        self.id() == other.id()
158    }
159}