1use super::Exchange;
4use crate::{
5 DomainError,
6 identifiers::{Figi, Isin, Symbol},
7};
8use serde::{Deserialize, Serialize};
9use std::borrow::Cow;
10
11paft_core::other_string_code_type!(
12 pub struct OtherAssetKind for AssetKind;
14 type Error = DomainError;
15 parse(input) => input.parse::<AssetKind>();
16 invalid(input) => DomainError::InvalidAssetKindValue {
17 value: input.to_string(),
18 };
19);
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
30#[non_exhaustive]
31pub enum AssetKind {
32 Equity,
34 Crypto,
36 Fund,
38 Index,
40 Forex,
42 Bond,
44 Commodity,
46 Option,
48 Future,
50 REIT,
52 Warrant,
54 Convertible,
56 NFT,
58 PerpetualFuture,
60 LeveragedToken,
62 LPToken,
64 LST,
66 RWA,
68 Other(OtherAssetKind),
70}
71
72crate::string_enum_with_code!(
73 AssetKind, Other(OtherAssetKind),
74 "AssetKind",
75 type Error = DomainError;
76 invalid(input) => DomainError::InvalidAssetKindValue {
77 value: input.to_string(),
78 };
79 {
80 "EQUITY" => AssetKind::Equity,
81 "CRYPTO" => AssetKind::Crypto,
82 "FUND" => AssetKind::Fund,
83 "INDEX" => AssetKind::Index,
84 "FOREX" => AssetKind::Forex,
85 "BOND" => AssetKind::Bond,
86 "COMMODITY" => AssetKind::Commodity,
87 "OPTION" => AssetKind::Option,
88 "FUTURE" => AssetKind::Future,
89 "REIT" => AssetKind::REIT,
90 "WARRANT" => AssetKind::Warrant,
91 "CONVERTIBLE" => AssetKind::Convertible,
92 "NFT" => AssetKind::NFT,
93 "PERPETUAL_FUTURE" => AssetKind::PerpetualFuture,
94 "LEVERAGED_TOKEN" => AssetKind::LeveragedToken,
95 "LP_TOKEN" => AssetKind::LPToken,
96 "LST" => AssetKind::LST,
97 "RWA" => AssetKind::RWA,
98 },
99 {
100 "STOCK" => AssetKind::Equity,
101 "FX" => AssetKind::Forex,
102 }
103);
104
105crate::impl_display_via_code!(AssetKind);
106
107impl AssetKind {
108 pub fn other(input: &str) -> Result<Self, DomainError> {
115 OtherAssetKind::new(input).map(Self::Other)
116 }
117
118 #[must_use]
120 pub fn full_name(&self) -> Cow<'static, str> {
121 match self {
122 Self::Equity => Cow::Borrowed("Equity"),
123 Self::Crypto => Cow::Borrowed("Crypto"),
124 Self::Fund => Cow::Borrowed("Fund"),
125 Self::Index => Cow::Borrowed("Index"),
126 Self::Forex => Cow::Borrowed("Forex"),
127 Self::Bond => Cow::Borrowed("Bond"),
128 Self::Commodity => Cow::Borrowed("Commodity"),
129 Self::Option => Cow::Borrowed("Option"),
130 Self::Future => Cow::Borrowed("Future"),
131 Self::REIT => Cow::Borrowed("REIT"),
132 Self::Warrant => Cow::Borrowed("Warrant"),
133 Self::Convertible => Cow::Borrowed("Convertible"),
134 Self::NFT => Cow::Borrowed("NFT"),
135 Self::PerpetualFuture => Cow::Borrowed("Perpetual Future"),
136 Self::LeveragedToken => Cow::Borrowed("Leveraged Token"),
137 Self::LPToken => Cow::Borrowed("LP Token"),
138 Self::LST => Cow::Borrowed("Liquid Staking Token"),
139 Self::RWA => Cow::Borrowed("Real-World Asset"),
140 Self::Other(code) => Cow::Owned(code.as_ref().to_string()),
141 }
142 }
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
147#[cfg_attr(feature = "dataframe", derive(df_derive_macros::ToDataFrame))]
148pub struct Instrument {
149 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
151 pub symbol: Symbol,
152 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
154 pub exchange: Option<Exchange>,
155 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
157 pub figi: Option<Figi>,
158 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
160 pub isin: Option<Isin>,
161 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
163 pub kind: AssetKind,
164}
165
166impl Instrument {
167 #[must_use]
169 pub const fn new(symbol: Symbol, kind: AssetKind) -> Self {
170 Self {
171 symbol,
172 exchange: None,
173 figi: None,
174 isin: None,
175 kind,
176 }
177 }
178
179 #[cfg_attr(
184 feature = "tracing",
185 tracing::instrument(level = "debug", skip(symbol), err)
186 )]
187 pub fn from_symbol(symbol: impl AsRef<str>, kind: AssetKind) -> Result<Self, DomainError> {
188 Ok(Self {
189 symbol: Symbol::new(symbol.as_ref())?,
190 exchange: None,
191 figi: None,
192 isin: None,
193 kind,
194 })
195 }
196
197 #[cfg_attr(
202 feature = "tracing",
203 tracing::instrument(level = "debug", skip(symbol), err)
204 )]
205 pub fn from_symbol_and_exchange(
206 symbol: impl AsRef<str>,
207 exchange: Exchange,
208 kind: AssetKind,
209 ) -> Result<Self, DomainError> {
210 Ok(Self {
211 symbol: Symbol::new(symbol.as_ref())?,
212 exchange: Some(exchange),
213 figi: None,
214 isin: None,
215 kind,
216 })
217 }
218
219 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", err))]
224 pub fn from_figi(figi: &str, symbol: Symbol, kind: AssetKind) -> Result<Self, DomainError> {
225 Ok(Self {
226 symbol,
227 exchange: None,
228 figi: Some(Figi::new(figi)?),
229 isin: None,
230 kind,
231 })
232 }
233
234 #[must_use]
246 pub fn unique_key(&self) -> String {
247 let kind = self.kind.code();
248
249 if let Some(figi) = &self.figi {
250 return format!("{kind}|FIGI|{}", figi.as_ref());
251 }
252 if let Some(isin) = &self.isin {
253 return format!("{kind}|ISIN|{}", isin.as_ref());
254 }
255
256 let symbol = self.symbol.as_str();
257 let symbol_len = symbol.len();
258
259 if let Some(exchange) = &self.exchange {
260 return format!(
261 "{kind}|SYMBOL|{symbol_len}:{symbol}|EXCHANGE|{}",
262 exchange.code()
263 );
264 }
265
266 format!("{kind}|SYMBOL|{symbol_len}:{symbol}")
267 }
268
269 #[must_use]
272 pub fn display_key(&self) -> Cow<'_, str> {
273 if let Some(figi) = &self.figi {
274 return Cow::Borrowed(figi.as_ref());
275 }
276 if let Some(isin) = &self.isin {
277 return Cow::Borrowed(isin.as_ref());
278 }
279 if let Some(exchange) = &self.exchange {
280 return Cow::Owned(format!("{}@{}", self.symbol, exchange.code()));
281 }
282 Cow::Borrowed(self.symbol.as_str())
283 }
284}
285
286impl std::fmt::Display for Instrument {
287 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288 write!(f, "{}", self.display_key())
289 }
290}