use super::Exchange;
use crate::{
DomainError,
identifiers::{Figi, Isin, Symbol},
};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
paft_core::other_string_code_type!(
pub struct OtherAssetKind for AssetKind;
type Error = DomainError;
parse(input) => input.parse::<AssetKind>();
invalid(input) => DomainError::InvalidAssetKindValue {
value: input.to_string(),
};
);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum AssetKind {
Equity,
Crypto,
Fund,
Index,
Forex,
Bond,
Commodity,
Option,
Future,
REIT,
Warrant,
Convertible,
NFT,
PerpetualFuture,
LeveragedToken,
LPToken,
LST,
RWA,
Other(OtherAssetKind),
}
crate::string_enum_with_code!(
AssetKind, Other(OtherAssetKind),
"AssetKind",
type Error = DomainError;
invalid(input) => DomainError::InvalidAssetKindValue {
value: input.to_string(),
};
{
"EQUITY" => AssetKind::Equity,
"CRYPTO" => AssetKind::Crypto,
"FUND" => AssetKind::Fund,
"INDEX" => AssetKind::Index,
"FOREX" => AssetKind::Forex,
"BOND" => AssetKind::Bond,
"COMMODITY" => AssetKind::Commodity,
"OPTION" => AssetKind::Option,
"FUTURE" => AssetKind::Future,
"REIT" => AssetKind::REIT,
"WARRANT" => AssetKind::Warrant,
"CONVERTIBLE" => AssetKind::Convertible,
"NFT" => AssetKind::NFT,
"PERPETUAL_FUTURE" => AssetKind::PerpetualFuture,
"LEVERAGED_TOKEN" => AssetKind::LeveragedToken,
"LP_TOKEN" => AssetKind::LPToken,
"LST" => AssetKind::LST,
"RWA" => AssetKind::RWA,
},
{
"STOCK" => AssetKind::Equity,
"FX" => AssetKind::Forex,
}
);
crate::impl_display_via_code!(AssetKind);
impl AssetKind {
pub fn other(input: &str) -> Result<Self, DomainError> {
OtherAssetKind::new(input).map(Self::Other)
}
#[must_use]
pub fn full_name(&self) -> Cow<'static, str> {
match self {
Self::Equity => Cow::Borrowed("Equity"),
Self::Crypto => Cow::Borrowed("Crypto"),
Self::Fund => Cow::Borrowed("Fund"),
Self::Index => Cow::Borrowed("Index"),
Self::Forex => Cow::Borrowed("Forex"),
Self::Bond => Cow::Borrowed("Bond"),
Self::Commodity => Cow::Borrowed("Commodity"),
Self::Option => Cow::Borrowed("Option"),
Self::Future => Cow::Borrowed("Future"),
Self::REIT => Cow::Borrowed("REIT"),
Self::Warrant => Cow::Borrowed("Warrant"),
Self::Convertible => Cow::Borrowed("Convertible"),
Self::NFT => Cow::Borrowed("NFT"),
Self::PerpetualFuture => Cow::Borrowed("Perpetual Future"),
Self::LeveragedToken => Cow::Borrowed("Leveraged Token"),
Self::LPToken => Cow::Borrowed("LP Token"),
Self::LST => Cow::Borrowed("Liquid Staking Token"),
Self::RWA => Cow::Borrowed("Real-World Asset"),
Self::Other(code) => Cow::Owned(code.as_ref().to_string()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(df_derive_macros::ToDataFrame))]
pub struct Instrument {
#[cfg_attr(feature = "dataframe", df_derive(as_str))]
pub symbol: Symbol,
#[cfg_attr(feature = "dataframe", df_derive(as_str))]
pub exchange: Option<Exchange>,
#[cfg_attr(feature = "dataframe", df_derive(as_str))]
pub figi: Option<Figi>,
#[cfg_attr(feature = "dataframe", df_derive(as_str))]
pub isin: Option<Isin>,
#[cfg_attr(feature = "dataframe", df_derive(as_str))]
pub kind: AssetKind,
}
impl Instrument {
#[must_use]
pub const fn new(symbol: Symbol, kind: AssetKind) -> Self {
Self {
symbol,
exchange: None,
figi: None,
isin: None,
kind,
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "debug", skip(symbol), err)
)]
pub fn from_symbol(symbol: impl AsRef<str>, kind: AssetKind) -> Result<Self, DomainError> {
Ok(Self {
symbol: Symbol::new(symbol.as_ref())?,
exchange: None,
figi: None,
isin: None,
kind,
})
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "debug", skip(symbol), err)
)]
pub fn from_symbol_and_exchange(
symbol: impl AsRef<str>,
exchange: Exchange,
kind: AssetKind,
) -> Result<Self, DomainError> {
Ok(Self {
symbol: Symbol::new(symbol.as_ref())?,
exchange: Some(exchange),
figi: None,
isin: None,
kind,
})
}
#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", err))]
pub fn from_figi(figi: &str, symbol: Symbol, kind: AssetKind) -> Result<Self, DomainError> {
Ok(Self {
symbol,
exchange: None,
figi: Some(Figi::new(figi)?),
isin: None,
kind,
})
}
#[must_use]
pub fn unique_key(&self) -> String {
let kind = self.kind.code();
if let Some(figi) = &self.figi {
return format!("{kind}|FIGI|{}", figi.as_ref());
}
if let Some(isin) = &self.isin {
return format!("{kind}|ISIN|{}", isin.as_ref());
}
let symbol = self.symbol.as_str();
let symbol_len = symbol.len();
if let Some(exchange) = &self.exchange {
return format!(
"{kind}|SYMBOL|{symbol_len}:{symbol}|EXCHANGE|{}",
exchange.code()
);
}
format!("{kind}|SYMBOL|{symbol_len}:{symbol}")
}
#[must_use]
pub fn display_key(&self) -> Cow<'_, str> {
if let Some(figi) = &self.figi {
return Cow::Borrowed(figi.as_ref());
}
if let Some(isin) = &self.isin {
return Cow::Borrowed(isin.as_ref());
}
if let Some(exchange) = &self.exchange {
return Cow::Owned(format!("{}@{}", self.symbol, exchange.code()));
}
Cow::Borrowed(self.symbol.as_str())
}
}
impl std::fmt::Display for Instrument {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display_key())
}
}