#[cfg(feature = "debug")]
extern crate serde_json_path_to_error as serde_json;
use std::{sync::Arc, time::Duration};
pub use quotes::decimal::Decimal;
use reqwest::{Client, ClientBuilder, Proxy};
pub use time;
use time::OffsetDateTime;
mod quotes;
pub mod rate_limiter;
mod search_result;
pub mod yahoo_error;
pub mod builder;
pub mod presets;
pub mod circuit_breaker;
pub mod connection_pool;
pub mod enterprise;
pub mod error_categories;
pub mod observability;
pub mod request_deduplication;
pub mod response_cache;
pub mod retry;
pub mod health;
pub mod metrics;
pub mod tracing;
#[cfg(feature = "config-management")]
pub mod config;
#[cfg(feature = "config-management")]
pub mod runtime_config;
#[cfg(feature = "performance-cache")]
pub mod advanced_cache;
#[cfg(feature = "performance-pool")]
pub mod connection_pool_advanced;
#[cfg(feature = "performance-rate-limit")]
pub mod intelligent_rate_limit;
#[cfg(feature = "performance-optimization")]
pub mod performance_optimization;
#[cfg(feature = "websocket-streaming")]
pub mod websocket;
pub mod batch;
pub mod validation;
pub mod market_hours;
pub mod screener;
#[cfg(feature = "phase7")]
pub mod security;
#[cfg(feature = "phase7")]
pub mod audit;
#[cfg(feature = "phase7")]
pub mod fallback;
pub mod runtime;
#[cfg(feature = "phase9")]
pub mod analytics;
pub mod export;
#[cfg(feature = "phase5-compression")]
pub mod compression;
#[cfg(feature = "phase5-http2")]
pub mod http2;
#[cfg(feature = "phase5-limits")]
pub mod limits;
#[cfg(feature = "phase5-shutdown")]
pub mod shutdown;
pub use builder::YahooConnectorBuilder as EnterpriseYahooConnectorBuilder;
pub use circuit_breaker::{
CircuitBreaker, CircuitBreakerConfig, CircuitBreakerStats, CircuitState,
};
pub use connection_pool::{ConnectionPool, ConnectionPoolConfig, ConnectionStats};
pub use enterprise::{
EnterpriseConfig, EnterpriseHealthStatus, EnterpriseMetrics, EnterpriseYahooConnector,
};
pub use error_categories::{ErrorCategorizer, ErrorCategory, ErrorInfo};
pub use observability::{
HealthCheck, HealthStatus, LibraryMetrics, ObservabilityConfig, ObservabilityManager,
RequestContext,
};
pub use presets::{PresetConfig, PresetFormat, PresetManager};
pub use quotes::{
AdjClose, AssetProfile, CapitalGain, CurrentTradingPeriod, DefaultKeyStatistics, Dividend,
ExtendedQuoteSummary, FinancialData, FinancialEvent, PeriodInfo, Quote, QuoteBlock, QuoteList,
QuoteType, Split, SummaryDetail, TradingPeriods, YChart, YMetaData, YQuoteBlock, YQuoteSummary,
YResponse, YSummaryData,
};
pub use rate_limiter::{RateLimitConfig, RateLimitError, RateLimitStatus, RateLimiter};
pub use request_deduplication::{DeduplicationConfig, DeduplicationStats, RequestDeduplicator};
pub use response_cache::{CacheStats, ResponseCache, ResponseCacheConfig};
pub use retry::{RetryConfig, RetryPolicy, RetryStats};
pub use search_result::{
YNewsItem, YOptionChain, YOptionChainData, YOptionChainResult, YOptionContract, YOptionDetails,
YQuote, YQuoteItem, YQuoteItemOpt, YSearchResult, YSearchResultOpt,
};
pub use yahoo_error::{ErrorContext, YahooError, YahooErrorCode, YahooErrorWithContext};
const YCHART_URL: &str = "https://query1.finance.yahoo.com/v8/finance/chart";
const YSEARCH_URL: &str = "https://query2.finance.yahoo.com/v1/finance/search";
const Y_GET_COOKIE_URL: &str = "https://fc.yahoo.com";
const Y_GET_CRUMB_URL: &str = "https://query1.finance.yahoo.com/v1/test/getcrumb";
const Y_EARNINGS_URL: &str = "https://query1.finance.yahoo.com/v1/finance/visualization";
const Y_COOKIE_REQUEST_HEADER: &str = "set-cookie";
const USER_AGENT: &str = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) \
Chrome/122.0.0.0 Safari/537.36";
macro_rules! YCHART_PERIOD_QUERY {
() => {
"{url}/{symbol}?symbol={symbol}&period1={start}&period2={end}&interval={interval}&\
events=div|split|capitalGains"
};
}
macro_rules! YCHART_PERIOD_QUERY_PRE_POST {
() => {
"{url}/{symbol}?symbol={symbol}&period1={start}&period2={end}&interval={interval}&\
events=div|split|capitalGains&includePrePost={prepost}"
};
}
macro_rules! YCHART_RANGE_QUERY {
() => {
"{url}/{symbol}?symbol={symbol}&interval={interval}&range={range}&\
events=div|split|capitalGains"
};
}
macro_rules! YCHART_PERIOD_INTERVAL_QUERY {
() => {
"{url}/{symbol}?symbol={symbol}&range={range}&interval={interval}&includePrePost={prepost}"
};
}
macro_rules! YTICKER_QUERY {
() => {
"{url}?q={name}"
};
}
macro_rules! YQUOTE_SUMMARY_QUERY {
() => {
"https://query2.finance.yahoo.com/v10/finance/quoteSummary/{symbol}?modules=financialData,quoteType,defaultKeyStatistics,assetProfile,summaryDetail&corsDomain=finance.yahoo.com&formatted=false&symbol={symbol}&crumb={crumb}"
}
}
macro_rules! YEARNINGS_QUERY {
() => {
"{url}?lang={lang}®ion={region}&crumb={crumb}"
};
}
#[derive(Debug, Clone)]
pub struct YahooConnector {
client: Client,
url: &'static str,
search_url: &'static str,
timeout: Option<Duration>,
user_agent: Option<String>,
proxy: Option<Proxy>,
cookie: Option<String>,
crumb: Option<String>,
pub rate_limiter: Option<Arc<RateLimiter>>,
}
#[derive(Default)]
pub struct YahooConnectorBuilderLegacy {
inner: ClientBuilder,
timeout: Option<Duration>,
user_agent: Option<String>,
proxy: Option<Proxy>,
rate_limit_config: Option<RateLimitConfig>,
}
impl YahooConnector {
pub fn new() -> Result<YahooConnector, YahooError> {
Self::builder().build()
}
pub fn builder() -> crate::builder::YahooConnectorBuilder {
crate::builder::YahooConnectorBuilder::default()
}
pub fn from_preset(name: &str) -> Result<YahooConnector, YahooError> {
use crate::{
enterprise::{EnterpriseConfig, EnterpriseYahooConnector},
presets::PresetManager,
};
let manager = PresetManager::new();
let preset = manager.load_preset(name)?;
let enterprise_config = EnterpriseConfig::from(preset);
let rate_limit_config = enterprise_config.rate_limiter.clone();
let _enterprise_connector = EnterpriseYahooConnector::new(enterprise_config)?;
Self::builder().rate_limit(rate_limit_config.requests_per_hour as f64).build()
}
pub fn save_preset(&self, _name: &str) -> Result<(), YahooError> {
Err(YahooError::ConnectionFailed(
format!(
"Preset saving not yet implemented. Need to extract current configuration from \
YahooConnector and convert to PresetConfig."
)
.into(),
))
}
fn default_internal() -> Self {
YahooConnector {
client: Client::default(),
url: YCHART_URL,
search_url: YSEARCH_URL,
timeout: None,
user_agent: Some(USER_AGENT.to_string()),
proxy: None,
cookie: None,
crumb: None,
rate_limiter: None,
}
}
}
impl YahooConnectorBuilderLegacy {
pub fn new() -> Self {
YahooConnectorBuilderLegacy {
inner: Client::builder(),
user_agent: Some(USER_AGENT.to_string()),
..Default::default()
}
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
self
}
pub fn user_agent(mut self, user_agent: &str) -> Self {
self.user_agent = Some(user_agent.to_string());
self
}
pub fn proxy(mut self, proxy: Proxy) -> Self {
self.proxy = Some(proxy);
self
}
pub fn rate_limit_config(mut self, config: RateLimitConfig) -> Self {
self.rate_limit_config = Some(config);
self
}
pub fn build(mut self) -> Result<YahooConnector, YahooError> {
if let Some(timeout) = &self.timeout {
self.inner = self.inner.timeout(*timeout);
}
if let Some(user_agent) = &self.user_agent {
self.inner = self.inner.user_agent(user_agent.clone());
}
if let Some(proxy) = &self.proxy {
self.inner = self.inner.proxy(proxy.clone());
}
let rate_limiter = self.rate_limit_config.map(|config| Arc::new(RateLimiter::new(config)));
Ok(YahooConnector {
client: self.inner.use_rustls_tls().build()?,
timeout: self.timeout,
user_agent: self.user_agent,
proxy: self.proxy,
rate_limiter,
..YahooConnector::default_internal()
})
}
pub fn build_with_client(client: Client) -> Result<YahooConnector, YahooError> {
Ok(YahooConnector {
client,
..YahooConnector::default_internal()
})
}
}
impl YahooConnector {
pub fn with_rate_limiting() -> Result<YahooConnector, YahooError> {
Self::builder()
.rate_limit(RateLimitConfig::default().requests_per_hour as f64)
.build()
}
pub fn with_custom_rate_limiting(
config: RateLimitConfig,
) -> Result<YahooConnector, YahooError> {
Self::builder().rate_limit(config.requests_per_hour as f64).build()
}
pub fn rate_limit_status(&self) -> Option<RateLimitStatus> {
self.rate_limiter.as_ref().map(|limiter| limiter.status())
}
pub fn is_rate_limited(&self) -> bool {
self.rate_limiter.is_some()
}
}
pub mod async_impl;