1use serde::{Deserialize, Serialize};
4use std::str::FromStr;
5
6use chrono::NaiveDate;
7#[cfg(feature = "dataframe")]
8use df_derive_macros::ToDataFrame;
9use paft_decimal::{Decimal, Ratio};
10use paft_domain::ReportingPeriod;
11use paft_money::Money;
12
13use crate::FundamentalsError;
14
15paft_core::other_string_code_type!(
16 pub struct OtherTransactionType for TransactionType;
18 type Error = FundamentalsError;
19 parse(input) => TransactionType::from_str(input);
20 invalid(input) => FundamentalsError::InvalidEnumValue {
21 enum_name: "TransactionType",
22 value: input.to_string(),
23 };
24);
25
26#[derive(Debug, Clone, PartialEq, Eq, Hash)]
38#[non_exhaustive]
39pub enum TransactionType {
40 Buy,
42 Sell,
44 Award,
46 Exercise,
48 Gift,
50 Conversion,
52 Other(OtherTransactionType),
54}
55
56impl TransactionType {
57 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", err))]
62 pub fn try_from_str(input: &str) -> Result<Self, FundamentalsError> {
63 Self::from_str(input)
64 }
65
66 pub fn other(input: &str) -> Result<Self, FundamentalsError> {
72 OtherTransactionType::new(input).map(Self::Other)
73 }
74}
75
76paft_core::string_enum_with_code!(
78 TransactionType, Other(OtherTransactionType), "TransactionType",
79 type Error = FundamentalsError;
80 invalid(input) => FundamentalsError::InvalidEnumValue {
81 enum_name: "TransactionType",
82 value: input.to_string(),
83 };
84 {
85 "BUY" => TransactionType::Buy,
86 "SELL" => TransactionType::Sell,
87 "AWARD" => TransactionType::Award,
88 "EXERCISE" => TransactionType::Exercise,
89 "GIFT" => TransactionType::Gift,
90 "CONVERSION" => TransactionType::Conversion
91 },
92 {
93 "PURCHASE" => TransactionType::Buy,
95 "ACQUISITION" => TransactionType::Buy,
96 "SALE" => TransactionType::Sell,
97 "DISPOSAL" => TransactionType::Sell,
98 "GRANT" => TransactionType::Award,
99 "STOCK_AWARD" => TransactionType::Award,
100 "OPTION_EXERCISE" => TransactionType::Exercise
101 }
102);
103
104paft_core::impl_display_via_code!(TransactionType);
106
107paft_core::other_string_code_type!(
108 pub struct OtherInsiderPosition for InsiderPosition;
110 type Error = FundamentalsError;
111 parse(input) => InsiderPosition::from_str(input);
112 invalid(input) => FundamentalsError::InvalidEnumValue {
113 enum_name: "InsiderPosition",
114 value: input.to_string(),
115 };
116);
117
118#[derive(Debug, Clone, PartialEq, Eq, Hash)]
130#[non_exhaustive]
131pub enum InsiderPosition {
132 Officer,
134 Director,
136 Owner,
138 Ceo,
140 Cfo,
142 Coo,
144 Cto,
146 President,
148 VicePresident,
150 Secretary,
152 Treasurer,
154 Other(OtherInsiderPosition),
156}
157
158impl InsiderPosition {
159 #[cfg_attr(feature = "tracing", tracing::instrument(level = "debug", err))]
164 pub fn try_from_str(input: &str) -> Result<Self, FundamentalsError> {
165 Self::from_str(input)
166 }
167
168 pub fn other(input: &str) -> Result<Self, FundamentalsError> {
174 OtherInsiderPosition::new(input).map(Self::Other)
175 }
176}
177
178paft_core::string_enum_with_code!(
180 InsiderPosition, Other(OtherInsiderPosition), "InsiderPosition",
181 type Error = FundamentalsError;
182 invalid(input) => FundamentalsError::InvalidEnumValue {
183 enum_name: "InsiderPosition",
184 value: input.to_string(),
185 };
186 {
187 "OFFICER" => InsiderPosition::Officer,
188 "DIRECTOR" => InsiderPosition::Director,
189 "OWNER" => InsiderPosition::Owner,
190 "CEO" => InsiderPosition::Ceo,
191 "CFO" => InsiderPosition::Cfo,
192 "COO" => InsiderPosition::Coo,
193 "CTO" => InsiderPosition::Cto,
194 "PRESIDENT" => InsiderPosition::President,
195 "VICE_PRESIDENT" => InsiderPosition::VicePresident,
196 "SECRETARY" => InsiderPosition::Secretary,
197 "TREASURER" => InsiderPosition::Treasurer
198 },
199 {
200 "BOARD_MEMBER" => InsiderPosition::Director,
202 "BENEFICIAL_OWNER" => InsiderPosition::Owner,
203 "10_OWNER" => InsiderPosition::Owner,
204 "CHIEF_EXECUTIVE_OFFICER" => InsiderPosition::Ceo,
205 "CHIEF_FINANCIAL_OFFICER" => InsiderPosition::Cfo,
206 "CHIEF_OPERATING_OFFICER" => InsiderPosition::Coo,
207 "CHIEF_TECHNOLOGY_OFFICER" => InsiderPosition::Cto,
208 "VP" => InsiderPosition::VicePresident
209 }
210);
211
212paft_core::impl_display_via_code!(InsiderPosition);
213
214#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
215#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
216pub struct MajorHolder {
218 pub category: String,
220 #[cfg_attr(feature = "dataframe", df_derive(decimal(precision = 38, scale = 10)))]
222 pub value: Ratio,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
227#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
228pub struct InstitutionalHolder {
229 pub holder: String,
231 pub shares: Option<u64>,
233 pub date_reported: NaiveDate,
235 #[cfg_attr(feature = "dataframe", df_derive(decimal(precision = 38, scale = 10)))]
237 pub pct_held: Option<Ratio>,
238 pub value: Option<Money>,
240}
241
242#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
244#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
245pub struct InsiderTransaction {
246 pub insider: String,
248 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
250 pub position: InsiderPosition,
251 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
253 pub transaction_type: TransactionType,
254 pub shares: Option<u64>,
256 pub value: Option<Money>,
258 pub transaction_date: NaiveDate,
260 pub url: Option<String>,
262}
263
264#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
266#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
267pub struct InsiderRosterHolder {
268 pub name: String,
270 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
272 pub position: InsiderPosition,
273 #[cfg_attr(feature = "dataframe", df_derive(as_str))]
275 pub most_recent_transaction: TransactionType,
276 pub latest_transaction_date: NaiveDate,
278 pub shares_owned_directly: Option<u64>,
280 pub position_direct_date: NaiveDate,
282}
283
284#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
286#[cfg_attr(feature = "dataframe", derive(ToDataFrame))]
287pub struct NetSharePurchaseActivity {
288 #[cfg_attr(feature = "dataframe", df_derive(as_string))]
290 pub period: ReportingPeriod,
291 pub buy_shares: Option<u64>,
293 pub buy_count: Option<u64>,
295 pub sell_shares: Option<u64>,
297 pub sell_count: Option<u64>,
299 pub net_shares: Option<i64>,
301 pub net_count: Option<i64>,
303 pub total_insider_shares: Option<u64>,
305 #[serde(default, with = "paft_decimal::serde::option_canonical_str")]
307 pub net_percent_insider_shares: Option<Decimal>,
308}