use crate::adapters::yahoo::client::{ClientConfig, YahooClient};
use crate::constants::Region;
use crate::constants::screeners::Screener;
use crate::constants::sectors::Sector;
use crate::error::Result;
use crate::models::corporate::transcript::{Transcript, TranscriptWithMeta};
use crate::models::discovery::screeners::ScreenerResults;
use crate::models::discovery::search::SearchResults;
use crate::models::market::industries::IndustryData;
use crate::models::market::sectors::SectorData;
#[cfg(any(feature = "fmp", feature = "alphavantage"))]
use serde::{Deserialize, Serialize};
pub use crate::adapters::yahoo::discovery::lookup::{LookupOptions, LookupType};
pub use crate::adapters::yahoo::discovery::search::SearchOptions;
pub async fn search(query: &str, options: &SearchOptions) -> Result<SearchResults> {
let client = YahooClient::new(ClientConfig::default()).await?;
client.search(query, options).await
}
pub async fn lookup(
query: &str,
options: &LookupOptions,
) -> Result<crate::models::discovery::lookup::LookupResults> {
let client = YahooClient::new(ClientConfig::default()).await?;
client.lookup(query, options).await
}
pub async fn screener(screener_type: Screener, count: u32) -> Result<ScreenerResults> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::discovery::screeners::fetch(&client, screener_type, count).await
}
pub async fn custom_screener<F: crate::models::discovery::screeners::ScreenerField>(
query: crate::models::discovery::screeners::ScreenerQuery<F>,
) -> Result<ScreenerResults> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::discovery::screeners::fetch_custom(&client, query).await
}
pub async fn news() -> Result<Vec<crate::models::corporate::news::News>> {
crate::scrapers::stockanalysis::scrape_general_news().await
}
pub async fn earnings_transcript(
symbol: &str,
quarter: Option<&str>,
year: Option<i32>,
) -> Result<Transcript> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::corporate::transcripts::fetch_for_symbol(&client, symbol, quarter, year)
.await
}
pub async fn earnings_transcripts(
symbol: &str,
limit: Option<usize>,
) -> Result<Vec<TranscriptWithMeta>> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::corporate::transcripts::fetch_all_for_symbol(&client, symbol, limit)
.await
}
pub async fn hours(region: Option<&str>) -> Result<crate::models::market::hours::MarketHours> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::market::hours::fetch(&client, region).await
}
pub async fn indices(
region: Option<crate::constants::indices::Region>,
) -> Result<crate::tickers::BatchQuotesResponse> {
use crate::Tickers;
use crate::constants::indices::all_symbols;
let symbols: Vec<&str> = match region {
Some(r) => r.symbols().to_vec(),
None => all_symbols(),
};
let tickers = Tickers::new(symbols).await?;
tickers.quotes().await
}
pub async fn sector(sector_type: Sector) -> Result<SectorData> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::market::sectors::fetch(&client, sector_type).await
}
pub async fn industry(industry_key: impl AsRef<str>) -> Result<IndustryData> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::market::industries::fetch(&client, industry_key.as_ref()).await
}
pub async fn currencies() -> Result<Vec<crate::models::market::currencies::Currency>> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::market::currencies::fetch(&client).await
}
pub async fn exchanges() -> Result<Vec<crate::models::market::exchanges::Exchange>> {
crate::scrapers::yahoo_exchanges::scrape_exchanges().await
}
pub async fn market_summary(
region: Option<Region>,
) -> Result<Vec<crate::models::market::market_summary::MarketSummaryQuote>> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::market::market_summary::fetch(&client, region).await
}
pub async fn trending(
region: Option<Region>,
) -> Result<Vec<crate::models::discovery::trending::TrendingQuote>> {
let client = YahooClient::new(ClientConfig::default()).await?;
crate::adapters::yahoo::market::trending::fetch(&client, region).await
}
pub async fn fear_and_greed() -> Result<crate::models::sentiment::FearAndGreed> {
crate::adapters::yahoo::market::fear_and_greed::fetch().await
}
#[cfg(feature = "fmp")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Period {
Annual,
Quarter,
}
#[cfg(feature = "fmp")]
impl From<Period> for crate::adapters::fmp::models::Period {
fn from(p: Period) -> Self {
match p {
Period::Annual => Self::Annual,
Period::Quarter => Self::Quarter,
}
}
}
#[cfg(feature = "fmp")]
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InsiderTransaction {
pub symbol: Option<String>,
pub filing_date: Option<String>,
pub transaction_date: Option<String>,
pub reporting_name: Option<String>,
pub transaction_type: Option<String>,
pub securities_transacted: Option<f64>,
pub price: Option<f64>,
pub securities_owned: Option<f64>,
pub type_of_owner: Option<String>,
pub link: Option<String>,
}
#[cfg(feature = "fmp")]
impl From<crate::adapters::fmp::corporate::insider_trading::InsiderTradeDTO>
for InsiderTransaction
{
fn from(d: crate::adapters::fmp::corporate::insider_trading::InsiderTradeDTO) -> Self {
use crate::adapters::fmp::corporate::insider_trading::InsiderTradeDTO;
let InsiderTradeDTO {
symbol,
filing_date,
transaction_date,
reporting_name,
transaction_type,
securities_transacted,
price,
securities_owned,
type_of_owner,
link,
..
} = d;
Self {
symbol,
filing_date,
transaction_date,
reporting_name,
transaction_type,
securities_transacted,
price,
securities_owned,
type_of_owner,
link,
}
}
}
#[cfg(feature = "fmp")]
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalystEstimate {
pub symbol: Option<String>,
pub date: Option<String>,
pub estimated_revenue_low: Option<f64>,
pub estimated_revenue_high: Option<f64>,
pub estimated_revenue_avg: Option<f64>,
pub estimated_ebitda_low: Option<f64>,
pub estimated_ebitda_high: Option<f64>,
pub estimated_ebitda_avg: Option<f64>,
pub estimated_eps_avg: Option<f64>,
pub estimated_eps_high: Option<f64>,
pub estimated_eps_low: Option<f64>,
pub number_analyst_estimated_revenue: Option<i32>,
pub number_analysts_estimated_eps: Option<i32>,
}
#[cfg(feature = "fmp")]
impl From<crate::adapters::fmp::fundamentals::estimates::AnalystEstimateDTO> for AnalystEstimate {
fn from(d: crate::adapters::fmp::fundamentals::estimates::AnalystEstimateDTO) -> Self {
use crate::adapters::fmp::fundamentals::estimates::AnalystEstimateDTO;
let AnalystEstimateDTO {
symbol,
date,
estimated_revenue_low,
estimated_revenue_high,
estimated_revenue_avg,
estimated_ebitda_low,
estimated_ebitda_high,
estimated_ebitda_avg,
estimated_eps_avg,
estimated_eps_high,
estimated_eps_low,
number_analyst_estimated_revenue,
number_analysts_estimated_eps,
} = d;
Self {
symbol,
date,
estimated_revenue_low,
estimated_revenue_high,
estimated_revenue_avg,
estimated_ebitda_low,
estimated_ebitda_high,
estimated_ebitda_avg,
estimated_eps_avg,
estimated_eps_high,
estimated_eps_low,
number_analyst_estimated_revenue,
number_analysts_estimated_eps,
}
}
}
#[cfg(feature = "fmp")]
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalystRecommendation {
pub symbol: Option<String>,
pub date: Option<String>,
pub analyst_ratings_buy: Option<i32>,
pub analyst_ratings_hold: Option<i32>,
pub analyst_ratings_sell: Option<i32>,
pub analyst_ratings_strong_buy: Option<i32>,
pub analyst_ratings_strong_sell: Option<i32>,
}
#[cfg(feature = "fmp")]
impl From<crate::adapters::fmp::fundamentals::estimates::AnalystRecommendationDTO>
for AnalystRecommendation
{
fn from(d: crate::adapters::fmp::fundamentals::estimates::AnalystRecommendationDTO) -> Self {
use crate::adapters::fmp::fundamentals::estimates::AnalystRecommendationDTO;
let AnalystRecommendationDTO {
symbol,
date,
analyst_ratings_buy,
analyst_ratings_hold,
analyst_ratings_sell,
analyst_ratings_strong_buy,
analyst_ratings_strong_sell,
} = d;
Self {
symbol,
date,
analyst_ratings_buy,
analyst_ratings_hold,
analyst_ratings_sell,
analyst_ratings_strong_buy,
analyst_ratings_strong_sell,
}
}
}
#[cfg(feature = "fmp")]
pub async fn insider_trading(symbol: &str, limit: u32) -> Result<Vec<InsiderTransaction>> {
crate::adapters::fmp::corporate::insider_trading::insider_trading(symbol, limit)
.await
.map(|v| v.into_iter().map(Into::into).collect())
}
#[cfg(feature = "fmp")]
pub async fn analyst_estimates(symbol: &str, period: Period) -> Result<Vec<AnalystEstimate>> {
crate::adapters::fmp::fundamentals::estimates::analyst_estimates(symbol, period.into(), 4)
.await
.map(|v| v.into_iter().map(Into::into).collect())
}
#[cfg(feature = "fmp")]
pub async fn analyst_recommendations(symbol: &str) -> Result<Vec<AnalystRecommendation>> {
crate::adapters::fmp::fundamentals::estimates::analyst_recommendations(symbol)
.await
.map(|v| v.into_iter().map(Into::into).collect())
}
#[cfg(feature = "polygon")]
pub async fn symbol_sentiment(symbol: &str) -> Result<crate::models::sentiment::SymbolSentiment> {
use crate::adapters::polygon;
let paginated = polygon::stock_news(&[("ticker", symbol), ("limit", "10")]).await?;
let articles = paginated.results.unwrap_or_default();
let mut positive = 0u32;
let mut negative = 0u32;
let total = articles.len().max(1) as f64;
for article in &articles {
if let Some(ref insights) = article.insights {
for insight in insights {
if insight.ticker.as_deref() == Some(symbol) {
match insight.sentiment.as_deref() {
Some("positive") => positive += 1,
Some("negative") => negative += 1,
_ => {}
}
}
}
}
}
let (score, label): (Option<f64>, Option<String>) = if total > 0.0 {
let s = (positive as f64 - negative as f64) / total;
let l = if s > 0.2 {
"positive"
} else if s < -0.2 {
"negative"
} else {
"neutral"
};
(Some(s), Some(l.to_string()))
} else {
(None, None)
};
Ok(crate::models::sentiment::SymbolSentiment { score, label })
}
#[cfg(feature = "alphavantage")]
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EarningsCalendarEntry {
pub symbol: String,
pub name: Option<String>,
pub report_date: Option<String>,
pub fiscal_date_ending: Option<String>,
pub estimate: Option<f64>,
pub currency: Option<String>,
}
#[cfg(feature = "alphavantage")]
impl From<crate::adapters::alphavantage::models::EarningsCalendarEntryDTO>
for EarningsCalendarEntry
{
fn from(d: crate::adapters::alphavantage::models::EarningsCalendarEntryDTO) -> Self {
Self {
symbol: d.symbol,
name: d.name,
report_date: d.report_date,
fiscal_date_ending: d.fiscal_date_ending,
estimate: d.estimate,
currency: d.currency,
}
}
}
#[cfg(feature = "alphavantage")]
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IpoCalendarEntry {
pub symbol: Option<String>,
pub name: Option<String>,
pub ipo_date: Option<String>,
pub price_range: Option<String>,
pub exchange: Option<String>,
}
#[cfg(feature = "alphavantage")]
impl From<crate::adapters::alphavantage::models::IpoCalendarEntryDTO> for IpoCalendarEntry {
fn from(d: crate::adapters::alphavantage::models::IpoCalendarEntryDTO) -> Self {
Self {
symbol: d.symbol,
name: d.name,
ipo_date: d.ipo_date,
price_range: d.price_range,
exchange: d.exchange,
}
}
}
#[cfg(feature = "alphavantage")]
pub async fn earnings_calendar() -> Result<Vec<EarningsCalendarEntry>> {
crate::adapters::alphavantage::fundamentals::earnings_calendar()
.await
.map(|v| v.into_iter().map(Into::into).collect())
}
#[cfg(feature = "alphavantage")]
pub async fn ipo_calendar() -> Result<Vec<IpoCalendarEntry>> {
crate::adapters::alphavantage::fundamentals::ipo_calendar()
.await
.map(|v| v.into_iter().map(Into::into).collect())
}