Skip to main content

finance_query/constants/
screeners.rs

1/// Predefined Yahoo Finance screener selector
2///
3/// Passed to `finance::screener()` or `client.get_screener()` to select one of the
4/// 15 built-in Yahoo Finance screeners (equity or fund).
5///
6/// The `alias`es mirror the shorthands [`FromStr`](std::str::FromStr) accepts, so
7/// deserializing (axum path extraction, JSON) takes the same spellings parsing does.
8#[non_exhaustive]
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
10#[serde(rename_all = "kebab-case")]
11pub enum Screener {
12    // Equity screeners
13    /// Small caps with high EPS growth, sorted by volume
14    AggressiveSmallCaps,
15    /// Top gaining stocks (>3% change, >$2B market cap)
16    #[serde(alias = "gainers")]
17    DayGainers,
18    /// Top losing stocks (<-2.5% change, >$2B market cap)
19    #[serde(alias = "losers")]
20    DayLosers,
21    /// Tech stocks with 25%+ revenue and EPS growth
22    #[serde(alias = "growth-tech")]
23    GrowthTechnologyStocks,
24    /// Most actively traded stocks by volume
25    #[serde(alias = "actives")]
26    MostActives,
27    /// Stocks with highest short interest percentage
28    #[serde(alias = "most-shorted")]
29    MostShortedStocks,
30    /// Small cap gainers (<$2B market cap)
31    SmallCapGainers,
32    /// Low P/E (<20), low PEG (<1), high EPS growth (25%+)
33    #[serde(alias = "undervalued-growth")]
34    UndervaluedGrowthStocks,
35    /// Large caps ($10B-$100B) with low P/E and PEG
36    #[serde(alias = "undervalued-large")]
37    UndervaluedLargeCaps,
38    // Fund screeners
39    /// Low-risk foreign large cap funds (4-5 star rated)
40    ConservativeForeignFunds,
41    /// High yield bond funds (4-5 star rated)
42    HighYieldBond,
43    /// Large blend core funds (4-5 star rated)
44    PortfolioAnchors,
45    /// Large growth funds (4-5 star rated)
46    SolidLargeGrowthFunds,
47    /// Mid-cap growth funds (4-5 star rated)
48    SolidMidcapGrowthFunds,
49    /// Top performing mutual funds by percent change
50    TopMutualFunds,
51}
52
53impl Screener {
54    /// Convert to Yahoo Finance scrId parameter value (SCREAMING_SNAKE_CASE)
55    pub fn as_scr_id(&self) -> &'static str {
56        match self {
57            Screener::AggressiveSmallCaps => "aggressive_small_caps",
58            Screener::DayGainers => "day_gainers",
59            Screener::DayLosers => "day_losers",
60            Screener::GrowthTechnologyStocks => "growth_technology_stocks",
61            Screener::MostActives => "most_actives",
62            Screener::MostShortedStocks => "most_shorted_stocks",
63            Screener::SmallCapGainers => "small_cap_gainers",
64            Screener::UndervaluedGrowthStocks => "undervalued_growth_stocks",
65            Screener::UndervaluedLargeCaps => "undervalued_large_caps",
66            Screener::ConservativeForeignFunds => "conservative_foreign_funds",
67            Screener::HighYieldBond => "high_yield_bond",
68            Screener::PortfolioAnchors => "portfolio_anchors",
69            Screener::SolidLargeGrowthFunds => "solid_large_growth_funds",
70            Screener::SolidMidcapGrowthFunds => "solid_midcap_growth_funds",
71            Screener::TopMutualFunds => "top_mutual_funds",
72        }
73    }
74
75    /// Parse from string, returns None on invalid input
76    ///
77    /// # Example
78    /// ```
79    /// use finance_query::Screener;
80    ///
81    /// assert_eq!(Screener::parse("most-actives"), Some(Screener::MostActives));
82    /// assert_eq!(Screener::parse("day-gainers"), Some(Screener::DayGainers));
83    /// ```
84    pub fn parse(s: &str) -> Option<Self> {
85        s.parse().ok()
86    }
87
88    /// List all valid screener types for error messages
89    pub fn valid_types() -> &'static str {
90        "aggressive-small-caps, day-gainers, day-losers, growth-technology-stocks, \
91         most-actives, most-shorted-stocks, small-cap-gainers, undervalued-growth-stocks, \
92         undervalued-large-caps, conservative-foreign-funds, high-yield-bond, \
93         portfolio-anchors, solid-large-growth-funds, solid-midcap-growth-funds, \
94         top-mutual-funds"
95    }
96
97    /// Get all screener types as an array
98    pub fn all() -> &'static [Screener] {
99        &[
100            Screener::AggressiveSmallCaps,
101            Screener::DayGainers,
102            Screener::DayLosers,
103            Screener::GrowthTechnologyStocks,
104            Screener::MostActives,
105            Screener::MostShortedStocks,
106            Screener::SmallCapGainers,
107            Screener::UndervaluedGrowthStocks,
108            Screener::UndervaluedLargeCaps,
109            Screener::ConservativeForeignFunds,
110            Screener::HighYieldBond,
111            Screener::PortfolioAnchors,
112            Screener::SolidLargeGrowthFunds,
113            Screener::SolidMidcapGrowthFunds,
114            Screener::TopMutualFunds,
115        ]
116    }
117}
118
119impl std::str::FromStr for Screener {
120    type Err = ();
121
122    fn from_str(s: &str) -> Result<Self, Self::Err> {
123        match s.to_lowercase().replace('_', "-").as_str() {
124            "aggressive-small-caps" => Ok(Screener::AggressiveSmallCaps),
125            "day-gainers" | "gainers" => Ok(Screener::DayGainers),
126            "day-losers" | "losers" => Ok(Screener::DayLosers),
127            "growth-technology-stocks" | "growth-tech" => Ok(Screener::GrowthTechnologyStocks),
128            "most-actives" | "actives" => Ok(Screener::MostActives),
129            "most-shorted-stocks" | "most-shorted" => Ok(Screener::MostShortedStocks),
130            "small-cap-gainers" => Ok(Screener::SmallCapGainers),
131            "undervalued-growth-stocks" | "undervalued-growth" => {
132                Ok(Screener::UndervaluedGrowthStocks)
133            }
134            "undervalued-large-caps" | "undervalued-large" => Ok(Screener::UndervaluedLargeCaps),
135            "conservative-foreign-funds" => Ok(Screener::ConservativeForeignFunds),
136            "high-yield-bond" => Ok(Screener::HighYieldBond),
137            "portfolio-anchors" => Ok(Screener::PortfolioAnchors),
138            "solid-large-growth-funds" => Ok(Screener::SolidLargeGrowthFunds),
139            "solid-midcap-growth-funds" => Ok(Screener::SolidMidcapGrowthFunds),
140            "top-mutual-funds" => Ok(Screener::TopMutualFunds),
141            _ => Err(()),
142        }
143    }
144}