use core::fmt; use std::borrow::Cow; use std::fmt::{Display, Formatter}; use std::str::FromStr; use regex::Regex; use crate::BoxError; #[derive(Clone, PartialEq, Hash, Debug, Eq)] pub struct Symbol { name: Cow<'static, str>, } impl From for Symbol where S: Into, { fn from(item: S) -> Self { Symbol::new(item.into()) } } impl Symbol { pub const XMR: Symbol = Symbol::new_static("XMR"); pub const BTC: Symbol = Symbol::new_static("BTC"); pub const ETH: Symbol = Symbol::new_static("ETH"); pub const LTC: Symbol = Symbol::new_static("LTC"); pub const DOT: Symbol = Symbol::new_static("DOT"); // Paper trading pub const TESTBTC: Symbol = Symbol::new_static("TESTBTC"); pub const TESTUSD: Symbol = Symbol::new_static("TESTUSD"); // Fiat coins pub const USD: Symbol = Symbol::new_static("USD"); pub const GBP: Symbol = Symbol::new_static("GBP"); pub const EUR: Symbol = Symbol::new_static("EUR"); pub fn new(name: String) -> Self { Symbol { name: Cow::from(name), } } pub const fn new_static(name: &'static str) -> Self { Symbol { name: Cow::Borrowed(name), } } pub fn name(&self) -> &str { &self.name } } impl Display for Symbol { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", self.name) } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct SymbolPair { quote: Symbol, base: Symbol, } impl SymbolPair { pub fn new(quote: Symbol, base: Symbol) -> Self { SymbolPair { quote, base } } pub fn trading_repr(&self) -> String { format!("t{}{}", self.quote, self.base) } pub fn funding_repr(&self) -> String { format!("f{}{}", self.quote, self.base) } pub fn quote(&self) -> &Symbol { &self.quote } pub fn base(&self) -> &Symbol { &self.base } } impl Into for SymbolPair { fn into(self) -> String { format!("{}/{}", self.base, self.quote) } } impl FromStr for SymbolPair { type Err = BoxError; fn from_str(value: &str) -> Result { const REGEX: &str = r"^[t|f](?P\w{3,7}):?(?P\w{3,7})"; let captures = Regex::new(REGEX)?.captures(&value).ok_or("Invalid input")?; let quote = captures.name("quote").ok_or("Quote not found")?.as_str(); let base = captures.name("base").ok_or("Base not found")?.as_str(); Ok(SymbolPair { quote: quote.into(), base: base.into(), }) } } impl Display for SymbolPair { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}/{}", self.base, self.quote) } } #[derive(Clone)] enum WalletKind { Margin, Exchange, Funding, } #[derive(Clone)] struct Balance { pair: SymbolPair, base_price: f64, base_amount: f64, quote_equivalent: f64, wallet: WalletKind, } impl Balance { pub fn new(pair: SymbolPair, base_price: f64, base_amount: f64, wallet: WalletKind) -> Self { Balance { pair, base_price, base_amount, quote_equivalent: base_amount * base_price, wallet, } } pub fn pair(&self) -> &SymbolPair { &self.pair } pub fn base_price(&self) -> f64 { self.base_price } pub fn base_amount(&self) -> f64 { self.base_amount } pub fn quote_equivalent(&self) -> f64 { self.quote_equivalent } pub fn wallet(&self) -> &WalletKind { &self.wallet } } struct BalanceGroup { quote_equivalent: f64, balances: Vec, } impl BalanceGroup { pub fn new() -> Self { BalanceGroup { balances: Vec::new(), quote_equivalent: 0f64, } } pub fn add_balance(&mut self, balance: &Balance) { self.balances.push(balance.clone()); self.quote_equivalent += balance.quote_equivalent() } pub fn currency_names(&self) -> Vec { self.balances .iter() .map(|x| x.pair().base().name().into()) .collect() } pub fn balances(&self) -> &Vec { &self.balances } }