Skip to main content

finance_query/
constants.rs

1use serde::{Deserialize, Serialize};
2
3/// Predefined screener selectors for Yahoo Finance
4pub mod screeners {
5    /// Predefined Yahoo Finance screener selector
6    ///
7    /// Passed to `finance::screener()` or `client.get_screener()` to select one of the
8    /// 15 built-in Yahoo Finance screeners (equity or fund).
9    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10    pub enum Screener {
11        // Equity screeners
12        /// Small caps with high EPS growth, sorted by volume
13        AggressiveSmallCaps,
14        /// Top gaining stocks (>3% change, >$2B market cap)
15        DayGainers,
16        /// Top losing stocks (<-2.5% change, >$2B market cap)
17        DayLosers,
18        /// Tech stocks with 25%+ revenue and EPS growth
19        GrowthTechnologyStocks,
20        /// Most actively traded stocks by volume
21        MostActives,
22        /// Stocks with highest short interest percentage
23        MostShortedStocks,
24        /// Small cap gainers (<$2B market cap)
25        SmallCapGainers,
26        /// Low P/E (<20), low PEG (<1), high EPS growth (25%+)
27        UndervaluedGrowthStocks,
28        /// Large caps ($10B-$100B) with low P/E and PEG
29        UndervaluedLargeCaps,
30        // Fund screeners
31        /// Low-risk foreign large cap funds (4-5 star rated)
32        ConservativeForeignFunds,
33        /// High yield bond funds (4-5 star rated)
34        HighYieldBond,
35        /// Large blend core funds (4-5 star rated)
36        PortfolioAnchors,
37        /// Large growth funds (4-5 star rated)
38        SolidLargeGrowthFunds,
39        /// Mid-cap growth funds (4-5 star rated)
40        SolidMidcapGrowthFunds,
41        /// Top performing mutual funds by percent change
42        TopMutualFunds,
43    }
44
45    impl Screener {
46        /// Convert to Yahoo Finance scrId parameter value (SCREAMING_SNAKE_CASE)
47        pub fn as_scr_id(&self) -> &'static str {
48            match self {
49                Screener::AggressiveSmallCaps => "aggressive_small_caps",
50                Screener::DayGainers => "day_gainers",
51                Screener::DayLosers => "day_losers",
52                Screener::GrowthTechnologyStocks => "growth_technology_stocks",
53                Screener::MostActives => "most_actives",
54                Screener::MostShortedStocks => "most_shorted_stocks",
55                Screener::SmallCapGainers => "small_cap_gainers",
56                Screener::UndervaluedGrowthStocks => "undervalued_growth_stocks",
57                Screener::UndervaluedLargeCaps => "undervalued_large_caps",
58                Screener::ConservativeForeignFunds => "conservative_foreign_funds",
59                Screener::HighYieldBond => "high_yield_bond",
60                Screener::PortfolioAnchors => "portfolio_anchors",
61                Screener::SolidLargeGrowthFunds => "solid_large_growth_funds",
62                Screener::SolidMidcapGrowthFunds => "solid_midcap_growth_funds",
63                Screener::TopMutualFunds => "top_mutual_funds",
64            }
65        }
66
67        /// Parse from string, returns None on invalid input
68        ///
69        /// # Example
70        /// ```
71        /// use finance_query::Screener;
72        ///
73        /// assert_eq!(Screener::parse("most-actives"), Some(Screener::MostActives));
74        /// assert_eq!(Screener::parse("day-gainers"), Some(Screener::DayGainers));
75        /// ```
76        pub fn parse(s: &str) -> Option<Self> {
77            s.parse().ok()
78        }
79
80        /// List all valid screener types for error messages
81        pub fn valid_types() -> &'static str {
82            "aggressive-small-caps, day-gainers, day-losers, growth-technology-stocks, \
83             most-actives, most-shorted-stocks, small-cap-gainers, undervalued-growth-stocks, \
84             undervalued-large-caps, conservative-foreign-funds, high-yield-bond, \
85             portfolio-anchors, solid-large-growth-funds, solid-midcap-growth-funds, \
86             top-mutual-funds"
87        }
88
89        /// Get all screener types as an array
90        pub fn all() -> &'static [Screener] {
91            &[
92                Screener::AggressiveSmallCaps,
93                Screener::DayGainers,
94                Screener::DayLosers,
95                Screener::GrowthTechnologyStocks,
96                Screener::MostActives,
97                Screener::MostShortedStocks,
98                Screener::SmallCapGainers,
99                Screener::UndervaluedGrowthStocks,
100                Screener::UndervaluedLargeCaps,
101                Screener::ConservativeForeignFunds,
102                Screener::HighYieldBond,
103                Screener::PortfolioAnchors,
104                Screener::SolidLargeGrowthFunds,
105                Screener::SolidMidcapGrowthFunds,
106                Screener::TopMutualFunds,
107            ]
108        }
109    }
110
111    impl std::str::FromStr for Screener {
112        type Err = ();
113
114        fn from_str(s: &str) -> Result<Self, Self::Err> {
115            match s.to_lowercase().replace('_', "-").as_str() {
116                "aggressive-small-caps" => Ok(Screener::AggressiveSmallCaps),
117                "day-gainers" | "gainers" => Ok(Screener::DayGainers),
118                "day-losers" | "losers" => Ok(Screener::DayLosers),
119                "growth-technology-stocks" | "growth-tech" => Ok(Screener::GrowthTechnologyStocks),
120                "most-actives" | "actives" => Ok(Screener::MostActives),
121                "most-shorted-stocks" | "most-shorted" => Ok(Screener::MostShortedStocks),
122                "small-cap-gainers" => Ok(Screener::SmallCapGainers),
123                "undervalued-growth-stocks" | "undervalued-growth" => {
124                    Ok(Screener::UndervaluedGrowthStocks)
125                }
126                "undervalued-large-caps" | "undervalued-large" => {
127                    Ok(Screener::UndervaluedLargeCaps)
128                }
129                "conservative-foreign-funds" => Ok(Screener::ConservativeForeignFunds),
130                "high-yield-bond" => Ok(Screener::HighYieldBond),
131                "portfolio-anchors" => Ok(Screener::PortfolioAnchors),
132                "solid-large-growth-funds" => Ok(Screener::SolidLargeGrowthFunds),
133                "solid-midcap-growth-funds" => Ok(Screener::SolidMidcapGrowthFunds),
134                "top-mutual-funds" => Ok(Screener::TopMutualFunds),
135                _ => Err(()),
136            }
137        }
138    }
139}
140
141/// Yahoo Finance sector types
142///
143/// These are the 11 GICS sectors available on Yahoo Finance.
144pub mod sectors {
145    use serde::{Deserialize, Serialize};
146
147    /// Market sector types available on Yahoo Finance
148    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
149    #[serde(rename_all = "kebab-case")]
150    pub enum Sector {
151        /// Technology sector (software, semiconductors, hardware)
152        Technology,
153        /// Financial Services sector (banks, insurance, asset management)
154        FinancialServices,
155        /// Consumer Cyclical sector (retail, automotive, leisure)
156        ConsumerCyclical,
157        /// Communication Services sector (telecom, media, entertainment)
158        CommunicationServices,
159        /// Healthcare sector (pharma, biotech, medical devices)
160        Healthcare,
161        /// Industrials sector (aerospace, machinery, construction)
162        Industrials,
163        /// Consumer Defensive sector (food, beverages, household products)
164        ConsumerDefensive,
165        /// Energy sector (oil, gas, renewable energy)
166        Energy,
167        /// Basic Materials sector (chemicals, metals, mining)
168        BasicMaterials,
169        /// Real Estate sector (REITs, property management)
170        RealEstate,
171        /// Utilities sector (electric, gas, water utilities)
172        Utilities,
173    }
174
175    impl Sector {
176        /// Convert to Yahoo Finance API path segment (lowercase with hyphens)
177        pub fn as_api_path(&self) -> &'static str {
178            match self {
179                Sector::Technology => "technology",
180                Sector::FinancialServices => "financial-services",
181                Sector::ConsumerCyclical => "consumer-cyclical",
182                Sector::CommunicationServices => "communication-services",
183                Sector::Healthcare => "healthcare",
184                Sector::Industrials => "industrials",
185                Sector::ConsumerDefensive => "consumer-defensive",
186                Sector::Energy => "energy",
187                Sector::BasicMaterials => "basic-materials",
188                Sector::RealEstate => "real-estate",
189                Sector::Utilities => "utilities",
190            }
191        }
192
193        /// Get human-readable display name
194        pub fn display_name(&self) -> &'static str {
195            match self {
196                Sector::Technology => "Technology",
197                Sector::FinancialServices => "Financial Services",
198                Sector::ConsumerCyclical => "Consumer Cyclical",
199                Sector::CommunicationServices => "Communication Services",
200                Sector::Healthcare => "Healthcare",
201                Sector::Industrials => "Industrials",
202                Sector::ConsumerDefensive => "Consumer Defensive",
203                Sector::Energy => "Energy",
204                Sector::BasicMaterials => "Basic Materials",
205                Sector::RealEstate => "Real Estate",
206                Sector::Utilities => "Utilities",
207            }
208        }
209
210        /// List all valid sector types for error messages
211        pub fn valid_types() -> &'static str {
212            "technology, financial-services, consumer-cyclical, communication-services, \
213             healthcare, industrials, consumer-defensive, energy, basic-materials, \
214             real-estate, utilities"
215        }
216
217        /// Get all sector types as an array
218        pub fn all() -> &'static [Sector] {
219            &[
220                Sector::Technology,
221                Sector::FinancialServices,
222                Sector::ConsumerCyclical,
223                Sector::CommunicationServices,
224                Sector::Healthcare,
225                Sector::Industrials,
226                Sector::ConsumerDefensive,
227                Sector::Energy,
228                Sector::BasicMaterials,
229                Sector::RealEstate,
230                Sector::Utilities,
231            ]
232        }
233    }
234
235    impl std::str::FromStr for Sector {
236        type Err = ();
237
238        fn from_str(s: &str) -> Result<Self, Self::Err> {
239            match s.to_lowercase().replace('_', "-").as_str() {
240                "technology" | "tech" => Ok(Sector::Technology),
241                "financial-services" | "financials" | "financial" => Ok(Sector::FinancialServices),
242                "consumer-cyclical" => Ok(Sector::ConsumerCyclical),
243                "communication-services" | "communication" => Ok(Sector::CommunicationServices),
244                "healthcare" | "health" => Ok(Sector::Healthcare),
245                "industrials" | "industrial" => Ok(Sector::Industrials),
246                "consumer-defensive" => Ok(Sector::ConsumerDefensive),
247                "energy" => Ok(Sector::Energy),
248                "basic-materials" | "materials" => Ok(Sector::BasicMaterials),
249                "real-estate" | "realestate" => Ok(Sector::RealEstate),
250                "utilities" | "utility" => Ok(Sector::Utilities),
251                _ => Err(()),
252            }
253        }
254    }
255
256    impl std::fmt::Display for Sector {
257        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258            write!(f, "{}", self.display_name())
259        }
260    }
261
262    impl From<Sector> for String {
263        /// Returns the display name used by Yahoo Finance screener (e.g. `"Technology"`).
264        fn from(v: Sector) -> Self {
265            v.display_name().to_string()
266        }
267    }
268}
269
270/// World market indices
271pub mod indices {
272    /// Region categories for world indices
273    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
274    pub enum Region {
275        /// North and South America
276        Americas,
277        /// European markets
278        Europe,
279        /// Asia and Pacific markets
280        AsiaPacific,
281        /// Middle East and Africa
282        MiddleEastAfrica,
283        /// Currency indices
284        Currencies,
285    }
286
287    impl std::str::FromStr for Region {
288        type Err = ();
289
290        fn from_str(s: &str) -> Result<Self, Self::Err> {
291            match s.to_lowercase().replace(['-', '_'], "").as_str() {
292                "americas" | "america" | "am" => Ok(Region::Americas),
293                "europe" | "eu" => Ok(Region::Europe),
294                "asiapacific" | "asia" | "apac" => Ok(Region::AsiaPacific),
295                "middleeastafrica" | "mea" | "emea" => Ok(Region::MiddleEastAfrica),
296                "currencies" | "currency" | "fx" => Ok(Region::Currencies),
297                _ => Err(()),
298            }
299        }
300    }
301
302    impl Region {
303        /// Parse from string, returns None on invalid input
304        pub fn parse(s: &str) -> Option<Self> {
305            s.parse().ok()
306        }
307
308        /// Get the symbols for this region
309        pub fn symbols(&self) -> &'static [&'static str] {
310            match self {
311                Region::Americas => AMERICAS,
312                Region::Europe => EUROPE,
313                Region::AsiaPacific => ASIA_PACIFIC,
314                Region::MiddleEastAfrica => MIDDLE_EAST_AFRICA,
315                Region::Currencies => CURRENCIES,
316            }
317        }
318
319        /// Convert to string representation
320        pub fn as_str(&self) -> &'static str {
321            match self {
322                Region::Americas => "americas",
323                Region::Europe => "europe",
324                Region::AsiaPacific => "asia-pacific",
325                Region::MiddleEastAfrica => "middle-east-africa",
326                Region::Currencies => "currencies",
327            }
328        }
329
330        /// All region variants
331        pub fn all() -> &'static [Region] {
332            &[
333                Region::Americas,
334                Region::Europe,
335                Region::AsiaPacific,
336                Region::MiddleEastAfrica,
337                Region::Currencies,
338            ]
339        }
340    }
341
342    /// Americas indices
343    pub const AMERICAS: &[&str] = &[
344        "^GSPC",   // S&P 500
345        "^DJI",    // Dow Jones Industrial Average
346        "^IXIC",   // NASDAQ Composite
347        "^NYA",    // NYSE Composite Index
348        "^XAX",    // NYSE American Composite Index
349        "^RUT",    // Russell 2000 Index
350        "^VIX",    // CBOE Volatility Index
351        "^GSPTSE", // S&P/TSX Composite (Canada)
352        "^BVSP",   // IBOVESPA (Brazil)
353        "^MXX",    // IPC MEXICO
354        "^IPSA",   // S&P IPSA (Chile)
355        "^MERV",   // MERVAL (Argentina)
356    ];
357
358    /// Europe indices
359    pub const EUROPE: &[&str] = &[
360        "^FTSE",            // FTSE 100 (UK)
361        "^GDAXI",           // DAX (Germany)
362        "^FCHI",            // CAC 40 (France)
363        "^STOXX50E",        // EURO STOXX 50
364        "^N100",            // Euronext 100 Index
365        "^BFX",             // BEL 20 (Belgium)
366        "^BUK100P",         // Cboe UK 100
367        "MOEX.ME",          // Moscow Exchange
368        "^125904-USD-STRD", // MSCI EUROPE
369    ];
370
371    /// Asia Pacific indices
372    pub const ASIA_PACIFIC: &[&str] = &[
373        "^N225",     // Nikkei 225 (Japan)
374        "^HSI",      // Hang Seng Index (Hong Kong)
375        "000001.SS", // SSE Composite Index (China)
376        "^KS11",     // KOSPI (South Korea)
377        "^TWII",     // Taiwan Weighted Index
378        "^STI",      // STI Index (Singapore)
379        "^AXJO",     // S&P/ASX 200 (Australia)
380        "^AORD",     // All Ordinaries (Australia)
381        "^NZ50",     // S&P/NZX 50 (New Zealand)
382        "^BSESN",    // S&P BSE SENSEX (India)
383        "^JKSE",     // IDX Composite (Indonesia)
384        "^KLSE",     // FTSE Bursa Malaysia KLCI
385    ];
386
387    /// Middle East & Africa indices
388    pub const MIDDLE_EAST_AFRICA: &[&str] = &[
389        "^TA125.TA", // TA-125 (Israel)
390        "^CASE30",   // EGX 30 (Egypt)
391        "^JN0U.JO",  // Top 40 USD Net TRI (South Africa)
392    ];
393
394    /// Currency indices
395    pub const CURRENCIES: &[&str] = &[
396        "DX-Y.NYB", // US Dollar Index
397        "^XDB",     // British Pound Currency Index
398        "^XDE",     // Euro Currency Index
399        "^XDN",     // Japanese Yen Currency Index
400        "^XDA",     // Australian Dollar Currency Index
401    ];
402
403    /// All world indices (all regions combined)
404    pub fn all_symbols() -> Vec<&'static str> {
405        Region::all()
406            .iter()
407            .flat_map(|r| r.symbols().iter().copied())
408            .collect()
409    }
410}
411
412/// Fundamental timeseries field types for financial statements
413///
414/// These constants represent field names that must be prefixed with frequency ("annual" or "quarterly")
415/// Example: "annualTotalRevenue", "quarterlyTotalRevenue"
416#[allow(missing_docs)]
417pub mod fundamental_types {
418    // ==================
419    // INCOME STATEMENT (48 fields)
420    // ==================
421    pub const TOTAL_REVENUE: &str = "TotalRevenue";
422    pub const OPERATING_REVENUE: &str = "OperatingRevenue";
423    pub const COST_OF_REVENUE: &str = "CostOfRevenue";
424    pub const GROSS_PROFIT: &str = "GrossProfit";
425    pub const OPERATING_EXPENSE: &str = "OperatingExpense";
426    pub const SELLING_GENERAL_AND_ADMIN: &str = "SellingGeneralAndAdministration";
427    pub const RESEARCH_AND_DEVELOPMENT: &str = "ResearchAndDevelopment";
428    pub const OPERATING_INCOME: &str = "OperatingIncome";
429    pub const NET_INTEREST_INCOME: &str = "NetInterestIncome";
430    pub const INTEREST_EXPENSE: &str = "InterestExpense";
431    pub const INTEREST_INCOME: &str = "InterestIncome";
432    pub const NET_NON_OPERATING_INTEREST_INCOME_EXPENSE: &str =
433        "NetNonOperatingInterestIncomeExpense";
434    pub const OTHER_INCOME_EXPENSE: &str = "OtherIncomeExpense";
435    pub const PRETAX_INCOME: &str = "PretaxIncome";
436    pub const TAX_PROVISION: &str = "TaxProvision";
437    pub const NET_INCOME_COMMON_STOCKHOLDERS: &str = "NetIncomeCommonStockholders";
438    pub const NET_INCOME: &str = "NetIncome";
439    pub const DILUTED_EPS: &str = "DilutedEPS";
440    pub const BASIC_EPS: &str = "BasicEPS";
441    pub const DILUTED_AVERAGE_SHARES: &str = "DilutedAverageShares";
442    pub const BASIC_AVERAGE_SHARES: &str = "BasicAverageShares";
443    pub const EBIT: &str = "EBIT";
444    pub const EBITDA: &str = "EBITDA";
445    pub const RECONCILED_COST_OF_REVENUE: &str = "ReconciledCostOfRevenue";
446    pub const RECONCILED_DEPRECIATION: &str = "ReconciledDepreciation";
447    pub const NET_INCOME_FROM_CONTINUING_OPERATION_NET_MINORITY_INTEREST: &str =
448        "NetIncomeFromContinuingOperationNetMinorityInterest";
449    pub const NORMALIZED_EBITDA: &str = "NormalizedEBITDA";
450    pub const TOTAL_EXPENSES: &str = "TotalExpenses";
451    pub const TOTAL_OPERATING_INCOME_AS_REPORTED: &str = "TotalOperatingIncomeAsReported";
452
453    // ==================
454    // BALANCE SHEET (42 fields)
455    // ==================
456    pub const TOTAL_ASSETS: &str = "TotalAssets";
457    pub const CURRENT_ASSETS: &str = "CurrentAssets";
458    pub const CASH_CASH_EQUIVALENTS_AND_SHORT_TERM_INVESTMENTS: &str =
459        "CashCashEquivalentsAndShortTermInvestments";
460    pub const CASH_AND_CASH_EQUIVALENTS: &str = "CashAndCashEquivalents";
461    pub const CASH_FINANCIAL: &str = "CashFinancial";
462    pub const RECEIVABLES: &str = "Receivables";
463    pub const ACCOUNTS_RECEIVABLE: &str = "AccountsReceivable";
464    pub const INVENTORY: &str = "Inventory";
465    pub const PREPAID_ASSETS: &str = "PrepaidAssets";
466    pub const OTHER_CURRENT_ASSETS: &str = "OtherCurrentAssets";
467    pub const TOTAL_NON_CURRENT_ASSETS: &str = "TotalNonCurrentAssets";
468    pub const NET_PPE: &str = "NetPPE";
469    pub const GROSS_PPE: &str = "GrossPPE";
470    pub const ACCUMULATED_DEPRECIATION: &str = "AccumulatedDepreciation";
471    pub const GOODWILL: &str = "Goodwill";
472    pub const GOODWILL_AND_OTHER_INTANGIBLE_ASSETS: &str = "GoodwillAndOtherIntangibleAssets";
473    pub const OTHER_INTANGIBLE_ASSETS: &str = "OtherIntangibleAssets";
474    pub const INVESTMENTS_AND_ADVANCES: &str = "InvestmentsAndAdvances";
475    pub const LONG_TERM_EQUITY_INVESTMENT: &str = "LongTermEquityInvestment";
476    pub const OTHER_NON_CURRENT_ASSETS: &str = "OtherNonCurrentAssets";
477    pub const TOTAL_LIABILITIES_NET_MINORITY_INTEREST: &str = "TotalLiabilitiesNetMinorityInterest";
478    pub const CURRENT_LIABILITIES: &str = "CurrentLiabilities";
479    pub const PAYABLES_AND_ACCRUED_EXPENSES: &str = "PayablesAndAccruedExpenses";
480    pub const ACCOUNTS_PAYABLE: &str = "AccountsPayable";
481    pub const CURRENT_DEBT: &str = "CurrentDebt";
482    pub const CURRENT_DEFERRED_REVENUE: &str = "CurrentDeferredRevenue";
483    pub const OTHER_CURRENT_LIABILITIES: &str = "OtherCurrentLiabilities";
484    pub const TOTAL_NON_CURRENT_LIABILITIES_NET_MINORITY_INTEREST: &str =
485        "TotalNonCurrentLiabilitiesNetMinorityInterest";
486    pub const LONG_TERM_DEBT: &str = "LongTermDebt";
487    pub const LONG_TERM_DEBT_AND_CAPITAL_LEASE_OBLIGATION: &str =
488        "LongTermDebtAndCapitalLeaseObligation";
489    pub const NON_CURRENT_DEFERRED_REVENUE: &str = "NonCurrentDeferredRevenue";
490    pub const NON_CURRENT_DEFERRED_TAXES_LIABILITIES: &str = "NonCurrentDeferredTaxesLiabilities";
491    pub const OTHER_NON_CURRENT_LIABILITIES: &str = "OtherNonCurrentLiabilities";
492    pub const STOCKHOLDERS_EQUITY: &str = "StockholdersEquity";
493    pub const COMMON_STOCK_EQUITY: &str = "CommonStockEquity";
494    pub const COMMON_STOCK: &str = "CommonStock";
495    pub const RETAINED_EARNINGS: &str = "RetainedEarnings";
496    pub const ADDITIONAL_PAID_IN_CAPITAL: &str = "AdditionalPaidInCapital";
497    pub const TREASURY_STOCK: &str = "TreasuryStock";
498    pub const TOTAL_EQUITY_GROSS_MINORITY_INTEREST: &str = "TotalEquityGrossMinorityInterest";
499    pub const WORKING_CAPITAL: &str = "WorkingCapital";
500    pub const INVESTED_CAPITAL: &str = "InvestedCapital";
501    pub const TANGIBLE_BOOK_VALUE: &str = "TangibleBookValue";
502    pub const TOTAL_DEBT: &str = "TotalDebt";
503    pub const NET_DEBT: &str = "NetDebt";
504    pub const SHARE_ISSUED: &str = "ShareIssued";
505    pub const ORDINARY_SHARES_NUMBER: &str = "OrdinarySharesNumber";
506
507    // ==================
508    // CASH FLOW STATEMENT (48 fields)
509    // ==================
510    pub const OPERATING_CASH_FLOW: &str = "OperatingCashFlow";
511    pub const CASH_FLOW_FROM_CONTINUING_OPERATING_ACTIVITIES: &str =
512        "CashFlowFromContinuingOperatingActivities";
513    pub const NET_INCOME_FROM_CONTINUING_OPERATIONS: &str = "NetIncomeFromContinuingOperations";
514    pub const DEPRECIATION_AND_AMORTIZATION: &str = "DepreciationAndAmortization";
515    pub const DEFERRED_INCOME_TAX: &str = "DeferredIncomeTax";
516    pub const CHANGE_IN_WORKING_CAPITAL: &str = "ChangeInWorkingCapital";
517    pub const CHANGE_IN_RECEIVABLES: &str = "ChangeInReceivables";
518    pub const CHANGES_IN_ACCOUNT_RECEIVABLES: &str = "ChangesInAccountReceivables";
519    pub const CHANGE_IN_INVENTORY: &str = "ChangeInInventory";
520    pub const CHANGE_IN_ACCOUNT_PAYABLE: &str = "ChangeInAccountPayable";
521    pub const CHANGE_IN_OTHER_WORKING_CAPITAL: &str = "ChangeInOtherWorkingCapital";
522    pub const STOCK_BASED_COMPENSATION: &str = "StockBasedCompensation";
523    pub const OTHER_NON_CASH_ITEMS: &str = "OtherNonCashItems";
524    pub const INVESTING_CASH_FLOW: &str = "InvestingCashFlow";
525    pub const CASH_FLOW_FROM_CONTINUING_INVESTING_ACTIVITIES: &str =
526        "CashFlowFromContinuingInvestingActivities";
527    pub const NET_PPE_PURCHASE_AND_SALE: &str = "NetPPEPurchaseAndSale";
528    pub const PURCHASE_OF_PPE: &str = "PurchaseOfPPE";
529    pub const SALE_OF_PPE: &str = "SaleOfPPE";
530    pub const CAPITAL_EXPENDITURE: &str = "CapitalExpenditure";
531    pub const NET_BUSINESS_PURCHASE_AND_SALE: &str = "NetBusinessPurchaseAndSale";
532    pub const PURCHASE_OF_BUSINESS: &str = "PurchaseOfBusiness";
533    pub const SALE_OF_BUSINESS: &str = "SaleOfBusiness";
534    pub const NET_INVESTMENT_PURCHASE_AND_SALE: &str = "NetInvestmentPurchaseAndSale";
535    pub const PURCHASE_OF_INVESTMENT: &str = "PurchaseOfInvestment";
536    pub const SALE_OF_INVESTMENT: &str = "SaleOfInvestment";
537    pub const NET_OTHER_INVESTING_CHANGES: &str = "NetOtherInvestingChanges";
538    pub const FINANCING_CASH_FLOW: &str = "FinancingCashFlow";
539    pub const CASH_FLOW_FROM_CONTINUING_FINANCING_ACTIVITIES: &str =
540        "CashFlowFromContinuingFinancingActivities";
541    pub const NET_ISSUANCE_PAYMENTS_OF_DEBT: &str = "NetIssuancePaymentsOfDebt";
542    pub const NET_LONG_TERM_DEBT_ISSUANCE: &str = "NetLongTermDebtIssuance";
543    pub const LONG_TERM_DEBT_ISSUANCE: &str = "LongTermDebtIssuance";
544    pub const LONG_TERM_DEBT_PAYMENTS: &str = "LongTermDebtPayments";
545    pub const NET_SHORT_TERM_DEBT_ISSUANCE: &str = "NetShortTermDebtIssuance";
546    pub const NET_COMMON_STOCK_ISSUANCE: &str = "NetCommonStockIssuance";
547    pub const COMMON_STOCK_ISSUANCE: &str = "CommonStockIssuance";
548    pub const COMMON_STOCK_PAYMENTS: &str = "CommonStockPayments";
549    pub const REPURCHASE_OF_CAPITAL_STOCK: &str = "RepurchaseOfCapitalStock";
550    pub const CASH_DIVIDENDS_PAID: &str = "CashDividendsPaid";
551    pub const COMMON_STOCK_DIVIDEND_PAID: &str = "CommonStockDividendPaid";
552    pub const NET_OTHER_FINANCING_CHARGES: &str = "NetOtherFinancingCharges";
553    pub const END_CASH_POSITION: &str = "EndCashPosition";
554    pub const BEGINNING_CASH_POSITION: &str = "BeginningCashPosition";
555    pub const CHANGESIN_CASH: &str = "ChangesinCash";
556    pub const EFFECT_OF_EXCHANGE_RATE_CHANGES: &str = "EffectOfExchangeRateChanges";
557    pub const FREE_CASH_FLOW: &str = "FreeCashFlow";
558    pub const CAPITAL_EXPENDITURE_REPORTED: &str = "CapitalExpenditureReported";
559}
560
561/// Statement types for financial data
562#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
563pub enum StatementType {
564    /// Income statement
565    Income,
566    /// Balance sheet
567    Balance,
568    /// Cash flow statement
569    CashFlow,
570}
571
572impl StatementType {
573    /// Convert statement type to string representation
574    pub fn as_str(&self) -> &'static str {
575        match self {
576            StatementType::Income => "income",
577            StatementType::Balance => "balance",
578            StatementType::CashFlow => "cashflow",
579        }
580    }
581
582    /// Get the list of fields for this statement type
583    ///
584    /// Returns field names without frequency prefix (e.g., "TotalRevenue" not "annualTotalRevenue")
585    pub fn get_fields(&self) -> &'static [&'static str] {
586        match self {
587            StatementType::Income => &INCOME_STATEMENT_FIELDS,
588            StatementType::Balance => &BALANCE_SHEET_FIELDS,
589            StatementType::CashFlow => &CASH_FLOW_FIELDS,
590        }
591    }
592}
593
594impl std::str::FromStr for StatementType {
595    type Err = ();
596
597    /// Case-insensitive; accepts `as_str()`'s canonical form plus the hyphenated
598    /// `-statement`/`-sheet` variants and the `"cash"` shorthand.
599    fn from_str(s: &str) -> Result<Self, Self::Err> {
600        match s.to_lowercase().as_str() {
601            "income" | "income-statement" => Ok(StatementType::Income),
602            "balance" | "balance-sheet" => Ok(StatementType::Balance),
603            "cash" | "cashflow" | "cash-flow" => Ok(StatementType::CashFlow),
604            _ => Err(()),
605        }
606    }
607}
608
609/// Income statement fields (without frequency prefix)
610const INCOME_STATEMENT_FIELDS: [&str; 30] = [
611    fundamental_types::TOTAL_REVENUE,
612    fundamental_types::OPERATING_REVENUE,
613    fundamental_types::COST_OF_REVENUE,
614    fundamental_types::GROSS_PROFIT,
615    fundamental_types::OPERATING_EXPENSE,
616    fundamental_types::SELLING_GENERAL_AND_ADMIN,
617    fundamental_types::RESEARCH_AND_DEVELOPMENT,
618    fundamental_types::OPERATING_INCOME,
619    fundamental_types::NET_INTEREST_INCOME,
620    fundamental_types::INTEREST_EXPENSE,
621    fundamental_types::INTEREST_INCOME,
622    fundamental_types::NET_NON_OPERATING_INTEREST_INCOME_EXPENSE,
623    fundamental_types::OTHER_INCOME_EXPENSE,
624    fundamental_types::PRETAX_INCOME,
625    fundamental_types::TAX_PROVISION,
626    fundamental_types::NET_INCOME_COMMON_STOCKHOLDERS,
627    fundamental_types::NET_INCOME,
628    fundamental_types::DILUTED_EPS,
629    fundamental_types::BASIC_EPS,
630    fundamental_types::DILUTED_AVERAGE_SHARES,
631    fundamental_types::BASIC_AVERAGE_SHARES,
632    fundamental_types::EBIT,
633    fundamental_types::EBITDA,
634    fundamental_types::RECONCILED_COST_OF_REVENUE,
635    fundamental_types::RECONCILED_DEPRECIATION,
636    fundamental_types::NET_INCOME_FROM_CONTINUING_OPERATION_NET_MINORITY_INTEREST,
637    fundamental_types::NORMALIZED_EBITDA,
638    fundamental_types::TOTAL_EXPENSES,
639    fundamental_types::TOTAL_OPERATING_INCOME_AS_REPORTED,
640    fundamental_types::DEPRECIATION_AND_AMORTIZATION,
641];
642
643/// Balance sheet fields (without frequency prefix)
644const BALANCE_SHEET_FIELDS: [&str; 48] = [
645    fundamental_types::TOTAL_ASSETS,
646    fundamental_types::CURRENT_ASSETS,
647    fundamental_types::CASH_CASH_EQUIVALENTS_AND_SHORT_TERM_INVESTMENTS,
648    fundamental_types::CASH_AND_CASH_EQUIVALENTS,
649    fundamental_types::CASH_FINANCIAL,
650    fundamental_types::RECEIVABLES,
651    fundamental_types::ACCOUNTS_RECEIVABLE,
652    fundamental_types::INVENTORY,
653    fundamental_types::PREPAID_ASSETS,
654    fundamental_types::OTHER_CURRENT_ASSETS,
655    fundamental_types::TOTAL_NON_CURRENT_ASSETS,
656    fundamental_types::NET_PPE,
657    fundamental_types::GROSS_PPE,
658    fundamental_types::ACCUMULATED_DEPRECIATION,
659    fundamental_types::GOODWILL,
660    fundamental_types::GOODWILL_AND_OTHER_INTANGIBLE_ASSETS,
661    fundamental_types::OTHER_INTANGIBLE_ASSETS,
662    fundamental_types::INVESTMENTS_AND_ADVANCES,
663    fundamental_types::LONG_TERM_EQUITY_INVESTMENT,
664    fundamental_types::OTHER_NON_CURRENT_ASSETS,
665    fundamental_types::TOTAL_LIABILITIES_NET_MINORITY_INTEREST,
666    fundamental_types::CURRENT_LIABILITIES,
667    fundamental_types::PAYABLES_AND_ACCRUED_EXPENSES,
668    fundamental_types::ACCOUNTS_PAYABLE,
669    fundamental_types::CURRENT_DEBT,
670    fundamental_types::CURRENT_DEFERRED_REVENUE,
671    fundamental_types::OTHER_CURRENT_LIABILITIES,
672    fundamental_types::TOTAL_NON_CURRENT_LIABILITIES_NET_MINORITY_INTEREST,
673    fundamental_types::LONG_TERM_DEBT,
674    fundamental_types::LONG_TERM_DEBT_AND_CAPITAL_LEASE_OBLIGATION,
675    fundamental_types::NON_CURRENT_DEFERRED_REVENUE,
676    fundamental_types::NON_CURRENT_DEFERRED_TAXES_LIABILITIES,
677    fundamental_types::OTHER_NON_CURRENT_LIABILITIES,
678    fundamental_types::STOCKHOLDERS_EQUITY,
679    fundamental_types::COMMON_STOCK_EQUITY,
680    fundamental_types::COMMON_STOCK,
681    fundamental_types::RETAINED_EARNINGS,
682    fundamental_types::ADDITIONAL_PAID_IN_CAPITAL,
683    fundamental_types::TREASURY_STOCK,
684    fundamental_types::TOTAL_EQUITY_GROSS_MINORITY_INTEREST,
685    fundamental_types::WORKING_CAPITAL,
686    fundamental_types::INVESTED_CAPITAL,
687    fundamental_types::TANGIBLE_BOOK_VALUE,
688    fundamental_types::TOTAL_DEBT,
689    fundamental_types::NET_DEBT,
690    fundamental_types::SHARE_ISSUED,
691    fundamental_types::ORDINARY_SHARES_NUMBER,
692    fundamental_types::DEPRECIATION_AND_AMORTIZATION,
693];
694
695/// Cash flow statement fields (without frequency prefix)
696const CASH_FLOW_FIELDS: [&str; 47] = [
697    fundamental_types::OPERATING_CASH_FLOW,
698    fundamental_types::CASH_FLOW_FROM_CONTINUING_OPERATING_ACTIVITIES,
699    fundamental_types::NET_INCOME_FROM_CONTINUING_OPERATIONS,
700    fundamental_types::DEPRECIATION_AND_AMORTIZATION,
701    fundamental_types::DEFERRED_INCOME_TAX,
702    fundamental_types::CHANGE_IN_WORKING_CAPITAL,
703    fundamental_types::CHANGE_IN_RECEIVABLES,
704    fundamental_types::CHANGES_IN_ACCOUNT_RECEIVABLES,
705    fundamental_types::CHANGE_IN_INVENTORY,
706    fundamental_types::CHANGE_IN_ACCOUNT_PAYABLE,
707    fundamental_types::CHANGE_IN_OTHER_WORKING_CAPITAL,
708    fundamental_types::STOCK_BASED_COMPENSATION,
709    fundamental_types::OTHER_NON_CASH_ITEMS,
710    fundamental_types::INVESTING_CASH_FLOW,
711    fundamental_types::CASH_FLOW_FROM_CONTINUING_INVESTING_ACTIVITIES,
712    fundamental_types::NET_PPE_PURCHASE_AND_SALE,
713    fundamental_types::PURCHASE_OF_PPE,
714    fundamental_types::SALE_OF_PPE,
715    fundamental_types::CAPITAL_EXPENDITURE,
716    fundamental_types::NET_BUSINESS_PURCHASE_AND_SALE,
717    fundamental_types::PURCHASE_OF_BUSINESS,
718    fundamental_types::SALE_OF_BUSINESS,
719    fundamental_types::NET_INVESTMENT_PURCHASE_AND_SALE,
720    fundamental_types::PURCHASE_OF_INVESTMENT,
721    fundamental_types::SALE_OF_INVESTMENT,
722    fundamental_types::NET_OTHER_INVESTING_CHANGES,
723    fundamental_types::FINANCING_CASH_FLOW,
724    fundamental_types::CASH_FLOW_FROM_CONTINUING_FINANCING_ACTIVITIES,
725    fundamental_types::NET_ISSUANCE_PAYMENTS_OF_DEBT,
726    fundamental_types::NET_LONG_TERM_DEBT_ISSUANCE,
727    fundamental_types::LONG_TERM_DEBT_ISSUANCE,
728    fundamental_types::LONG_TERM_DEBT_PAYMENTS,
729    fundamental_types::NET_SHORT_TERM_DEBT_ISSUANCE,
730    fundamental_types::NET_COMMON_STOCK_ISSUANCE,
731    fundamental_types::COMMON_STOCK_ISSUANCE,
732    fundamental_types::COMMON_STOCK_PAYMENTS,
733    fundamental_types::REPURCHASE_OF_CAPITAL_STOCK,
734    fundamental_types::CASH_DIVIDENDS_PAID,
735    fundamental_types::COMMON_STOCK_DIVIDEND_PAID,
736    fundamental_types::NET_OTHER_FINANCING_CHARGES,
737    fundamental_types::END_CASH_POSITION,
738    fundamental_types::BEGINNING_CASH_POSITION,
739    fundamental_types::CHANGESIN_CASH,
740    fundamental_types::EFFECT_OF_EXCHANGE_RATE_CHANGES,
741    fundamental_types::FREE_CASH_FLOW,
742    fundamental_types::CAPITAL_EXPENDITURE_REPORTED,
743    fundamental_types::DEPRECIATION_AND_AMORTIZATION,
744];
745
746/// Frequency for financial data (annual or quarterly)
747#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
748pub enum Frequency {
749    /// Annual financial data
750    Annual,
751    /// Quarterly financial data
752    Quarterly,
753}
754
755impl Frequency {
756    /// Convert frequency to string representation
757    pub fn as_str(&self) -> &'static str {
758        match self {
759            Frequency::Annual => "annual",
760            Frequency::Quarterly => "quarterly",
761        }
762    }
763
764    /// Build a fundamental type string with frequency prefix
765    ///
766    /// # Example
767    ///
768    /// ```
769    /// use finance_query::Frequency;
770    ///
771    /// let field = Frequency::Annual.prefix("TotalRevenue");
772    /// assert_eq!(field, "annualTotalRevenue");
773    /// ```
774    pub fn prefix(&self, field: &str) -> String {
775        format!("{}{}", self.as_str(), field)
776    }
777}
778
779impl std::str::FromStr for Frequency {
780    type Err = ();
781
782    /// Case-insensitive; accepts `as_str()`'s canonical form plus common
783    /// shorthands (`"year"`/`"yearly"`, `"quarter"`/`"q"`).
784    fn from_str(s: &str) -> Result<Self, Self::Err> {
785        match s.to_lowercase().as_str() {
786            "annual" | "yearly" | "year" => Ok(Frequency::Annual),
787            "quarterly" | "quarter" | "q" => Ok(Frequency::Quarterly),
788            _ => Err(()),
789        }
790    }
791}
792
793/// Chart intervals
794#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
795pub enum Interval {
796    /// 1 minute
797    #[serde(rename = "1m")]
798    OneMinute,
799    /// 5 minutes
800    #[serde(rename = "5m")]
801    FiveMinutes,
802    /// 15 minutes
803    #[serde(rename = "15m")]
804    FifteenMinutes,
805    /// 30 minutes
806    #[serde(rename = "30m")]
807    ThirtyMinutes,
808    /// 1 hour
809    #[serde(rename = "1h")]
810    OneHour,
811    /// 1 day
812    #[serde(rename = "1d")]
813    OneDay,
814    /// 1 week
815    #[serde(rename = "1wk")]
816    OneWeek,
817    /// 1 month
818    #[serde(rename = "1mo")]
819    OneMonth,
820    /// 3 months
821    #[serde(rename = "3mo")]
822    ThreeMonths,
823}
824
825impl Interval {
826    /// Convert interval to Yahoo Finance API format
827    pub fn as_str(&self) -> &'static str {
828        match self {
829            Interval::OneMinute => "1m",
830            Interval::FiveMinutes => "5m",
831            Interval::FifteenMinutes => "15m",
832            Interval::ThirtyMinutes => "30m",
833            Interval::OneHour => "1h",
834            Interval::OneDay => "1d",
835            Interval::OneWeek => "1wk",
836            Interval::OneMonth => "1mo",
837            Interval::ThreeMonths => "3mo",
838        }
839    }
840}
841
842impl std::fmt::Display for Interval {
843    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
844        f.write_str(self.as_str())
845    }
846}
847
848impl std::str::FromStr for Interval {
849    type Err = ();
850
851    /// Parses the same short codes returned by [`Interval::as_str`] (e.g. `"1d"`,
852    /// `"1wk"`), case-insensitively.
853    fn from_str(s: &str) -> Result<Self, Self::Err> {
854        match s.trim().to_lowercase().as_str() {
855            "1m" => Ok(Interval::OneMinute),
856            "5m" => Ok(Interval::FiveMinutes),
857            "15m" => Ok(Interval::FifteenMinutes),
858            "30m" => Ok(Interval::ThirtyMinutes),
859            "1h" => Ok(Interval::OneHour),
860            "1d" => Ok(Interval::OneDay),
861            "1wk" => Ok(Interval::OneWeek),
862            "1mo" => Ok(Interval::OneMonth),
863            "3mo" => Ok(Interval::ThreeMonths),
864            _ => Err(()),
865        }
866    }
867}
868
869/// Time ranges for chart data
870#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
871pub enum TimeRange {
872    /// 1 day
873    #[serde(rename = "1d")]
874    OneDay,
875    /// 5 days
876    #[serde(rename = "5d")]
877    FiveDays,
878    /// 1 month
879    #[serde(rename = "1mo")]
880    OneMonth,
881    /// 3 months
882    #[serde(rename = "3mo")]
883    ThreeMonths,
884    /// 6 months
885    #[serde(rename = "6mo")]
886    SixMonths,
887    /// 1 year
888    #[serde(rename = "1y")]
889    OneYear,
890    /// 2 years
891    #[serde(rename = "2y")]
892    TwoYears,
893    /// 5 years
894    #[serde(rename = "5y")]
895    FiveYears,
896    /// 10 years
897    #[serde(rename = "10y")]
898    TenYears,
899    /// Year to date
900    #[serde(rename = "ytd")]
901    YearToDate,
902    /// Maximum available
903    #[serde(rename = "max")]
904    Max,
905}
906
907impl TimeRange {
908    /// Convert time range to Yahoo Finance API format
909    pub fn as_str(&self) -> &'static str {
910        match self {
911            TimeRange::OneDay => "1d",
912            TimeRange::FiveDays => "5d",
913            TimeRange::OneMonth => "1mo",
914            TimeRange::ThreeMonths => "3mo",
915            TimeRange::SixMonths => "6mo",
916            TimeRange::OneYear => "1y",
917            TimeRange::TwoYears => "2y",
918            TimeRange::FiveYears => "5y",
919            TimeRange::TenYears => "10y",
920            TimeRange::YearToDate => "ytd",
921            TimeRange::Max => "max",
922        }
923    }
924
925    /// A sensible default candle interval for this range, used by the
926    /// `history(range)` convenience on domain handles: finer granularity for
927    /// short ranges, coarser for long ones.
928    pub fn default_interval(&self) -> Interval {
929        match self {
930            TimeRange::OneDay => Interval::FiveMinutes,
931            TimeRange::FiveDays => Interval::FifteenMinutes,
932            TimeRange::OneMonth
933            | TimeRange::ThreeMonths
934            | TimeRange::SixMonths
935            | TimeRange::OneYear
936            | TimeRange::YearToDate => Interval::OneDay,
937            TimeRange::TwoYears | TimeRange::FiveYears => Interval::OneWeek,
938            TimeRange::TenYears | TimeRange::Max => Interval::OneMonth,
939        }
940    }
941
942    /// Approximate span of this range in seconds.
943    ///
944    /// Calendar approximations: a month is 30 days, a year 365. `YearToDate` is
945    /// approximated as one year and `Max` as a far-future horizon.
946    pub const fn approx_duration_secs(&self) -> i64 {
947        const DAY: i64 = 86_400;
948        match self {
949            TimeRange::OneDay => DAY,
950            TimeRange::FiveDays => 5 * DAY,
951            TimeRange::OneMonth => 30 * DAY,
952            TimeRange::ThreeMonths => 90 * DAY,
953            TimeRange::SixMonths => 180 * DAY,
954            TimeRange::OneYear | TimeRange::YearToDate => 365 * DAY,
955            TimeRange::TwoYears => 730 * DAY,
956            TimeRange::FiveYears => 1_825 * DAY,
957            TimeRange::TenYears => 3_650 * DAY,
958            TimeRange::Max => 36_500 * DAY,
959        }
960    }
961}
962
963impl std::fmt::Display for TimeRange {
964    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
965        f.write_str(self.as_str())
966    }
967}
968
969impl std::str::FromStr for TimeRange {
970    type Err = ();
971
972    /// Parses the same short codes returned by [`TimeRange::as_str`] (e.g. `"3mo"`,
973    /// `"ytd"`), case-insensitively. `"1wk"` is also accepted as an alias for
974    /// [`TimeRange::FiveDays`] (a trading week).
975    fn from_str(s: &str) -> Result<Self, Self::Err> {
976        match s.trim().to_lowercase().as_str() {
977            "1d" => Ok(TimeRange::OneDay),
978            "5d" | "1wk" => Ok(TimeRange::FiveDays),
979            "1mo" => Ok(TimeRange::OneMonth),
980            "3mo" => Ok(TimeRange::ThreeMonths),
981            "6mo" => Ok(TimeRange::SixMonths),
982            "1y" => Ok(TimeRange::OneYear),
983            "2y" => Ok(TimeRange::TwoYears),
984            "5y" => Ok(TimeRange::FiveYears),
985            "10y" => Ok(TimeRange::TenYears),
986            "ytd" => Ok(TimeRange::YearToDate),
987            "max" => Ok(TimeRange::Max),
988            _ => Err(()),
989        }
990    }
991}
992
993/// Supported regions for Yahoo Finance regional APIs
994///
995/// Each region has predefined language and region codes that work together.
996/// Using the Region enum ensures correct lang/region pairing.
997#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
998pub enum Region {
999    /// Argentina (es-AR, AR)
1000    Argentina,
1001    /// Australia (en-AU, AU)
1002    Australia,
1003    /// Brazil (pt-BR, BR)
1004    Brazil,
1005    /// Canada (en-CA, CA)
1006    Canada,
1007    /// China (zh-CN, CN)
1008    China,
1009    /// Denmark (da-DK, DK)
1010    Denmark,
1011    /// Finland (fi-FI, FI)
1012    Finland,
1013    /// France (fr-FR, FR)
1014    France,
1015    /// Germany (de-DE, DE)
1016    Germany,
1017    /// Greece (el-GR, GR)
1018    Greece,
1019    /// Hong Kong (zh-Hant-HK, HK)
1020    HongKong,
1021    /// India (en-IN, IN)
1022    India,
1023    /// Israel (he-IL, IL)
1024    Israel,
1025    /// Italy (it-IT, IT)
1026    Italy,
1027    /// Japan (ja-JP, JP)
1028    Japan,
1029    /// South Korea (ko-KR, KR)
1030    Korea,
1031    /// Malaysia (ms-MY, MY)
1032    Malaysia,
1033    /// Mexico (es-MX, MX)
1034    Mexico,
1035    /// New Zealand (en-NZ, NZ)
1036    NewZealand,
1037    /// Norway (nb-NO, NO)
1038    Norway,
1039    /// Portugal (pt-PT, PT)
1040    Portugal,
1041    /// Qatar (ar-QA, QA)
1042    Qatar,
1043    /// Russia (ru-RU, RU)
1044    Russia,
1045    /// Singapore (en-SG, SG)
1046    Singapore,
1047    /// Spain (es-ES, ES)
1048    Spain,
1049    /// Sweden (sv-SE, SE)
1050    Sweden,
1051    /// Taiwan (zh-TW, TW)
1052    Taiwan,
1053    /// Thailand (th-TH, TH)
1054    Thailand,
1055    /// Turkey (tr-TR, TR)
1056    Turkey,
1057    /// United Kingdom (en-GB, GB)
1058    UnitedKingdom,
1059    /// United States (en-US, US) - Default
1060    #[default]
1061    UnitedStates,
1062    /// Vietnam (vi-VN, VN)
1063    Vietnam,
1064}
1065
1066impl Region {
1067    /// Get the language code for this region
1068    ///
1069    /// # Example
1070    ///
1071    /// ```
1072    /// use finance_query::Region;
1073    ///
1074    /// assert_eq!(Region::France.lang(), "fr-FR");
1075    /// assert_eq!(Region::UnitedStates.lang(), "en-US");
1076    /// ```
1077    pub fn lang(&self) -> &'static str {
1078        match self {
1079            Region::Argentina => "es-AR",
1080            Region::Australia => "en-AU",
1081            Region::Brazil => "pt-BR",
1082            Region::Canada => "en-CA",
1083            Region::China => "zh-CN",
1084            Region::Denmark => "da-DK",
1085            Region::Finland => "fi-FI",
1086            Region::France => "fr-FR",
1087            Region::Germany => "de-DE",
1088            Region::Greece => "el-GR",
1089            Region::HongKong => "zh-Hant-HK",
1090            Region::India => "en-IN",
1091            Region::Israel => "he-IL",
1092            Region::Italy => "it-IT",
1093            Region::Japan => "ja-JP",
1094            Region::Korea => "ko-KR",
1095            Region::Malaysia => "ms-MY",
1096            Region::Mexico => "es-MX",
1097            Region::NewZealand => "en-NZ",
1098            Region::Norway => "nb-NO",
1099            Region::Portugal => "pt-PT",
1100            Region::Qatar => "ar-QA",
1101            Region::Russia => "ru-RU",
1102            Region::Singapore => "en-SG",
1103            Region::Spain => "es-ES",
1104            Region::Sweden => "sv-SE",
1105            Region::Taiwan => "zh-TW",
1106            Region::Thailand => "th-TH",
1107            Region::Turkey => "tr-TR",
1108            Region::UnitedKingdom => "en-GB",
1109            Region::UnitedStates => "en-US",
1110            Region::Vietnam => "vi-VN",
1111        }
1112    }
1113
1114    /// Get the region code for this region
1115    ///
1116    /// # Example
1117    ///
1118    /// ```
1119    /// use finance_query::Region;
1120    ///
1121    /// assert_eq!(Region::France.region(), "FR");
1122    /// assert_eq!(Region::UnitedStates.region(), "US");
1123    /// ```
1124    pub fn region(&self) -> &'static str {
1125        match self {
1126            Region::Argentina => "AR",
1127            Region::Australia => "AU",
1128            Region::Brazil => "BR",
1129            Region::Canada => "CA",
1130            Region::China => "CN",
1131            Region::Denmark => "DK",
1132            Region::Finland => "FI",
1133            Region::France => "FR",
1134            Region::Germany => "DE",
1135            Region::Greece => "GR",
1136            Region::HongKong => "HK",
1137            Region::India => "IN",
1138            Region::Israel => "IL",
1139            Region::Italy => "IT",
1140            Region::Japan => "JP",
1141            Region::Korea => "KR",
1142            Region::Malaysia => "MY",
1143            Region::Mexico => "MX",
1144            Region::NewZealand => "NZ",
1145            Region::Norway => "NO",
1146            Region::Portugal => "PT",
1147            Region::Qatar => "QA",
1148            Region::Russia => "RU",
1149            Region::Singapore => "SG",
1150            Region::Spain => "ES",
1151            Region::Sweden => "SE",
1152            Region::Taiwan => "TW",
1153            Region::Thailand => "TH",
1154            Region::Turkey => "TR",
1155            Region::UnitedKingdom => "GB",
1156            Region::UnitedStates => "US",
1157            Region::Vietnam => "VN",
1158        }
1159    }
1160
1161    /// UTC offset in seconds for the region's primary exchange.
1162    ///
1163    /// Returns the standard-time (non-DST) UTC offset of each country's main
1164    /// exchange. This is used by the backtesting engine to align higher-timeframe
1165    /// resampling bucket boundaries to local calendar weeks and months, preventing
1166    /// APAC and other non-UTC exchanges from having bars mis-bucketed into the
1167    /// prior week due to UTC midnight falling inside their local trading day.
1168    ///
1169    /// # Note
1170    ///
1171    /// DST transitions are not modelled. For exchanges in regions with DST
1172    /// (e.g. NYSE, LSE) the boundary shift is at most ±1 hour and affects only
1173    /// the transition candles. This is a deliberate simplification — exact DST
1174    /// handling would require a timezone database dependency.
1175    pub const fn utc_offset_secs(&self) -> i64 {
1176        match self {
1177            // UTC-5 (NYSE/TSX winter)
1178            Region::UnitedStates | Region::Canada => -18_000,
1179            // UTC-6 (BMV — Mexico abolished DST for most of the country in 2022)
1180            Region::Mexico => -21_600,
1181            // UTC-3 (BYMA / B3 winter)
1182            Region::Argentina | Region::Brazil => -10_800,
1183            // UTC+0 (LSE / Euronext Lisbon)
1184            Region::UnitedKingdom | Region::Portugal => 0,
1185            // UTC+1 (Euronext Paris/Amsterdam/Milan/Madrid, Oslo, Stockholm, Copenhagen, Helsinki)
1186            Region::France
1187            | Region::Germany
1188            | Region::Italy
1189            | Region::Spain
1190            | Region::Norway
1191            | Region::Sweden
1192            | Region::Denmark
1193            | Region::Finland => 3_600,
1194            // UTC+2 (Athens, Tel Aviv, Moscow — note Russia stays UTC+3 year-round)
1195            Region::Greece | Region::Israel => 7_200,
1196            // UTC+3 (MOEX — no DST since 2014; Qatar/AST has no DST)
1197            Region::Turkey | Region::Russia | Region::Qatar => 10_800,
1198            // UTC+5:30 (BSE/NSE — India has no DST)
1199            Region::India => 19_800,
1200            // UTC+7 (SET Bangkok, HSX Hanoi)
1201            Region::Thailand | Region::Vietnam => 25_200,
1202            // UTC+8 (SSE/SZSE, HKEX, SGX, Bursa Malaysia, TWSE)
1203            Region::China
1204            | Region::HongKong
1205            | Region::Singapore
1206            | Region::Malaysia
1207            | Region::Taiwan => 28_800,
1208            // UTC+9 (TSE, KRX — neither observes DST)
1209            Region::Japan | Region::Korea => 32_400,
1210            // UTC+10 (ASX — AEST winter)
1211            Region::Australia => 36_000,
1212            // UTC+12 (NZX — NZST winter)
1213            Region::NewZealand => 43_200,
1214        }
1215    }
1216}
1217
1218impl std::str::FromStr for Region {
1219    type Err = ();
1220
1221    fn from_str(s: &str) -> Result<Self, Self::Err> {
1222        match s.to_uppercase().as_str() {
1223            "AR" => Ok(Region::Argentina),
1224            "AU" => Ok(Region::Australia),
1225            "BR" => Ok(Region::Brazil),
1226            "CA" => Ok(Region::Canada),
1227            "CN" => Ok(Region::China),
1228            "DK" => Ok(Region::Denmark),
1229            "FI" => Ok(Region::Finland),
1230            "FR" => Ok(Region::France),
1231            "DE" => Ok(Region::Germany),
1232            "GR" => Ok(Region::Greece),
1233            "HK" => Ok(Region::HongKong),
1234            "IN" => Ok(Region::India),
1235            "IL" => Ok(Region::Israel),
1236            "IT" => Ok(Region::Italy),
1237            "JP" => Ok(Region::Japan),
1238            "KR" => Ok(Region::Korea),
1239            "MY" => Ok(Region::Malaysia),
1240            "MX" => Ok(Region::Mexico),
1241            "NZ" => Ok(Region::NewZealand),
1242            "NO" => Ok(Region::Norway),
1243            "PT" => Ok(Region::Portugal),
1244            "QA" => Ok(Region::Qatar),
1245            "RU" => Ok(Region::Russia),
1246            "SG" => Ok(Region::Singapore),
1247            "ES" => Ok(Region::Spain),
1248            "SE" => Ok(Region::Sweden),
1249            "TW" => Ok(Region::Taiwan),
1250            "TH" => Ok(Region::Thailand),
1251            "TR" => Ok(Region::Turkey),
1252            "GB" | "UK" => Ok(Region::UnitedKingdom),
1253            "US" => Ok(Region::UnitedStates),
1254            "VN" => Ok(Region::Vietnam),
1255            _ => Err(()),
1256        }
1257    }
1258}
1259
1260impl From<Region> for String {
1261    /// Returns the lowercase two-letter country code used by the Yahoo Finance screener
1262    /// (e.g. `"us"`, `"gb"`).
1263    fn from(v: Region) -> Self {
1264        v.region().to_lowercase()
1265    }
1266}
1267
1268/// Value format for API responses
1269///
1270/// Controls how `FormattedValue<T>` fields are serialized in responses.
1271/// This allows API consumers to choose between raw numeric values,
1272/// human-readable formatted strings, or both.
1273#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1274pub enum ValueFormat {
1275    /// Return only raw numeric values (e.g., `123.45`) - default
1276    /// Best for programmatic use, calculations, charts
1277    #[default]
1278    Raw,
1279    /// Return only formatted strings (e.g., `"$123.45"`, `"1.2B"`)
1280    /// Best for display purposes
1281    Pretty,
1282    /// Return both raw and formatted values
1283    /// Returns the full `{raw, fmt, longFmt}` object
1284    Both,
1285}
1286
1287impl std::str::FromStr for ValueFormat {
1288    type Err = ();
1289
1290    fn from_str(s: &str) -> Result<Self, Self::Err> {
1291        match s.to_lowercase().as_str() {
1292            "raw" => Ok(ValueFormat::Raw),
1293            "pretty" | "fmt" => Ok(ValueFormat::Pretty),
1294            "both" | "full" => Ok(ValueFormat::Both),
1295            _ => Err(()),
1296        }
1297    }
1298}
1299
1300impl ValueFormat {
1301    /// Parse from string (case-insensitive), returns None on invalid input
1302    pub fn parse(s: &str) -> Option<Self> {
1303        s.parse().ok()
1304    }
1305
1306    /// Convert to string representation
1307    pub fn as_str(&self) -> &'static str {
1308        match self {
1309            ValueFormat::Raw => "raw",
1310            ValueFormat::Pretty => "pretty",
1311            ValueFormat::Both => "both",
1312        }
1313    }
1314
1315    /// Transform a JSON value based on this format
1316    ///
1317    /// Recursively processes the JSON, detecting FormattedValue objects
1318    /// (objects with `raw` key and optionally `fmt`/`longFmt`) and
1319    /// transforming them according to the format setting.
1320    ///
1321    /// # Example
1322    ///
1323    /// ```
1324    /// use finance_query::ValueFormat;
1325    /// use serde_json::json;
1326    ///
1327    /// let data = json!({"price": {"raw": 123.45, "fmt": "$123.45"}});
1328    ///
1329    /// // Raw format extracts just the raw value (default)
1330    /// let raw = ValueFormat::default().transform(data.clone());
1331    /// assert_eq!(raw, json!({"price": 123.45}));
1332    ///
1333    /// // Pretty extracts just the formatted string
1334    /// let pretty = ValueFormat::Pretty.transform(data.clone());
1335    /// assert_eq!(pretty, json!({"price": "$123.45"}));
1336    ///
1337    /// // Both keeps the full object
1338    /// let both = ValueFormat::Both.transform(data);
1339    /// assert_eq!(both, json!({"price": {"raw": 123.45, "fmt": "$123.45"}}));
1340    /// ```
1341    pub fn transform(&self, value: serde_json::Value) -> serde_json::Value {
1342        match self {
1343            ValueFormat::Both => value, // No transformation needed
1344            _ => self.transform_recursive(value),
1345        }
1346    }
1347
1348    fn transform_recursive(&self, value: serde_json::Value) -> serde_json::Value {
1349        use serde_json::Value;
1350
1351        match value {
1352            Value::Object(map) => {
1353                // Check if this looks like a FormattedValue (has 'raw' key)
1354                if self.is_formatted_value(&map) {
1355                    return self.extract_value(&map);
1356                }
1357
1358                // Otherwise, recursively transform all values
1359                let transformed: serde_json::Map<String, Value> = map
1360                    .into_iter()
1361                    .map(|(k, v)| (k, self.transform_recursive(v)))
1362                    .collect();
1363                Value::Object(transformed)
1364            }
1365            Value::Array(arr) => Value::Array(
1366                arr.into_iter()
1367                    .map(|v| self.transform_recursive(v))
1368                    .collect(),
1369            ),
1370            // Primitives pass through unchanged
1371            other => other,
1372        }
1373    }
1374
1375    /// Check if an object looks like a FormattedValue
1376    fn is_formatted_value(&self, map: &serde_json::Map<String, serde_json::Value>) -> bool {
1377        // Must have 'raw' key (can be null)
1378        // May have 'fmt' and/or 'longFmt'
1379        // Should not have many other keys (FormattedValue only has these 3)
1380        if !map.contains_key("raw") {
1381            return false;
1382        }
1383
1384        let known_keys = ["raw", "fmt", "longFmt"];
1385        let unknown_keys = map
1386            .keys()
1387            .filter(|k| !known_keys.contains(&k.as_str()))
1388            .count();
1389
1390        // If there are unknown keys, it's probably not a FormattedValue
1391        unknown_keys == 0
1392    }
1393
1394    /// Extract the appropriate value based on format
1395    fn extract_value(&self, map: &serde_json::Map<String, serde_json::Value>) -> serde_json::Value {
1396        match self {
1397            ValueFormat::Raw => {
1398                // Return raw value directly (or null if not present)
1399                map.get("raw").cloned().unwrap_or(serde_json::Value::Null)
1400            }
1401            ValueFormat::Pretty => {
1402                // Prefer fmt, fall back to longFmt, then null
1403                map.get("fmt")
1404                    .or_else(|| map.get("longFmt"))
1405                    .cloned()
1406                    .unwrap_or(serde_json::Value::Null)
1407            }
1408            ValueFormat::Both => {
1409                // Keep as-is (shouldn't reach here, but handle anyway)
1410                serde_json::Value::Object(map.clone())
1411            }
1412        }
1413    }
1414}
1415
1416/// Typed industry identifiers shared between the industry endpoint and screener queries.
1417///
1418/// Use with [`finance::industry()`](crate::finance::industry) for the data endpoint, and with
1419/// [`EquityField::Industry`](crate::EquityField::Industry) +
1420/// [`ScreenerFieldExt::eq_str`](crate::ScreenerFieldExt::eq_str) for screener filtering.
1421///
1422/// - `as_slug()` → lowercase hyphenated key for `finance::industry()` (e.g. `"semiconductors"`)
1423/// - `From<Industry> for String` → screener display name (e.g. `"Semiconductors"`)
1424///
1425/// # Example
1426///
1427/// ```no_run
1428/// use finance_query::{finance, Industry, EquityField, EquityScreenerQuery, ScreenerFieldExt};
1429///
1430/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1431/// // Industry endpoint
1432/// let data = finance::industry(Industry::Semiconductors).await?;
1433///
1434/// // Screener filter
1435/// let query = EquityScreenerQuery::new()
1436///     .add_condition(EquityField::Industry.eq_str(Industry::Semiconductors));
1437/// # Ok(())
1438/// # }
1439/// ```
1440pub mod industries {
1441    /// Typed industry identifier for the industry endpoint and custom screener queries.
1442    ///
1443    /// See the module-level doc for usage.
1444    #[non_exhaustive]
1445    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1446    pub enum Industry {
1447        // ── Agriculture / Raw Materials ──────────────────────────────────────
1448        /// Agricultural inputs, fertilizers, and crop chemicals
1449        AgriculturalInputs,
1450        /// Aluminum production and processing companies
1451        Aluminum,
1452        /// Coal mining and processing companies
1453        Coal,
1454        /// Copper mining and processing companies
1455        Copper,
1456        /// Farm products including grains, livestock, and produce
1457        FarmProducts,
1458        /// Forest products including timber and paper pulp
1459        ForestProducts,
1460        /// Gold mining and royalty companies
1461        Gold,
1462        /// Lumber and wood production companies
1463        LumberAndWoodProduction,
1464        /// Other industrial metals and mining (zinc, nickel, etc.)
1465        OtherIndustrialMetalsAndMining,
1466        /// Other precious metals and mining (platinum, palladium, etc.)
1467        OtherPreciousMetalsAndMining,
1468        /// Silver mining and streaming companies
1469        Silver,
1470        /// Steel production and processing companies
1471        Steel,
1472        /// Thermal coal mining for electricity generation
1473        ThermalCoal,
1474        /// Uranium mining companies
1475        Uranium,
1476        // ── Consumer ─────────────────────────────────────────────────────────
1477        /// Clothing and apparel manufacturing companies
1478        ApparelManufacturing,
1479        /// Clothing and apparel retail chains
1480        ApparelRetail,
1481        /// Automotive and truck dealerships
1482        AutoAndTruckDealerships,
1483        /// Automobile manufacturers and assemblers
1484        AutoManufacturers,
1485        /// Automotive parts manufacturers and distributors
1486        AutoParts,
1487        /// Beer brewing and distribution companies
1488        BeveragesBrewers,
1489        /// Non-alcoholic beverages including soft drinks and juices
1490        BeveragesNonAlcoholic,
1491        /// Wineries, distilleries, and spirits producers
1492        BeveragesWineriesAndDistilleries,
1493        /// Candy, chocolate, and confectionery makers
1494        Confectioners,
1495        /// Traditional department store retailers
1496        DepartmentStores,
1497        /// Discount and value retail stores
1498        DiscountStores,
1499        /// Electronic gaming software and multimedia entertainment
1500        ElectronicGamingAndMultimedia,
1501        /// Wholesale food distribution companies
1502        FoodDistribution,
1503        /// Footwear, handbags, and fashion accessories
1504        FootwearAndAccessories,
1505        /// Furniture, fixtures, and household appliances
1506        FurnishingsFixturesAndAppliances,
1507        /// Casinos, online gambling, and gaming operators
1508        Gambling,
1509        /// Supermarkets and grocery retail chains
1510        GroceryStores,
1511        /// Home improvement retail stores
1512        HomeImprovementRetail,
1513        /// Household cleaning products and personal care items
1514        HouseholdAndPersonalProducts,
1515        /// Online retail and e-commerce marketplaces
1516        InternetRetail,
1517        /// Leisure, recreation, and entertainment companies
1518        Leisure,
1519        /// Hotels and lodging companies
1520        Lodging,
1521        /// Luxury goods, fashion, and premium consumer brands
1522        LuxuryGoods,
1523        /// Packaged and processed food manufacturers
1524        PackagedFoods,
1525        /// Personal care, laundry, and household services
1526        PersonalServices,
1527        /// Home builders and residential construction
1528        ResidentialConstruction,
1529        /// Resorts, integrated casinos, and hotel-casinos
1530        ResortsAndCasinos,
1531        /// Restaurant chains and food service operators
1532        Restaurants,
1533        /// Specialty retail stores (pets, books, electronics, etc.)
1534        SpecialtyRetail,
1535        /// Textile and fabric manufacturers
1536        TextileManufacturing,
1537        /// Tobacco product manufacturers
1538        Tobacco,
1539        /// Travel agencies, booking platforms, and tour operators
1540        TravelServices,
1541        // ── Energy ───────────────────────────────────────────────────────────
1542        /// Oil and gas contract drilling services
1543        OilAndGasDrilling,
1544        /// Oil and gas exploration and production companies
1545        OilAndGasEAndP,
1546        /// Oil field equipment, services, and engineering
1547        OilAndGasEquipmentAndServices,
1548        /// Vertically integrated oil and gas majors
1549        OilAndGasIntegrated,
1550        /// Oil and gas pipelines, storage, and transportation
1551        OilAndGasMidstream,
1552        /// Oil refining, wholesale fuel, and marketing
1553        OilAndGasRefiningAndMarketing,
1554        /// Solar panel manufacturers and solar energy producers
1555        Solar,
1556        // ── Financial Services ───────────────────────────────────────────────
1557        /// Asset managers, fund sponsors, and investment advisors
1558        AssetManagement,
1559        /// Large diversified national and international banks
1560        BanksDiversified,
1561        /// Regional and community banks
1562        BanksRegional,
1563        /// Investment banks, brokers, and financial exchanges
1564        CapitalMarkets,
1565        /// Credit card issuers and consumer credit services
1566        CreditServices,
1567        /// Financial data, analytics, and stock exchange operators
1568        FinancialDataAndStockExchanges,
1569        /// Insurance brokers and agencies
1570        InsuranceBrokers,
1571        /// Diversified multi-line insurance companies
1572        InsuranceDiversified,
1573        /// Life insurance and annuity providers
1574        InsuranceLife,
1575        /// Property and casualty insurance companies
1576        InsurancePropertyAndCasualty,
1577        /// Reinsurance companies
1578        InsuranceReinsurance,
1579        /// Specialty insurance lines (title, mortgage, etc.)
1580        InsuranceSpecialty,
1581        /// Mortgage banking and loan origination
1582        MortgageFinance,
1583        /// Blank-check and shell holding companies
1584        ShellCompanies,
1585        // ── Healthcare ───────────────────────────────────────────────────────
1586        /// Biotechnology drug development companies
1587        Biotechnology,
1588        /// Medical diagnostics labs and clinical research
1589        DiagnosticsAndResearch,
1590        /// Large branded pharmaceutical manufacturers
1591        DrugManufacturersGeneral,
1592        /// Specialty drugs, generics, and biosimilars
1593        DrugManufacturersSpecialtyAndGeneric,
1594        /// Healthcare IT, EHR, and health data services
1595        HealthInformationServices,
1596        /// Managed care organizations and health insurers
1597        HealthcarePlans,
1598        /// Hospitals, clinics, and outpatient care facilities
1599        MedicalCareFacilities,
1600        /// Medical device manufacturers (implants, diagnostics equipment)
1601        MedicalDevices,
1602        /// Medical product wholesalers and distributors
1603        MedicalDistribution,
1604        /// Surgical instruments, disposables, and medical supplies
1605        MedicalInstrumentsAndSupplies,
1606        /// Retail pharmacies and drug store chains
1607        PharmaceuticalRetailers,
1608        // ── Industrials ──────────────────────────────────────────────────────
1609        /// Defense contractors, aircraft, and space systems
1610        AerospaceAndDefense,
1611        /// Construction aggregates, cement, and building materials
1612        BuildingMaterials,
1613        /// HVAC, plumbing, windows, and building equipment
1614        BuildingProductsAndEquipment,
1615        /// Office supplies, commercial equipment, and printers
1616        BusinessEquipmentAndSupplies,
1617        /// Specialty chemical manufacturing for industrial use
1618        ChemicalManufacturing,
1619        /// Diversified commodity chemicals producers
1620        Chemicals,
1621        /// Diversified industrial holding companies
1622        Conglomerates,
1623        /// Management consulting and professional advisory services
1624        ConsultingServices,
1625        /// Electrical components, motors, and power equipment
1626        ElectricalEquipmentAndParts,
1627        /// Civil engineering, construction, and infrastructure projects
1628        EngineeringAndConstruction,
1629        /// Agricultural equipment and heavy construction machinery
1630        FarmAndHeavyConstructionMachinery,
1631        /// Industrial goods wholesalers and distributors
1632        IndustrialDistribution,
1633        /// Toll roads, airports, and infrastructure operators
1634        InfrastructureOperations,
1635        /// Third-party logistics and supply chain management
1636        IntegratedFreightAndLogistics,
1637        /// Diversified manufacturers across multiple industrial segments
1638        ManufacturingDiversified,
1639        /// Port operators and marine terminal services
1640        MarinePortsAndServices,
1641        /// Bulk cargo and tanker shipping companies
1642        MarineShipping,
1643        /// Custom metal fabrication and machined components
1644        MetalFabrication,
1645        /// Paper, packaging, and pulp product manufacturers
1646        PaperAndPaperProducts,
1647        /// Environmental controls, water treatment, and remediation
1648        PollutionAndTreatmentControls,
1649        /// Rail freight carriers and passenger rail operators
1650        Railroads,
1651        /// Equipment rental, leasing, and fleet management
1652        RentalAndLeasingServices,
1653        /// Security systems, guards, and monitoring services
1654        SecurityAndProtectionServices,
1655        /// Outsourced business services and BPO companies
1656        SpecialtyBusinessServices,
1657        /// High-value specialty chemicals and advanced materials
1658        SpecialtyChemicals,
1659        /// Specialized industrial machinery and equipment makers
1660        SpecialtyIndustrialMachinery,
1661        /// Staffing agencies and employment service providers
1662        StaffingAndEmploymentServices,
1663        /// Hand tools, power tools, and hardware accessories
1664        ToolsAndAccessories,
1665        /// Freight trucking and less-than-truckload carriers
1666        Trucking,
1667        /// Waste collection, recycling, and disposal services
1668        WasteManagement,
1669        // ── Real Estate ──────────────────────────────────────────────────────
1670        /// Real estate developers and homebuilders
1671        RealEstateDevelopment,
1672        /// Diversified real estate companies with mixed portfolios
1673        RealEstateDiversified,
1674        /// Real estate brokers, agents, and property managers
1675        RealEstateServices,
1676        /// Diversified REITs across multiple property types
1677        ReitDiversified,
1678        /// Healthcare and senior living facility REITs
1679        ReitHealthcareFacilities,
1680        /// Hotel and motel property REITs
1681        ReitHotelAndMotel,
1682        /// Industrial, warehouse, and logistics property REITs
1683        ReitIndustrial,
1684        /// Mortgage REITs investing in real estate debt
1685        ReitMortgage,
1686        /// Office building and commercial property REITs
1687        ReitOffice,
1688        /// Apartment, multifamily, and residential property REITs
1689        ReitResidential,
1690        /// Shopping center and retail property REITs
1691        ReitRetail,
1692        /// Specialty REITs (data centers, cell towers, self-storage)
1693        ReitSpecialty,
1694        // ── Technology ───────────────────────────────────────────────────────
1695        /// Networking hardware, routers, and communication equipment
1696        CommunicationEquipment,
1697        /// PCs, servers, and computer hardware manufacturers
1698        ComputerHardware,
1699        /// Smartphones, TVs, and consumer electronic devices
1700        ConsumerElectronics,
1701        /// Data analytics, business intelligence, and AI platforms
1702        DataAnalytics,
1703        /// Passive electronic components and circuit boards
1704        ElectronicComponents,
1705        /// Distributors of electronics and computer products
1706        ElectronicsAndComputerDistribution,
1707        /// Value-added resellers and software/hardware distributors
1708        HardwareAndSoftwareDistribution,
1709        /// IT services, outsourcing, and technology consulting
1710        InformationTechnologyServices,
1711        /// Online media, search engines, and digital content platforms
1712        InternetContentAndInformation,
1713        /// Precision instruments, sensors, and test equipment
1714        ScientificAndTechnicalInstruments,
1715        /// Semiconductor manufacturing equipment and materials
1716        SemiconductorEquipmentAndMaterials,
1717        /// Integrated circuit and chip designers and manufacturers
1718        Semiconductors,
1719        /// Business application software companies
1720        SoftwareApplication,
1721        /// Operating systems, middleware, and infrastructure software
1722        SoftwareInfrastructure,
1723        // ── Communication Services ───────────────────────────────────────────
1724        /// Television, radio, and broadcast media companies
1725        Broadcasting,
1726        /// Film studios, streaming, and live entertainment
1727        Entertainment,
1728        /// Book, magazine, newspaper, and digital media publishers
1729        Publishing,
1730        /// Wireless carriers and wireline telephone companies
1731        TelecomServices,
1732        // ── Utilities ────────────────────────────────────────────────────────
1733        /// Multi-utility companies serving electricity, gas, and water
1734        UtilitiesDiversified,
1735        /// Independent power producers and energy traders
1736        UtilitiesIndependentPowerProducers,
1737        /// Regulated electric utility companies
1738        UtilitiesRegulatedElectric,
1739        /// Regulated natural gas distribution utilities
1740        UtilitiesRegulatedGas,
1741        /// Regulated water and wastewater utilities
1742        UtilitiesRegulatedWater,
1743        /// Renewable energy generation companies (wind, solar, hydro)
1744        UtilitiesRenewable,
1745        // ── Special ──────────────────────────────────────────────────────────
1746        /// Closed-end funds investing in debt instruments
1747        ClosedEndFundDebt,
1748        /// Closed-end funds investing in equities
1749        ClosedEndFundEquity,
1750        /// Closed-end funds investing in foreign securities
1751        ClosedEndFundForeign,
1752        /// Exchange-traded fund products
1753        ExchangeTradedFund,
1754    }
1755
1756    impl Industry {
1757        /// Returns the lowercase hyphenated slug used by `finance::industry()`.
1758        ///
1759        /// # Example
1760        ///
1761        /// ```
1762        /// use finance_query::Industry;
1763        /// assert_eq!(Industry::Semiconductors.as_slug(), "semiconductors");
1764        /// assert_eq!(Industry::SoftwareApplication.as_slug(), "software-application");
1765        /// ```
1766        pub fn as_slug(self) -> &'static str {
1767            match self {
1768                Industry::AgriculturalInputs => "agricultural-inputs",
1769                Industry::Aluminum => "aluminum",
1770                Industry::Coal => "coal",
1771                Industry::Copper => "copper",
1772                Industry::FarmProducts => "farm-products",
1773                Industry::ForestProducts => "forest-products",
1774                Industry::Gold => "gold",
1775                Industry::LumberAndWoodProduction => "lumber-wood-production",
1776                Industry::OtherIndustrialMetalsAndMining => "other-industrial-metals-mining",
1777                Industry::OtherPreciousMetalsAndMining => "other-precious-metals-mining",
1778                Industry::Silver => "silver",
1779                Industry::Steel => "steel",
1780                Industry::ThermalCoal => "thermal-coal",
1781                Industry::Uranium => "uranium",
1782                Industry::ApparelManufacturing => "apparel-manufacturing",
1783                Industry::ApparelRetail => "apparel-retail",
1784                Industry::AutoAndTruckDealerships => "auto-truck-dealerships",
1785                Industry::AutoManufacturers => "auto-manufacturers",
1786                Industry::AutoParts => "auto-parts",
1787                Industry::BeveragesBrewers => "beverages-brewers",
1788                Industry::BeveragesNonAlcoholic => "beverages-non-alcoholic",
1789                Industry::BeveragesWineriesAndDistilleries => "beverages-wineries-distilleries",
1790                Industry::Confectioners => "confectioners",
1791                Industry::DepartmentStores => "department-stores",
1792                Industry::DiscountStores => "discount-stores",
1793                Industry::ElectronicGamingAndMultimedia => "electronic-gaming-multimedia",
1794                Industry::FoodDistribution => "food-distribution",
1795                Industry::FootwearAndAccessories => "footwear-accessories",
1796                Industry::FurnishingsFixturesAndAppliances => "furnishings-fixtures-appliances",
1797                Industry::Gambling => "gambling",
1798                Industry::GroceryStores => "grocery-stores",
1799                Industry::HomeImprovementRetail => "home-improvement-retail",
1800                Industry::HouseholdAndPersonalProducts => "household-personal-products",
1801                Industry::InternetRetail => "internet-retail",
1802                Industry::Leisure => "leisure",
1803                Industry::Lodging => "lodging",
1804                Industry::LuxuryGoods => "luxury-goods",
1805                Industry::PackagedFoods => "packaged-foods",
1806                Industry::PersonalServices => "personal-services",
1807                Industry::ResidentialConstruction => "residential-construction",
1808                Industry::ResortsAndCasinos => "resorts-casinos",
1809                Industry::Restaurants => "restaurants",
1810                Industry::SpecialtyRetail => "specialty-retail",
1811                Industry::TextileManufacturing => "textile-manufacturing",
1812                Industry::Tobacco => "tobacco",
1813                Industry::TravelServices => "travel-services",
1814                Industry::OilAndGasDrilling => "oil-gas-drilling",
1815                Industry::OilAndGasEAndP => "oil-gas-ep",
1816                Industry::OilAndGasEquipmentAndServices => "oil-gas-equipment-services",
1817                Industry::OilAndGasIntegrated => "oil-gas-integrated",
1818                Industry::OilAndGasMidstream => "oil-gas-midstream",
1819                Industry::OilAndGasRefiningAndMarketing => "oil-gas-refining-marketing",
1820                Industry::Solar => "solar",
1821                Industry::AssetManagement => "asset-management",
1822                Industry::BanksDiversified => "banks-diversified",
1823                Industry::BanksRegional => "banks-regional",
1824                Industry::CapitalMarkets => "capital-markets",
1825                Industry::CreditServices => "credit-services",
1826                Industry::FinancialDataAndStockExchanges => "financial-data-stock-exchanges",
1827                Industry::InsuranceBrokers => "insurance-brokers",
1828                Industry::InsuranceDiversified => "insurance-diversified",
1829                Industry::InsuranceLife => "insurance-life",
1830                Industry::InsurancePropertyAndCasualty => "insurance-property-casualty",
1831                Industry::InsuranceReinsurance => "insurance-reinsurance",
1832                Industry::InsuranceSpecialty => "insurance-specialty",
1833                Industry::MortgageFinance => "mortgage-finance",
1834                Industry::ShellCompanies => "shell-companies",
1835                Industry::Biotechnology => "biotechnology",
1836                Industry::DiagnosticsAndResearch => "diagnostics-research",
1837                Industry::DrugManufacturersGeneral => "drug-manufacturers-general",
1838                Industry::DrugManufacturersSpecialtyAndGeneric => {
1839                    "drug-manufacturers-specialty-generic"
1840                }
1841                Industry::HealthInformationServices => "health-information-services",
1842                Industry::HealthcarePlans => "healthcare-plans",
1843                Industry::MedicalCareFacilities => "medical-care-facilities",
1844                Industry::MedicalDevices => "medical-devices",
1845                Industry::MedicalDistribution => "medical-distribution",
1846                Industry::MedicalInstrumentsAndSupplies => "medical-instruments-supplies",
1847                Industry::PharmaceuticalRetailers => "pharmaceutical-retailers",
1848                Industry::AerospaceAndDefense => "aerospace-defense",
1849                Industry::BuildingMaterials => "building-materials",
1850                Industry::BuildingProductsAndEquipment => "building-products-equipment",
1851                Industry::BusinessEquipmentAndSupplies => "business-equipment-supplies",
1852                Industry::ChemicalManufacturing => "chemical-manufacturing",
1853                Industry::Chemicals => "chemicals",
1854                Industry::Conglomerates => "conglomerates",
1855                Industry::ConsultingServices => "consulting-services",
1856                Industry::ElectricalEquipmentAndParts => "electrical-equipment-parts",
1857                Industry::EngineeringAndConstruction => "engineering-construction",
1858                Industry::FarmAndHeavyConstructionMachinery => "farm-heavy-construction-machinery",
1859                Industry::IndustrialDistribution => "industrial-distribution",
1860                Industry::InfrastructureOperations => "infrastructure-operations",
1861                Industry::IntegratedFreightAndLogistics => "integrated-freight-logistics",
1862                Industry::ManufacturingDiversified => "manufacturing-diversified",
1863                Industry::MarinePortsAndServices => "marine-ports-services",
1864                Industry::MarineShipping => "marine-shipping",
1865                Industry::MetalFabrication => "metal-fabrication",
1866                Industry::PaperAndPaperProducts => "paper-paper-products",
1867                Industry::PollutionAndTreatmentControls => "pollution-treatment-controls",
1868                Industry::Railroads => "railroads",
1869                Industry::RentalAndLeasingServices => "rental-leasing-services",
1870                Industry::SecurityAndProtectionServices => "security-protection-services",
1871                Industry::SpecialtyBusinessServices => "specialty-business-services",
1872                Industry::SpecialtyChemicals => "specialty-chemicals",
1873                Industry::SpecialtyIndustrialMachinery => "specialty-industrial-machinery",
1874                Industry::StaffingAndEmploymentServices => "staffing-employment-services",
1875                Industry::ToolsAndAccessories => "tools-accessories",
1876                Industry::Trucking => "trucking",
1877                Industry::WasteManagement => "waste-management",
1878                Industry::RealEstateDevelopment => "real-estate-development",
1879                Industry::RealEstateDiversified => "real-estate-diversified",
1880                Industry::RealEstateServices => "real-estate-services",
1881                Industry::ReitDiversified => "reit-diversified",
1882                Industry::ReitHealthcareFacilities => "reit-healthcare-facilities",
1883                Industry::ReitHotelAndMotel => "reit-hotel-motel",
1884                Industry::ReitIndustrial => "reit-industrial",
1885                Industry::ReitMortgage => "reit-mortgage",
1886                Industry::ReitOffice => "reit-office",
1887                Industry::ReitResidential => "reit-residential",
1888                Industry::ReitRetail => "reit-retail",
1889                Industry::ReitSpecialty => "reit-specialty",
1890                Industry::CommunicationEquipment => "communication-equipment",
1891                Industry::ComputerHardware => "computer-hardware",
1892                Industry::ConsumerElectronics => "consumer-electronics",
1893                Industry::DataAnalytics => "data-analytics",
1894                Industry::ElectronicComponents => "electronic-components",
1895                Industry::ElectronicsAndComputerDistribution => "electronics-computer-distribution",
1896                Industry::HardwareAndSoftwareDistribution => "hardware-software-distribution",
1897                Industry::InformationTechnologyServices => "information-technology-services",
1898                Industry::InternetContentAndInformation => "internet-content-information",
1899                Industry::ScientificAndTechnicalInstruments => "scientific-technical-instruments",
1900                Industry::SemiconductorEquipmentAndMaterials => "semiconductor-equipment-materials",
1901                Industry::Semiconductors => "semiconductors",
1902                Industry::SoftwareApplication => "software-application",
1903                Industry::SoftwareInfrastructure => "software-infrastructure",
1904                Industry::Broadcasting => "broadcasting",
1905                Industry::Entertainment => "entertainment",
1906                Industry::Publishing => "publishing",
1907                Industry::TelecomServices => "telecom-services",
1908                Industry::UtilitiesDiversified => "utilities-diversified",
1909                Industry::UtilitiesIndependentPowerProducers => {
1910                    "utilities-independent-power-producers"
1911                }
1912                Industry::UtilitiesRegulatedElectric => "utilities-regulated-electric",
1913                Industry::UtilitiesRegulatedGas => "utilities-regulated-gas",
1914                Industry::UtilitiesRegulatedWater => "utilities-regulated-water",
1915                Industry::UtilitiesRenewable => "utilities-renewable",
1916                Industry::ClosedEndFundDebt => "closed-end-fund-debt",
1917                Industry::ClosedEndFundEquity => "closed-end-fund-equity",
1918                Industry::ClosedEndFundForeign => "closed-end-fund-foreign",
1919                Industry::ExchangeTradedFund => "exchange-traded-fund",
1920            }
1921        }
1922
1923        /// Returns the display name used by the Yahoo Finance screener.
1924        ///
1925        /// # Example
1926        ///
1927        /// ```
1928        /// use finance_query::Industry;
1929        /// assert_eq!(Industry::Semiconductors.screener_value(), "Semiconductors");
1930        /// assert_eq!(Industry::OilAndGasDrilling.screener_value(), "Oil & Gas Drilling");
1931        /// ```
1932        pub fn screener_value(self) -> &'static str {
1933            match self {
1934                Industry::AgriculturalInputs => "Agricultural Inputs",
1935                Industry::Aluminum => "Aluminum",
1936                Industry::Coal => "Coal",
1937                Industry::Copper => "Copper",
1938                Industry::FarmProducts => "Farm Products",
1939                Industry::ForestProducts => "Forest Products",
1940                Industry::Gold => "Gold",
1941                Industry::LumberAndWoodProduction => "Lumber & Wood Production",
1942                Industry::OtherIndustrialMetalsAndMining => "Other Industrial Metals & Mining",
1943                Industry::OtherPreciousMetalsAndMining => "Other Precious Metals & Mining",
1944                Industry::Silver => "Silver",
1945                Industry::Steel => "Steel",
1946                Industry::ThermalCoal => "Thermal Coal",
1947                Industry::Uranium => "Uranium",
1948                Industry::ApparelManufacturing => "Apparel Manufacturing",
1949                Industry::ApparelRetail => "Apparel Retail",
1950                Industry::AutoAndTruckDealerships => "Auto & Truck Dealerships",
1951                Industry::AutoManufacturers => "Auto Manufacturers",
1952                Industry::AutoParts => "Auto Parts",
1953                Industry::BeveragesBrewers => "Beverages - Brewers",
1954                Industry::BeveragesNonAlcoholic => "Beverages - Non-Alcoholic",
1955                Industry::BeveragesWineriesAndDistilleries => "Beverages - Wineries & Distilleries",
1956                Industry::Confectioners => "Confectioners",
1957                Industry::DepartmentStores => "Department Stores",
1958                Industry::DiscountStores => "Discount Stores",
1959                Industry::ElectronicGamingAndMultimedia => "Electronic Gaming & Multimedia",
1960                Industry::FoodDistribution => "Food Distribution",
1961                Industry::FootwearAndAccessories => "Footwear & Accessories",
1962                Industry::FurnishingsFixturesAndAppliances => "Furnishings, Fixtures & Appliances",
1963                Industry::Gambling => "Gambling",
1964                Industry::GroceryStores => "Grocery Stores",
1965                Industry::HomeImprovementRetail => "Home Improvement Retail",
1966                Industry::HouseholdAndPersonalProducts => "Household & Personal Products",
1967                Industry::InternetRetail => "Internet Retail",
1968                Industry::Leisure => "Leisure",
1969                Industry::Lodging => "Lodging",
1970                Industry::LuxuryGoods => "Luxury Goods",
1971                Industry::PackagedFoods => "Packaged Foods",
1972                Industry::PersonalServices => "Personal Services",
1973                Industry::ResidentialConstruction => "Residential Construction",
1974                Industry::ResortsAndCasinos => "Resorts & Casinos",
1975                Industry::Restaurants => "Restaurants",
1976                Industry::SpecialtyRetail => "Specialty Retail",
1977                Industry::TextileManufacturing => "Textile Manufacturing",
1978                Industry::Tobacco => "Tobacco",
1979                Industry::TravelServices => "Travel Services",
1980                Industry::OilAndGasDrilling => "Oil & Gas Drilling",
1981                Industry::OilAndGasEAndP => "Oil & Gas E&P",
1982                Industry::OilAndGasEquipmentAndServices => "Oil & Gas Equipment & Services",
1983                Industry::OilAndGasIntegrated => "Oil & Gas Integrated",
1984                Industry::OilAndGasMidstream => "Oil & Gas Midstream",
1985                Industry::OilAndGasRefiningAndMarketing => "Oil & Gas Refining & Marketing",
1986                Industry::Solar => "Solar",
1987                Industry::AssetManagement => "Asset Management",
1988                Industry::BanksDiversified => "Banks - Diversified",
1989                Industry::BanksRegional => "Banks - Regional",
1990                Industry::CapitalMarkets => "Capital Markets",
1991                Industry::CreditServices => "Credit Services",
1992                Industry::FinancialDataAndStockExchanges => "Financial Data & Stock Exchanges",
1993                Industry::InsuranceBrokers => "Insurance Brokers",
1994                Industry::InsuranceDiversified => "Insurance - Diversified",
1995                Industry::InsuranceLife => "Insurance - Life",
1996                Industry::InsurancePropertyAndCasualty => "Insurance - Property & Casualty",
1997                Industry::InsuranceReinsurance => "Insurance - Reinsurance",
1998                Industry::InsuranceSpecialty => "Insurance - Specialty",
1999                Industry::MortgageFinance => "Mortgage Finance",
2000                Industry::ShellCompanies => "Shell Companies",
2001                Industry::Biotechnology => "Biotechnology",
2002                Industry::DiagnosticsAndResearch => "Diagnostics & Research",
2003                Industry::DrugManufacturersGeneral => "Drug Manufacturers - General",
2004                Industry::DrugManufacturersSpecialtyAndGeneric => {
2005                    "Drug Manufacturers - Specialty & Generic"
2006                }
2007                Industry::HealthInformationServices => "Health Information Services",
2008                Industry::HealthcarePlans => "Healthcare Plans",
2009                Industry::MedicalCareFacilities => "Medical Care Facilities",
2010                Industry::MedicalDevices => "Medical Devices",
2011                Industry::MedicalDistribution => "Medical Distribution",
2012                Industry::MedicalInstrumentsAndSupplies => "Medical Instruments & Supplies",
2013                Industry::PharmaceuticalRetailers => "Pharmaceutical Retailers",
2014                Industry::AerospaceAndDefense => "Aerospace & Defense",
2015                Industry::BuildingMaterials => "Building Materials",
2016                Industry::BuildingProductsAndEquipment => "Building Products & Equipment",
2017                Industry::BusinessEquipmentAndSupplies => "Business Equipment & Supplies",
2018                Industry::ChemicalManufacturing => "Chemical Manufacturing",
2019                Industry::Chemicals => "Chemicals",
2020                Industry::Conglomerates => "Conglomerates",
2021                Industry::ConsultingServices => "Consulting Services",
2022                Industry::ElectricalEquipmentAndParts => "Electrical Equipment & Parts",
2023                Industry::EngineeringAndConstruction => "Engineering & Construction",
2024                Industry::FarmAndHeavyConstructionMachinery => {
2025                    "Farm & Heavy Construction Machinery"
2026                }
2027                Industry::IndustrialDistribution => "Industrial Distribution",
2028                Industry::InfrastructureOperations => "Infrastructure Operations",
2029                Industry::IntegratedFreightAndLogistics => "Integrated Freight & Logistics",
2030                Industry::ManufacturingDiversified => "Manufacturing - Diversified",
2031                Industry::MarinePortsAndServices => "Marine Ports & Services",
2032                Industry::MarineShipping => "Marine Shipping",
2033                Industry::MetalFabrication => "Metal Fabrication",
2034                Industry::PaperAndPaperProducts => "Paper & Paper Products",
2035                Industry::PollutionAndTreatmentControls => "Pollution & Treatment Controls",
2036                Industry::Railroads => "Railroads",
2037                Industry::RentalAndLeasingServices => "Rental & Leasing Services",
2038                Industry::SecurityAndProtectionServices => "Security & Protection Services",
2039                Industry::SpecialtyBusinessServices => "Specialty Business Services",
2040                Industry::SpecialtyChemicals => "Specialty Chemicals",
2041                Industry::SpecialtyIndustrialMachinery => "Specialty Industrial Machinery",
2042                Industry::StaffingAndEmploymentServices => "Staffing & Employment Services",
2043                Industry::ToolsAndAccessories => "Tools & Accessories",
2044                Industry::Trucking => "Trucking",
2045                Industry::WasteManagement => "Waste Management",
2046                Industry::RealEstateDevelopment => "Real Estate - Development",
2047                Industry::RealEstateDiversified => "Real Estate - Diversified",
2048                Industry::RealEstateServices => "Real Estate Services",
2049                Industry::ReitDiversified => "REIT - Diversified",
2050                Industry::ReitHealthcareFacilities => "REIT - Healthcare Facilities",
2051                Industry::ReitHotelAndMotel => "REIT - Hotel & Motel",
2052                Industry::ReitIndustrial => "REIT - Industrial",
2053                Industry::ReitMortgage => "REIT - Mortgage",
2054                Industry::ReitOffice => "REIT - Office",
2055                Industry::ReitResidential => "REIT - Residential",
2056                Industry::ReitRetail => "REIT - Retail",
2057                Industry::ReitSpecialty => "REIT - Specialty",
2058                Industry::CommunicationEquipment => "Communication Equipment",
2059                Industry::ComputerHardware => "Computer Hardware",
2060                Industry::ConsumerElectronics => "Consumer Electronics",
2061                Industry::DataAnalytics => "Data Analytics",
2062                Industry::ElectronicComponents => "Electronic Components",
2063                Industry::ElectronicsAndComputerDistribution => {
2064                    "Electronics & Computer Distribution"
2065                }
2066                Industry::HardwareAndSoftwareDistribution => "Hardware & Software Distribution",
2067                Industry::InformationTechnologyServices => "Information Technology Services",
2068                Industry::InternetContentAndInformation => "Internet Content & Information",
2069                Industry::ScientificAndTechnicalInstruments => "Scientific & Technical Instruments",
2070                Industry::SemiconductorEquipmentAndMaterials => {
2071                    "Semiconductor Equipment & Materials"
2072                }
2073                Industry::Semiconductors => "Semiconductors",
2074                Industry::SoftwareApplication => "Software - Application",
2075                Industry::SoftwareInfrastructure => "Software - Infrastructure",
2076                Industry::Broadcasting => "Broadcasting",
2077                Industry::Entertainment => "Entertainment",
2078                Industry::Publishing => "Publishing",
2079                Industry::TelecomServices => "Telecom Services",
2080                Industry::UtilitiesDiversified => "Utilities - Diversified",
2081                Industry::UtilitiesIndependentPowerProducers => {
2082                    "Utilities - Independent Power Producers"
2083                }
2084                Industry::UtilitiesRegulatedElectric => "Utilities - Regulated Electric",
2085                Industry::UtilitiesRegulatedGas => "Utilities - Regulated Gas",
2086                Industry::UtilitiesRegulatedWater => "Utilities - Regulated Water",
2087                Industry::UtilitiesRenewable => "Utilities - Renewable",
2088                Industry::ClosedEndFundDebt => "Closed-End Fund - Debt",
2089                Industry::ClosedEndFundEquity => "Closed-End Fund - Equity",
2090                Industry::ClosedEndFundForeign => "Closed-End Fund - Foreign",
2091                Industry::ExchangeTradedFund => "Exchange Traded Fund",
2092            }
2093        }
2094    }
2095
2096    impl AsRef<str> for Industry {
2097        /// Returns the slug, enabling `finance::industry(Industry::Semiconductors)`.
2098        fn as_ref(&self) -> &str {
2099            self.as_slug()
2100        }
2101    }
2102
2103    impl From<Industry> for String {
2104        /// Returns the screener display name, enabling `EquityField::Industry.eq_str(Industry::Semiconductors)`.
2105        fn from(v: Industry) -> Self {
2106            v.screener_value().to_string()
2107        }
2108    }
2109}
2110
2111/// Typed exchange codes for screener queries.
2112///
2113/// Use with [`EquityField::Exchange`](crate::EquityField::Exchange) or
2114/// [`FundField::Exchange`](crate::FundField::Exchange) and
2115/// [`ScreenerFieldExt::eq_str`](crate::ScreenerFieldExt::eq_str).
2116///
2117/// For mutual fund queries use [`ExchangeCode::Nas`].
2118///
2119/// # Example
2120///
2121/// ```
2122/// use finance_query::{EquityField, EquityScreenerQuery, ScreenerFieldExt, ExchangeCode};
2123///
2124/// let query = EquityScreenerQuery::new()
2125///     .add_condition(EquityField::Exchange.eq_str(ExchangeCode::Nms));
2126/// ```
2127pub mod exchange_codes {
2128    /// Typed exchange code for screener queries.
2129    #[non_exhaustive]
2130    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2131    pub enum ExchangeCode {
2132        // ── US Equity ─────────────────────────────────────────────────────────
2133        /// NYSE American / AMEX ("ASE")
2134        Ase,
2135        /// OTC Bulletin Board ("BTS")
2136        Bts,
2137        /// NASDAQ Capital Market ("NCM")
2138        Ncm,
2139        /// NASDAQ Global Market ("NGM")
2140        Ngm,
2141        /// NASDAQ Global Select Market ("NMS") — primary NASDAQ tier
2142        Nms,
2143        /// New York Stock Exchange ("NYQ")
2144        Nyq,
2145        /// NYSE Arca ("PCX")
2146        Pcx,
2147        /// OTC Pink Sheets / OTC Markets ("PNK")
2148        Pnk,
2149        // ── US Funds ──────────────────────────────────────────────────────────
2150        /// NASDAQ — used for US mutual funds ("NAS")
2151        Nas,
2152        // ── International ─────────────────────────────────────────────────────
2153        /// Australian Securities Exchange ("ASX")
2154        Asx,
2155        /// Bombay Stock Exchange ("BSE")
2156        Bse,
2157        /// Hong Kong Stock Exchange ("HKG")
2158        Hkg,
2159        /// Korea Exchange ("KRX")
2160        Krx,
2161        /// London Stock Exchange ("LSE")
2162        Lse,
2163        /// National Stock Exchange of India ("NSI")
2164        Nsi,
2165        /// Shanghai Stock Exchange ("SHH")
2166        Shh,
2167        /// Shenzhen Stock Exchange ("SHZ")
2168        Shz,
2169        /// Tokyo Stock Exchange ("TYO")
2170        Tyo,
2171        /// Toronto Stock Exchange ("TOR")
2172        Tor,
2173        /// XETRA / Deutsche Börse ("GER")
2174        Ger,
2175    }
2176
2177    impl ExchangeCode {
2178        /// Returns the exchange code string used by Yahoo Finance.
2179        pub fn as_str(self) -> &'static str {
2180            match self {
2181                ExchangeCode::Ase => "ASE",
2182                ExchangeCode::Bts => "BTS",
2183                ExchangeCode::Ncm => "NCM",
2184                ExchangeCode::Ngm => "NGM",
2185                ExchangeCode::Nms => "NMS",
2186                ExchangeCode::Nyq => "NYQ",
2187                ExchangeCode::Pcx => "PCX",
2188                ExchangeCode::Pnk => "PNK",
2189                ExchangeCode::Nas => "NAS",
2190                ExchangeCode::Asx => "ASX",
2191                ExchangeCode::Bse => "BSE",
2192                ExchangeCode::Hkg => "HKG",
2193                ExchangeCode::Krx => "KRX",
2194                ExchangeCode::Lse => "LSE",
2195                ExchangeCode::Nsi => "NSI",
2196                ExchangeCode::Shh => "SHH",
2197                ExchangeCode::Shz => "SHZ",
2198                ExchangeCode::Tyo => "TYO",
2199                ExchangeCode::Tor => "TOR",
2200                ExchangeCode::Ger => "GER",
2201            }
2202        }
2203    }
2204
2205    impl From<ExchangeCode> for String {
2206        fn from(v: ExchangeCode) -> Self {
2207            v.as_str().to_string()
2208        }
2209    }
2210}
2211
2212#[cfg(test)]
2213mod tests {
2214    use super::*;
2215
2216    #[test]
2217    fn test_interval_as_str() {
2218        assert_eq!(Interval::OneMinute.as_str(), "1m");
2219        assert_eq!(Interval::FiveMinutes.as_str(), "5m");
2220        assert_eq!(Interval::OneDay.as_str(), "1d");
2221        assert_eq!(Interval::OneWeek.as_str(), "1wk");
2222    }
2223
2224    #[test]
2225    fn test_time_range_as_str() {
2226        assert_eq!(TimeRange::OneDay.as_str(), "1d");
2227        assert_eq!(TimeRange::OneMonth.as_str(), "1mo");
2228        assert_eq!(TimeRange::OneYear.as_str(), "1y");
2229        assert_eq!(TimeRange::Max.as_str(), "max");
2230    }
2231
2232    #[test]
2233    fn test_statement_type_from_str_round_trips_as_str() {
2234        for statement in [
2235            StatementType::Income,
2236            StatementType::Balance,
2237            StatementType::CashFlow,
2238        ] {
2239            assert_eq!(statement.as_str().parse(), Ok(statement));
2240        }
2241        assert_eq!("INCOME-STATEMENT".parse(), Ok(StatementType::Income));
2242        assert_eq!("balance-sheet".parse(), Ok(StatementType::Balance));
2243        assert_eq!("cash".parse(), Ok(StatementType::CashFlow));
2244        assert_eq!("bogus".parse::<StatementType>(), Err(()));
2245    }
2246
2247    #[test]
2248    fn test_frequency_from_str_round_trips_as_str() {
2249        for frequency in [Frequency::Annual, Frequency::Quarterly] {
2250            assert_eq!(frequency.as_str().parse(), Ok(frequency));
2251        }
2252        assert_eq!("YEARLY".parse(), Ok(Frequency::Annual));
2253        assert_eq!("q".parse(), Ok(Frequency::Quarterly));
2254        assert_eq!("bogus".parse::<Frequency>(), Err(()));
2255    }
2256
2257    #[test]
2258    fn test_interval_from_str_round_trips_as_str() {
2259        for interval in [
2260            Interval::OneMinute,
2261            Interval::FiveMinutes,
2262            Interval::FifteenMinutes,
2263            Interval::ThirtyMinutes,
2264            Interval::OneHour,
2265            Interval::OneDay,
2266            Interval::OneWeek,
2267            Interval::OneMonth,
2268            Interval::ThreeMonths,
2269        ] {
2270            assert_eq!(interval.as_str().parse(), Ok(interval));
2271        }
2272        assert_eq!("1D".parse(), Ok(Interval::OneDay));
2273        assert_eq!(" 1d ".parse(), Ok(Interval::OneDay));
2274        assert_eq!("bogus".parse::<Interval>(), Err(()));
2275    }
2276
2277    #[test]
2278    fn test_time_range_from_str_round_trips_as_str() {
2279        for range in [
2280            TimeRange::OneDay,
2281            TimeRange::FiveDays,
2282            TimeRange::OneMonth,
2283            TimeRange::ThreeMonths,
2284            TimeRange::SixMonths,
2285            TimeRange::OneYear,
2286            TimeRange::TwoYears,
2287            TimeRange::FiveYears,
2288            TimeRange::TenYears,
2289            TimeRange::YearToDate,
2290            TimeRange::Max,
2291        ] {
2292            assert_eq!(range.as_str().parse(), Ok(range));
2293        }
2294        assert_eq!("1wk".parse(), Ok(TimeRange::FiveDays));
2295        assert_eq!("YTD".parse(), Ok(TimeRange::YearToDate));
2296        assert_eq!("bogus".parse::<TimeRange>(), Err(()));
2297    }
2298
2299    #[test]
2300    fn test_default_interval_buckets() {
2301        // Intraday ranges → sub-day candles; long ranges → coarse candles.
2302        assert_eq!(TimeRange::OneDay.default_interval(), Interval::FiveMinutes);
2303        assert_eq!(
2304            TimeRange::FiveDays.default_interval(),
2305            Interval::FifteenMinutes
2306        );
2307        assert_eq!(TimeRange::OneMonth.default_interval(), Interval::OneDay);
2308        assert_eq!(TimeRange::OneYear.default_interval(), Interval::OneDay);
2309        assert_eq!(TimeRange::TwoYears.default_interval(), Interval::OneWeek);
2310        assert_eq!(TimeRange::Max.default_interval(), Interval::OneMonth);
2311    }
2312}