1use serde::{Deserialize, Serialize};
4use std::str::FromStr;
5use strum::{AsRefStr, Display, EnumString};
6
7use chrono::{DateTime, Utc};
8#[cfg(feature = "dataframe")]
9use df_derive::ToDataFrame;
10#[cfg(feature = "dataframe")]
11use paft_core::dataframe::ToDataFrame;
12
13#[derive(
18 Debug,
19 Clone,
20 PartialEq,
21 Eq,
22 Hash,
23 Serialize,
24 Deserialize,
25 Display,
26 AsRefStr,
27 EnumString,
28 Default,
29)]
30#[strum(ascii_case_insensitive)]
31#[serde(from = "String", into = "String")]
32pub enum FundKind {
33 #[strum(to_string = "ETF", serialize = "EXCHANGE_TRADED_FUND")]
35 #[default]
36 Etf,
37 #[strum(to_string = "MUTUAL_FUND", serialize = "MUTUAL")]
39 MutualFund,
40 #[strum(to_string = "INDEX_FUND", serialize = "INDEX")]
42 IndexFund,
43 #[strum(to_string = "CLOSED_END_FUND", serialize = "CEF")]
45 ClosedEndFund,
46 #[strum(to_string = "MONEY_MARKET_FUND", serialize = "MMF")]
48 MoneyMarketFund,
49 #[strum(to_string = "HEDGE_FUND")]
51 HedgeFund,
52 #[strum(to_string = "REIT", serialize = "REAL_ESTATE_INVESTMENT_TRUST")]
54 Reit,
55 #[strum(to_string = "UIT", serialize = "UNIT_INVESTMENT_TRUST")]
57 UnitInvestmentTrust,
58 Other(String),
60}
61
62impl From<String> for FundKind {
63 fn from(s: String) -> Self {
64 Self::from_str(&s).unwrap_or_else(|_| Self::Other(s.to_uppercase()))
66 }
67}
68
69impl From<FundKind> for String {
70 fn from(fund_kind: FundKind) -> Self {
71 match fund_kind {
72 FundKind::Other(s) => s,
73 _ => fund_kind.to_string(),
74 }
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
79#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
80pub struct Address {
82 pub street1: Option<String>,
84 pub street2: Option<String>,
86 pub city: Option<String>,
88 pub state: Option<String>,
90 pub country: Option<String>,
92 pub zip: Option<String>,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
99pub struct CompanyProfile {
100 pub name: String,
102 pub sector: Option<String>,
104 pub industry: Option<String>,
106 pub website: Option<String>,
108 pub address: Option<Address>,
110 pub summary: Option<String>,
112 pub isin: Option<String>,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
118#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
119pub struct FundProfile {
120 pub name: String,
122 pub family: Option<String>,
124 #[cfg_attr(feature = "dataframe", df_derive(as_string))]
126 pub kind: FundKind,
127 pub isin: Option<String>,
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
133pub enum Profile {
134 Company(CompanyProfile),
136 Fund(FundProfile),
138}
139
140impl Profile {
141 #[must_use]
143 pub fn isin(&self) -> Option<&str> {
144 match self {
145 Self::Company(c) => c.isin.as_deref(),
146 Self::Fund(f) => f.isin.as_deref(),
147 }
148 }
149}
150
151#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
153#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
154pub struct ShareCount {
155 #[serde(with = "chrono::serde::ts_seconds")]
157 pub date: DateTime<Utc>,
158 pub shares: u64,
160}