2.2 KB
raw
//! S&P 500 membership, used to restrict the dashboard's market-movers lists to
//! recognizable large-cap names rather than the whole-market micro-caps Yahoo's
//! predefined screeners return (a user request: "market movers has a lot of
//! companies I've literally never heard of").
//!
//! The constituent list lives in `universe/sp500.txt` (one ticker per line, in
//! Yahoo symbology so `BRK.B` is `BRK-B`; `#` comments and blank lines allowed)
//! and is embedded at compile time, so there is no runtime file IO and it ships
//! inside the binary. Refresh by editing that file and redeploying (the file's
//! header documents the one-liner that regenerates it).
use std::collections::HashSet;
use std::sync::LazyLock;
/// The raw constituent list, embedded at build time.
const SP500_RAW: &str = include_str!("../universe/sp500.txt");
/// The membership set, parsed once on first use. Tickers are uppercased so the
/// lookup is case-insensitive; blank lines and `#` comments are skipped.
static SP500: LazyLock<HashSet<String>> = LazyLock::new(|| {
SP500_RAW
.lines()
.map(str::trim)
.filter(|l| !l.is_empty() && !l.starts_with('#'))
.map(str::to_uppercase)
.collect()
});
/// Whether `ticker` is an S&P 500 constituent (case-insensitive).
pub fn is_member(ticker: &str) -> bool {
SP500.contains(&ticker.to_uppercase())
}
/// How many constituents are loaded — for a boot log / sanity check.
pub fn count() -> usize {
SP500.len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn loads_a_full_roster() {
// The S&P 500 hovers around 500-505 names; guard against an empty or
// truncated embed (e.g. a botched refresh).
assert!(count() >= 490, "expected ~500 constituents, got {}", count());
}
#[test]
fn known_members_and_non_members() {
assert!(is_member("AAPL"));
assert!(is_member("aapl"), "lookup is case-insensitive");
assert!(is_member("BRK-B"), "dotted tickers stored in Yahoo dash form");
assert!(is_member("JPM"));
// A real ticker that is not in the index, and obvious junk.
assert!(!is_member("SPCX"), "a delisted micro-cap fund is not a member");
assert!(!is_member("NOTATICKER"));
}
}