#[derive(Debug, Clone)]
pub struct YahooFinanceUrls {
pub rest_base: &'static str,
pub rest_base_alt: &'static str, pub ws_base: Option<&'static str>,
}
impl Default for YahooFinanceUrls {
fn default() -> Self {
Self {
rest_base: "https://query1.finance.yahoo.com",
rest_base_alt: "https://query2.finance.yahoo.com",
ws_base: Some("wss://streamer.finance.yahoo.com/"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum YahooFinanceEndpoint {
Quote, Chart, QuoteSummary, MarketSummary, Spark,
DownloadHistory,
Options,
Search, Lookup, ScreenerPredefined, ScreenerCustom, Trending, RecommendationsBySymbol,
FundamentalsTimeSeries,
GetCrumb, }
impl YahooFinanceEndpoint {
pub fn path(&self) -> &'static str {
match self {
Self::Quote => "/v7/finance/quote",
Self::Chart => "/v8/finance/chart",
Self::QuoteSummary => "/v10/finance/quoteSummary",
Self::MarketSummary => "/v6/finance/quote/marketSummary",
Self::Spark => "/v1/finance/spark",
Self::DownloadHistory => "/v7/finance/download",
Self::Options => "/v7/finance/options",
Self::Search => "/v1/finance/search",
Self::Lookup => "/v1/finance/lookup",
Self::ScreenerPredefined => "/v1/finance/screener/predefined",
Self::ScreenerCustom => "/v1/finance/screener",
Self::Trending => "/v1/finance/trending",
Self::RecommendationsBySymbol => "/v1/finance/recommendationsBySymbol",
Self::FundamentalsTimeSeries => "/ws/fundamentals-timeseries/v1/finance/timeseries",
Self::GetCrumb => "/v1/test/getcrumb",
}
}
pub fn url(&self, base_url: &str, symbol: Option<&str>) -> String {
let path = self.path();
match (self, symbol) {
(Self::Chart, Some(sym)) => format!("{}{}/{}", base_url, path, sym),
(Self::QuoteSummary, Some(sym)) => format!("{}{}/{}", base_url, path, sym),
(Self::DownloadHistory, Some(sym)) => format!("{}{}/{}", base_url, path, sym),
(Self::Options, Some(sym)) => format!("{}{}/{}", base_url, path, sym),
(Self::Trending, Some(region)) => format!("{}{}/{}", base_url, path, region),
(Self::RecommendationsBySymbol, Some(sym)) => format!("{}{}/{}", base_url, path, sym),
(Self::FundamentalsTimeSeries, Some(sym)) => format!("{}{}/{}", base_url, path, sym),
_ => format!("{}{}", base_url, path),
}
}
pub fn method(&self) -> &'static str {
match self {
Self::ScreenerCustom => "POST",
_ => "GET",
}
}
pub fn requires_crumb(&self) -> bool {
matches!(self, Self::DownloadHistory)
}
}
pub fn format_symbol(base: &str, quote: &str) -> String {
if quote.is_empty() || quote.eq_ignore_ascii_case("USD") {
if is_crypto_symbol(base) {
format!("{}-{}", base.to_uppercase(), quote.to_uppercase())
} else {
base.to_uppercase()
}
} else {
format!("{}-{}", base.to_uppercase(), quote.to_uppercase())
}
}
fn is_crypto_symbol(symbol: &str) -> bool {
matches!(
symbol.to_uppercase().as_str(),
"BTC" | "ETH" | "BNB" | "XRP" | "ADA" | "SOL" | "DOGE" |
"DOT" | "MATIC" | "AVAX" | "LINK" | "UNI" | "ATOM" | "LTC"
)
}
#[allow(dead_code)]
pub fn parse_symbol(yahoo_symbol: &str) -> (String, String) {
if yahoo_symbol.starts_with('^') {
return (yahoo_symbol.to_string(), String::new());
}
if yahoo_symbol.ends_with("=X") {
let pair = yahoo_symbol.trim_end_matches("=X");
if pair.len() >= 6 {
return (pair[0..3].to_string(), pair[3..6].to_string());
}
}
if yahoo_symbol.ends_with("=F") {
return (yahoo_symbol.to_string(), String::new());
}
if let Some(pos) = yahoo_symbol.find('-') {
let base = yahoo_symbol[0..pos].to_string();
let quote = yahoo_symbol[pos + 1..].to_string();
return (base, quote);
}
(yahoo_symbol.to_string(), "USD".to_string())
}
#[allow(dead_code)]
pub mod quote_summary_modules {
pub const ASSET_PROFILE: &str = "assetProfile";
pub const BALANCE_SHEET_HISTORY: &str = "balanceSheetHistory";
pub const BALANCE_SHEET_HISTORY_QUARTERLY: &str = "balanceSheetHistoryQuarterly";
pub const CALENDAR_EVENTS: &str = "calendarEvents";
pub const CASHFLOW_STATEMENT_HISTORY: &str = "cashflowStatementHistory";
pub const CASHFLOW_STATEMENT_HISTORY_QUARTERLY: &str = "cashflowStatementHistoryQuarterly";
pub const DEFAULT_KEY_STATISTICS: &str = "defaultKeyStatistics";
pub const EARNINGS: &str = "earnings";
pub const EARNINGS_HISTORY: &str = "earningsHistory";
pub const EARNINGS_TREND: &str = "earningsTrend";
pub const ESG_SCORES: &str = "esgScores";
pub const FINANCIAL_DATA: &str = "financialData";
pub const FUND_OWNERSHIP: &str = "fundOwnership";
pub const FUND_PERFORMANCE: &str = "fundPerformance";
pub const FUND_PROFILE: &str = "fundProfile";
pub const INCOME_STATEMENT_HISTORY: &str = "incomeStatementHistory";
pub const INCOME_STATEMENT_HISTORY_QUARTERLY: &str = "incomeStatementHistoryQuarterly";
pub const INDEX_TREND: &str = "indexTrend";
pub const INDUSTRY_TREND: &str = "industryTrend";
pub const INSIDER_HOLDERS: &str = "insiderHolders";
pub const INSIDER_TRANSACTIONS: &str = "insiderTransactions";
pub const INSTITUTION_OWNERSHIP: &str = "institutionOwnership";
pub const MAJOR_DIRECT_HOLDERS: &str = "majorDirectHolders";
pub const MAJOR_HOLDERS_BREAKDOWN: &str = "majorHoldersBreakdown";
pub const NET_SHARE_PURCHASE_ACTIVITY: &str = "netSharePurchaseActivity";
pub const PRICE: &str = "price";
pub const QUOTE_TYPE: &str = "quoteType";
pub const RECOMMENDATION_TREND: &str = "recommendationTrend";
pub const SEC_FILINGS: &str = "secFilings";
pub const SECTOR_TREND: &str = "sectorTrend";
pub const SUMMARY_DETAIL: &str = "summaryDetail";
pub const SUMMARY_PROFILE: &str = "summaryProfile";
pub const SYMBOL: &str = "symbol";
pub const TOP_HOLDINGS: &str = "topHoldings";
pub const UPGRADE_DOWNGRADE_HISTORY: &str = "upgradeDowngradeHistory";
}
pub fn map_chart_interval(interval: &str) -> &'static str {
match interval {
"1m" => "1m",
"2m" => "2m",
"3m" => "5m", "5m" => "5m",
"15m" => "15m",
"30m" => "30m",
"1h" | "60m" => "1h",
"90m" => "90m",
"2h" => "1h", "4h" => "1h", "1d" => "1d",
"5d" => "5d",
"1w" | "1wk" => "1wk",
"1M" | "1mo" => "1mo",
"3M" | "3mo" => "3mo",
_ => "1d", }
}