1use super::condition::ScreenerField;
14use serde::Serialize;
15
16#[non_exhaustive]
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49pub enum EquityField {
50 Ticker,
53 CompanyShortName,
55
56 EodPrice,
59 IntradayPrice,
61 IntradayPriceChange,
63 PercentChange,
65 Lastclose52WkHigh,
67 FiftyTwoWkPctChange,
69 Lastclose52WkLow,
71 IntradayMarketCap,
73 LastcloseMarketCap,
75
76 Region,
79 Sector,
81 PeerGroup,
83 Industry,
85 Exchange,
87
88 Beta,
91 AvgDailyVol3M,
93 PctHeldInsider,
95 PctHeldInst,
97 DayVolume,
99 EodVolume,
101
102 ShortPctSharesOut,
105 ShortInterest,
107 ShortPctFloat,
109 DaysToCover,
111 ShortInterestPctChange,
113
114 BookValueShare,
117 MarketCapToRevenue,
119 TevToRevenue,
121 PriceBookRatio,
123 PeRatio,
125 PriceTangibleBook,
127 PriceEarnings,
129 PegRatio5Y,
131
132 ConsecutiveDivYears,
135 Roa,
137 Roe,
139 ForwardDivPerShare,
141 ForwardDivYield,
143 ReturnOnCapital,
145
146 TevEbit,
149 NetDebtEbitda,
151 TotalDebtEquity,
153 LtDebtEquity,
155 EbitInterestExp,
157 EbitdaInterestExp,
159 TevEbitda,
161 TotalDebtEbitda,
163
164 QuickRatio,
167 AltmanZScore,
169 CurrentRatio,
171 OcfToCurrentLiab,
173
174 TotalRevenues,
177 NetIncomeMargin,
179 GrossProfit,
181 Ebitda1YrGrowth,
183 DilutedEpsContOps,
185 QuarterlyRevGrowth,
187 EpsGrowth,
189 NetIncome,
191 Ebitda,
193 DilutedEps1YrGrowth,
195 Revenue1YrGrowth,
197 OperatingIncome,
199 NetIncome1YrGrowth,
201 GrossProfitMargin,
203 EbitdaMargin,
205 Ebit,
207 BasicEpsContOps,
209 NetEpsBasic,
211 NetEpsDiluted,
213
214 TotalAssets,
217 CommonSharesOut,
219 TotalDebt,
221 TotalEquity,
223 TotalCurrentAssets,
225 CashAndStInvestments,
227 TotalCommonEquity,
229 TotalCurrentLiab,
231 TotalSharesOut,
233
234 LeveredFcf,
237 Capex,
239 CashFromOps,
241 LeveredFcf1YrGrowth,
243 UnleveredFcf,
245 CashFromOps1YrGrowth,
247
248 EsgScore,
251 EnvironmentalScore,
253 GovernanceScore,
255 SocialScore,
257 HighestControversy,
259}
260
261impl EquityField {
262 pub fn as_str(&self) -> &'static str {
264 match self {
265 EquityField::Ticker => "ticker",
266 EquityField::CompanyShortName => "companyshortname",
267 EquityField::EodPrice => "eodprice",
268 EquityField::IntradayPrice => "intradayprice",
269 EquityField::IntradayPriceChange => "intradaypricechange",
270 EquityField::PercentChange => "percentchange",
271 EquityField::Lastclose52WkHigh => "lastclose52weekhigh.lasttwelvemonths",
272 EquityField::FiftyTwoWkPctChange => "fiftytwowkpercentchange",
273 EquityField::Lastclose52WkLow => "lastclose52weeklow.lasttwelvemonths",
274 EquityField::IntradayMarketCap => "intradaymarketcap",
275 EquityField::LastcloseMarketCap => "lastclosemarketcap.lasttwelvemonths",
276 EquityField::Region => "region",
277 EquityField::Sector => "sector",
278 EquityField::PeerGroup => "peer_group",
279 EquityField::Industry => "industry",
280 EquityField::Exchange => "exchange",
281 EquityField::Beta => "beta",
282 EquityField::AvgDailyVol3M => "avgdailyvol3m",
283 EquityField::PctHeldInsider => "pctheldinsider",
284 EquityField::PctHeldInst => "pctheldinst",
285 EquityField::DayVolume => "dayvolume",
286 EquityField::EodVolume => "eodvolume",
287 EquityField::ShortPctSharesOut => "short_percentage_of_shares_outstanding.value",
288 EquityField::ShortInterest => "short_interest.value",
289 EquityField::ShortPctFloat => "short_percentage_of_float.value",
290 EquityField::DaysToCover => "days_to_cover_short.value",
291 EquityField::ShortInterestPctChange => "short_interest_percentage_change.value",
292 EquityField::BookValueShare => "bookvalueshare.lasttwelvemonths",
293 EquityField::MarketCapToRevenue => "lastclosemarketcaptotalrevenue.lasttwelvemonths",
294 EquityField::TevToRevenue => "lastclosetevtotalrevenue.lasttwelvemonths",
295 EquityField::PriceBookRatio => "pricebookratio.quarterly",
296 EquityField::PeRatio => "peratio.lasttwelvemonths",
297 EquityField::PriceTangibleBook => "lastclosepricetangiblebookvalue.lasttwelvemonths",
298 EquityField::PriceEarnings => "lastclosepriceearnings.lasttwelvemonths",
299 EquityField::PegRatio5Y => "pegratio_5y",
300 EquityField::ConsecutiveDivYears => "consecutive_years_of_dividend_growth_count",
301 EquityField::Roa => "returnonassets.lasttwelvemonths",
302 EquityField::Roe => "returnonequity.lasttwelvemonths",
303 EquityField::ForwardDivPerShare => "forward_dividend_per_share",
304 EquityField::ForwardDivYield => "forward_dividend_yield",
305 EquityField::ReturnOnCapital => "returnontotalcapital.lasttwelvemonths",
306 EquityField::TevEbit => "lastclosetevebit.lasttwelvemonths",
307 EquityField::NetDebtEbitda => "netdebtebitda.lasttwelvemonths",
308 EquityField::TotalDebtEquity => "totaldebtequity.lasttwelvemonths",
309 EquityField::LtDebtEquity => "ltdebtequity.lasttwelvemonths",
310 EquityField::EbitInterestExp => "ebitinterestexpense.lasttwelvemonths",
311 EquityField::EbitdaInterestExp => "ebitdainterestexpense.lasttwelvemonths",
312 EquityField::TevEbitda => "lastclosetevebitda.lasttwelvemonths",
313 EquityField::TotalDebtEbitda => "totaldebtebitda.lasttwelvemonths",
314 EquityField::QuickRatio => "quickratio.lasttwelvemonths",
315 EquityField::AltmanZScore => {
316 "altmanzscoreusingtheaveragestockinformationforaperiod.lasttwelvemonths"
317 }
318 EquityField::CurrentRatio => "currentratio.lasttwelvemonths",
319 EquityField::OcfToCurrentLiab => {
320 "operatingcashflowtocurrentliabilities.lasttwelvemonths"
321 }
322 EquityField::TotalRevenues => "totalrevenues.lasttwelvemonths",
323 EquityField::NetIncomeMargin => "netincomemargin.lasttwelvemonths",
324 EquityField::GrossProfit => "grossprofit.lasttwelvemonths",
325 EquityField::Ebitda1YrGrowth => "ebitda1yrgrowth.lasttwelvemonths",
326 EquityField::DilutedEpsContOps => "dilutedepscontinuingoperations.lasttwelvemonths",
327 EquityField::QuarterlyRevGrowth => "quarterlyrevenuegrowth.quarterly",
328 EquityField::EpsGrowth => "epsgrowth.lasttwelvemonths",
329 EquityField::NetIncome => "netincomeis.lasttwelvemonths",
330 EquityField::Ebitda => "ebitda.lasttwelvemonths",
331 EquityField::DilutedEps1YrGrowth => "dilutedeps1yrgrowth.lasttwelvemonths",
332 EquityField::Revenue1YrGrowth => "totalrevenues1yrgrowth.lasttwelvemonths",
333 EquityField::OperatingIncome => "operatingincome.lasttwelvemonths",
334 EquityField::NetIncome1YrGrowth => "netincome1yrgrowth.lasttwelvemonths",
335 EquityField::GrossProfitMargin => "grossprofitmargin.lasttwelvemonths",
336 EquityField::EbitdaMargin => "ebitdamargin.lasttwelvemonths",
337 EquityField::Ebit => "ebit.lasttwelvemonths",
338 EquityField::BasicEpsContOps => "basicepscontinuingoperations.lasttwelvemonths",
339 EquityField::NetEpsBasic => "netepsbasic.lasttwelvemonths",
340 EquityField::NetEpsDiluted => "netepsdiluted.lasttwelvemonths",
341 EquityField::TotalAssets => "totalassets.lasttwelvemonths",
342 EquityField::CommonSharesOut => "totalcommonsharesoutstanding.lasttwelvemonths",
343 EquityField::TotalDebt => "totaldebt.lasttwelvemonths",
344 EquityField::TotalEquity => "totalequity.lasttwelvemonths",
345 EquityField::TotalCurrentAssets => "totalcurrentassets.lasttwelvemonths",
346 EquityField::CashAndStInvestments => {
347 "totalcashandshortterminvestments.lasttwelvemonths"
348 }
349 EquityField::TotalCommonEquity => "totalcommonequity.lasttwelvemonths",
350 EquityField::TotalCurrentLiab => "totalcurrentliabilities.lasttwelvemonths",
351 EquityField::TotalSharesOut => "totalsharesoutstanding",
352 EquityField::LeveredFcf => "leveredfreecashflow.lasttwelvemonths",
353 EquityField::Capex => "capitalexpenditure.lasttwelvemonths",
354 EquityField::CashFromOps => "cashfromoperations.lasttwelvemonths",
355 EquityField::LeveredFcf1YrGrowth => "leveredfreecashflow1yrgrowth.lasttwelvemonths",
356 EquityField::UnleveredFcf => "unleveredfreecashflow.lasttwelvemonths",
357 EquityField::CashFromOps1YrGrowth => "cashfromoperations1yrgrowth.lasttwelvemonths",
358 EquityField::EsgScore => "esg_score",
359 EquityField::EnvironmentalScore => "environmental_score",
360 EquityField::GovernanceScore => "governance_score",
361 EquityField::SocialScore => "social_score",
362 EquityField::HighestControversy => "highest_controversy",
363 }
364 }
365
366 pub fn all() -> &'static [EquityField] {
370 &[
371 EquityField::Ticker,
372 EquityField::CompanyShortName,
373 EquityField::EodPrice,
374 EquityField::IntradayPrice,
375 EquityField::IntradayPriceChange,
376 EquityField::PercentChange,
377 EquityField::Lastclose52WkHigh,
378 EquityField::FiftyTwoWkPctChange,
379 EquityField::Lastclose52WkLow,
380 EquityField::IntradayMarketCap,
381 EquityField::LastcloseMarketCap,
382 EquityField::Region,
383 EquityField::Sector,
384 EquityField::PeerGroup,
385 EquityField::Industry,
386 EquityField::Exchange,
387 EquityField::Beta,
388 EquityField::AvgDailyVol3M,
389 EquityField::PctHeldInsider,
390 EquityField::PctHeldInst,
391 EquityField::DayVolume,
392 EquityField::EodVolume,
393 EquityField::ShortPctSharesOut,
394 EquityField::ShortInterest,
395 EquityField::ShortPctFloat,
396 EquityField::DaysToCover,
397 EquityField::ShortInterestPctChange,
398 EquityField::BookValueShare,
399 EquityField::MarketCapToRevenue,
400 EquityField::TevToRevenue,
401 EquityField::PriceBookRatio,
402 EquityField::PeRatio,
403 EquityField::PriceTangibleBook,
404 EquityField::PriceEarnings,
405 EquityField::PegRatio5Y,
406 EquityField::ConsecutiveDivYears,
407 EquityField::Roa,
408 EquityField::Roe,
409 EquityField::ForwardDivPerShare,
410 EquityField::ForwardDivYield,
411 EquityField::ReturnOnCapital,
412 EquityField::TevEbit,
413 EquityField::NetDebtEbitda,
414 EquityField::TotalDebtEquity,
415 EquityField::LtDebtEquity,
416 EquityField::EbitInterestExp,
417 EquityField::EbitdaInterestExp,
418 EquityField::TevEbitda,
419 EquityField::TotalDebtEbitda,
420 EquityField::QuickRatio,
421 EquityField::AltmanZScore,
422 EquityField::CurrentRatio,
423 EquityField::OcfToCurrentLiab,
424 EquityField::TotalRevenues,
425 EquityField::NetIncomeMargin,
426 EquityField::GrossProfit,
427 EquityField::Ebitda1YrGrowth,
428 EquityField::DilutedEpsContOps,
429 EquityField::QuarterlyRevGrowth,
430 EquityField::EpsGrowth,
431 EquityField::NetIncome,
432 EquityField::Ebitda,
433 EquityField::DilutedEps1YrGrowth,
434 EquityField::Revenue1YrGrowth,
435 EquityField::OperatingIncome,
436 EquityField::NetIncome1YrGrowth,
437 EquityField::GrossProfitMargin,
438 EquityField::EbitdaMargin,
439 EquityField::Ebit,
440 EquityField::BasicEpsContOps,
441 EquityField::NetEpsBasic,
442 EquityField::NetEpsDiluted,
443 EquityField::TotalAssets,
444 EquityField::CommonSharesOut,
445 EquityField::TotalDebt,
446 EquityField::TotalEquity,
447 EquityField::TotalCurrentAssets,
448 EquityField::CashAndStInvestments,
449 EquityField::TotalCommonEquity,
450 EquityField::TotalCurrentLiab,
451 EquityField::TotalSharesOut,
452 EquityField::LeveredFcf,
453 EquityField::Capex,
454 EquityField::CashFromOps,
455 EquityField::LeveredFcf1YrGrowth,
456 EquityField::UnleveredFcf,
457 EquityField::CashFromOps1YrGrowth,
458 EquityField::EsgScore,
459 EquityField::EnvironmentalScore,
460 EquityField::GovernanceScore,
461 EquityField::SocialScore,
462 EquityField::HighestControversy,
463 ]
464 }
465}
466
467impl std::str::FromStr for EquityField {
468 type Err = ();
469
470 fn from_str(s: &str) -> Result<Self, Self::Err> {
471 EquityField::all()
472 .iter()
473 .find(|f| f.as_str() == s)
474 .copied()
475 .ok_or(())
476 }
477}
478
479impl Serialize for EquityField {
480 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
481 serializer.serialize_str(self.as_str())
482 }
483}
484
485impl ScreenerField for EquityField {
486 fn as_str(&self) -> &'static str {
487 self.as_str()
488 }
489}
490
491#[non_exhaustive]
516#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
517pub enum FundField {
518 Ticker,
521 CompanyShortName,
523
524 EodPrice,
527 IntradayPriceChange,
529 IntradayPrice,
531
532 CategoryName,
535 PerformanceRating,
537 InitialInvestment,
539 AnnualReturnRank,
541 RiskRating,
543 Exchange,
545}
546
547impl FundField {
548 pub fn as_str(&self) -> &'static str {
550 match self {
551 FundField::Ticker => "ticker",
552 FundField::CompanyShortName => "companyshortname",
553 FundField::EodPrice => "eodprice",
554 FundField::IntradayPriceChange => "intradaypricechange",
555 FundField::IntradayPrice => "intradayprice",
556 FundField::CategoryName => "categoryname",
557 FundField::PerformanceRating => "performanceratingoverall",
558 FundField::InitialInvestment => "initialinvestment",
559 FundField::AnnualReturnRank => "annualreturnnavy1categoryrank",
560 FundField::RiskRating => "riskratingoverall",
561 FundField::Exchange => "exchange",
562 }
563 }
564
565 pub fn all() -> &'static [FundField] {
567 &[
568 FundField::Ticker,
569 FundField::CompanyShortName,
570 FundField::EodPrice,
571 FundField::IntradayPriceChange,
572 FundField::IntradayPrice,
573 FundField::CategoryName,
574 FundField::PerformanceRating,
575 FundField::InitialInvestment,
576 FundField::AnnualReturnRank,
577 FundField::RiskRating,
578 FundField::Exchange,
579 ]
580 }
581}
582
583impl std::str::FromStr for FundField {
584 type Err = ();
585
586 fn from_str(s: &str) -> Result<Self, Self::Err> {
587 FundField::all()
588 .iter()
589 .find(|f| f.as_str() == s)
590 .copied()
591 .ok_or(())
592 }
593}
594
595impl Serialize for FundField {
596 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
597 serializer.serialize_str(self.as_str())
598 }
599}
600
601impl ScreenerField for FundField {
602 fn as_str(&self) -> &'static str {
603 self.as_str()
604 }
605}
606
607#[cfg(test)]
612mod tests {
613 use super::*;
614
615 #[test]
616 fn test_equity_field_as_str() {
617 assert_eq!(EquityField::PeRatio.as_str(), "peratio.lasttwelvemonths");
618 assert_eq!(EquityField::AvgDailyVol3M.as_str(), "avgdailyvol3m");
619 assert_eq!(EquityField::Region.as_str(), "region");
620 assert_eq!(EquityField::IntradayMarketCap.as_str(), "intradaymarketcap");
621 assert_eq!(EquityField::Ticker.as_str(), "ticker");
622 }
623
624 #[test]
625 fn test_equity_field_from_str_round_trip() {
626 for field in EquityField::all() {
627 let s = field.as_str();
628 let parsed: EquityField = s.parse().unwrap_or_else(|_| {
629 panic!("Failed to parse EquityField from '{s}'");
630 });
631 assert_eq!(&parsed, field, "Round-trip failed for field '{s}'");
632 }
633 }
634
635 #[test]
636 fn test_equity_field_from_str_unknown_returns_err() {
637 assert!("invalid_field_name".parse::<EquityField>().is_err());
638 assert!("".parse::<EquityField>().is_err());
639 }
640
641 #[test]
642 fn test_equity_field_serializes_as_string() {
643 let json = serde_json::to_value(EquityField::PeRatio).unwrap();
644 assert_eq!(json, "peratio.lasttwelvemonths");
645 }
646
647 #[test]
648 fn test_fund_field_as_str() {
649 assert_eq!(
650 FundField::PerformanceRating.as_str(),
651 "performanceratingoverall"
652 );
653 assert_eq!(FundField::RiskRating.as_str(), "riskratingoverall");
654 assert_eq!(FundField::CategoryName.as_str(), "categoryname");
655 }
656
657 #[test]
658 fn test_fund_field_from_str_round_trip() {
659 for field in FundField::all() {
660 let s = field.as_str();
661 let parsed: FundField = s.parse().unwrap_or_else(|_| {
662 panic!("Failed to parse FundField from '{s}'");
663 });
664 assert_eq!(&parsed, field, "Round-trip failed for fund field '{s}'");
665 }
666 }
667
668 #[test]
669 fn test_fund_field_from_str_unknown_returns_err() {
670 assert!("intradaymarketcap".parse::<FundField>().is_err());
671 }
672}