use serde::{Deserialize, Serialize};
use super::{
BalanceSheetHistory, BalanceSheetHistoryQuarterly, CalendarEvents, CashflowStatementHistory,
CashflowStatementHistoryQuarterly, Earnings, EarningsHistory, EarningsTrend, EquityPerformance,
FundOwnership, FundPerformance, FundProfile, IncomeStatementHistory,
IncomeStatementHistoryQuarterly, IndexTrend, IndustryTrend, InsiderHolders,
InsiderTransactions, InstitutionOwnership, MajorHoldersBreakdown, NetSharePurchaseActivity,
QuoteSummaryResponse, RecommendationTrend, SecFilings, SectorTrend, TopHoldings,
UpgradeDowngradeHistory,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "dataframe", derive(crate::ToDataFrame))]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct Quote {
pub symbol: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub logo_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub company_logo_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub short_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub long_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exchange: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exchange_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quote_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub currency_symbol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub underlying_symbol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub from_currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to_currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_change: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_change_percent: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_day_high: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_day_low: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_open: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_previous_close: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub regular_market_volume: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub market_state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub day_high: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub day_low: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub open: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub previous_close: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub volume: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub all_time_high: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub all_time_low: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pre_market_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pre_market_change: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pre_market_change_percent: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pre_market_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub post_market_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub post_market_change: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub post_market_change_percent: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub post_market_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub average_daily_volume10_day: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub average_daily_volume3_month: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub average_volume: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub average_volume10days: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub market_cap: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enterprise_value: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enterprise_to_revenue: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enterprise_to_ebitda: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price_to_book: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price_to_sales_trailing12_months: Option<super::FormattedValue<f64>>,
#[serde(rename = "forwardPE", skip_serializing_if = "Option::is_none")]
pub forward_pe: Option<super::FormattedValue<f64>>,
#[serde(rename = "trailingPE", skip_serializing_if = "Option::is_none")]
pub trailing_pe: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub beta: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fifty_two_week_high: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fifty_two_week_low: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fifty_day_average: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub two_hundred_day_average: Option<super::FormattedValue<f64>>,
#[serde(rename = "52WeekChange", skip_serializing_if = "Option::is_none")]
pub week_52_change: Option<super::FormattedValue<f64>>,
#[serde(rename = "SandP52WeekChange", skip_serializing_if = "Option::is_none")]
pub sand_p_52_week_change: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dividend_rate: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dividend_yield: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trailing_annual_dividend_rate: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trailing_annual_dividend_yield: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub five_year_avg_dividend_yield: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ex_dividend_date: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub payout_ratio: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_dividend_value: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_dividend_date: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bid: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bid_size: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ask: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ask_size: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shares_outstanding: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub float_shares: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub implied_shares_outstanding: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub held_percent_insiders: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub held_percent_institutions: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shares_short: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shares_short_prior_month: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub short_ratio: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub short_percent_of_float: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shares_percent_shares_out: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub date_short_interest: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_high_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_low_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_mean_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_median_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recommendation_mean: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recommendation_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub number_of_analyst_opinions: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_cash: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_cash_per_share: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ebitda: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_debt: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_revenue: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub net_income_to_common: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub debt_to_equity: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub revenue_per_share: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub return_on_assets: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub return_on_equity: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub free_cashflow: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub operating_cashflow: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub profit_margins: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gross_margins: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ebitda_margins: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub operating_margins: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gross_profits: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub earnings_growth: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub revenue_growth: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub earnings_quarterly_growth: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_ratio: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quick_ratio: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trailing_eps: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub forward_eps: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub book_value: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sector: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sector_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sector_disp: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub industry: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub industry_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub industry_disp: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub long_business_summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub website: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ir_website: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address1: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub city: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub zip: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub country: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub phone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub full_time_employees: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fund_family: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub audit_risk: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub board_risk: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub compensation_risk: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shareholder_rights_risk: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub overall_risk: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_zone_full_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub time_zone_short_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gmt_off_set_milliseconds: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub first_trade_date_epoch_utc: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message_board_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub exchange_data_delayed_by: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub nav_price: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_assets: Option<super::FormattedValue<i64>>,
#[serde(rename = "yield", skip_serializing_if = "Option::is_none")]
pub yield_value: Option<super::FormattedValue<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_split_factor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_split_date: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_fiscal_year_end: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_fiscal_year_end: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub most_recent_quarter: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub price_hint: Option<super::FormattedValue<i64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tradeable: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub financial_currency: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub company_officers: Option<Vec<super::CompanyOfficer>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub earnings: Option<Earnings>,
#[serde(skip_serializing_if = "Option::is_none")]
pub calendar_events: Option<CalendarEvents>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recommendation_trend: Option<RecommendationTrend>,
#[serde(skip_serializing_if = "Option::is_none")]
pub upgrade_downgrade_history: Option<UpgradeDowngradeHistory>,
#[serde(skip_serializing_if = "Option::is_none")]
pub earnings_history: Option<EarningsHistory>,
#[serde(skip_serializing_if = "Option::is_none")]
pub earnings_trend: Option<EarningsTrend>,
#[serde(skip_serializing_if = "Option::is_none")]
pub insider_holders: Option<InsiderHolders>,
#[serde(skip_serializing_if = "Option::is_none")]
pub insider_transactions: Option<InsiderTransactions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub institution_ownership: Option<InstitutionOwnership>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fund_ownership: Option<FundOwnership>,
#[serde(skip_serializing_if = "Option::is_none")]
pub major_holders_breakdown: Option<MajorHoldersBreakdown>,
#[serde(skip_serializing_if = "Option::is_none")]
pub net_share_purchase_activity: Option<NetSharePurchaseActivity>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sec_filings: Option<SecFilings>,
#[serde(skip_serializing_if = "Option::is_none")]
pub balance_sheet_history: Option<BalanceSheetHistory>,
#[serde(skip_serializing_if = "Option::is_none")]
pub balance_sheet_history_quarterly: Option<BalanceSheetHistoryQuarterly>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cashflow_statement_history: Option<CashflowStatementHistory>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cashflow_statement_history_quarterly: Option<CashflowStatementHistoryQuarterly>,
#[serde(skip_serializing_if = "Option::is_none")]
pub income_statement_history: Option<IncomeStatementHistory>,
#[serde(skip_serializing_if = "Option::is_none")]
pub income_statement_history_quarterly: Option<IncomeStatementHistoryQuarterly>,
#[serde(skip_serializing_if = "Option::is_none")]
pub equity_performance: Option<EquityPerformance>,
#[serde(skip_serializing_if = "Option::is_none")]
pub index_trend: Option<IndexTrend>,
#[serde(skip_serializing_if = "Option::is_none")]
pub industry_trend: Option<IndustryTrend>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sector_trend: Option<SectorTrend>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fund_profile: Option<FundProfile>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fund_performance: Option<FundPerformance>,
#[serde(skip_serializing_if = "Option::is_none")]
pub top_holdings: Option<TopHoldings>,
}
impl Quote {
pub(crate) fn from_response(
response: &QuoteSummaryResponse,
logo_url: Option<String>,
company_logo_url: Option<String>,
) -> Self {
let price = response.price.as_ref();
let quote_type = response.quote_type.as_ref();
let summary_detail = response.summary_detail.as_ref();
let financial_data = response.financial_data.as_ref();
let key_stats = response.default_key_statistics.as_ref();
let asset_profile = response.asset_profile.as_ref();
let summary_profile = response.summary_profile.as_ref();
Self {
symbol: response.symbol.clone(),
logo_url,
company_logo_url,
short_name: price
.and_then(|p| p.short_name.clone())
.or_else(|| quote_type.and_then(|q| q.short_name.clone())),
long_name: price
.and_then(|p| p.long_name.clone())
.or_else(|| quote_type.and_then(|q| q.long_name.clone())),
exchange: price
.and_then(|p| p.exchange.clone())
.or_else(|| quote_type.and_then(|q| q.exchange.clone())),
exchange_name: price.and_then(|p| p.exchange_name.clone()),
quote_type: price
.and_then(|p| p.quote_type.clone())
.or_else(|| quote_type.and_then(|q| q.quote_type.clone())),
currency: price.and_then(|p| p.currency.clone()).or_else(|| {
summary_detail
.and_then(|s| s.currency.clone())
.or_else(|| financial_data.and_then(|f| f.financial_currency.clone()))
}),
currency_symbol: price.and_then(|p| p.currency_symbol.clone()),
underlying_symbol: price
.and_then(|p| p.underlying_symbol.clone())
.or_else(|| quote_type.and_then(|q| q.underlying_symbol.clone())),
from_currency: price
.and_then(|p| p.from_currency.clone())
.or_else(|| summary_detail.and_then(|s| s.from_currency.clone())),
to_currency: price
.and_then(|p| p.to_currency.clone())
.or_else(|| summary_detail.and_then(|s| s.to_currency.clone())),
regular_market_price: price.and_then(|p| p.regular_market_price.clone()),
regular_market_change: price.and_then(|p| p.regular_market_change.clone()),
regular_market_change_percent: price
.and_then(|p| p.regular_market_change_percent.clone()),
regular_market_time: price.and_then(|p| p.regular_market_time),
regular_market_day_high: price.and_then(|p| p.regular_market_day_high.clone()),
regular_market_day_low: price.and_then(|p| p.regular_market_day_low.clone()),
regular_market_open: price.and_then(|p| p.regular_market_open.clone()),
regular_market_previous_close: price
.and_then(|p| p.regular_market_previous_close.clone()),
regular_market_volume: price.and_then(|p| p.regular_market_volume.clone()),
market_state: price.and_then(|p| p.market_state.clone()),
day_high: summary_detail.and_then(|s| s.day_high.clone()),
day_low: summary_detail.and_then(|s| s.day_low.clone()),
open: summary_detail.and_then(|s| s.open.clone()),
previous_close: summary_detail.and_then(|s| s.previous_close.clone()),
volume: summary_detail.and_then(|s| s.volume.clone()),
all_time_high: summary_detail.and_then(|s| s.all_time_high.clone()),
all_time_low: summary_detail.and_then(|s| s.all_time_low.clone()),
pre_market_price: price.and_then(|p| p.pre_market_price.clone()),
pre_market_change: price.and_then(|p| p.pre_market_change.clone()),
pre_market_change_percent: price.and_then(|p| p.pre_market_change_percent.clone()),
pre_market_time: price.and_then(|p| p.pre_market_time),
post_market_price: price.and_then(|p| p.post_market_price.clone()),
post_market_change: price.and_then(|p| p.post_market_change.clone()),
post_market_change_percent: price.and_then(|p| p.post_market_change_percent.clone()),
post_market_time: price.and_then(|p| p.post_market_time),
average_daily_volume10_day: price
.and_then(|p| p.average_daily_volume10_day.clone())
.or_else(|| summary_detail.and_then(|s| s.average_daily_volume10_day.clone())),
average_daily_volume3_month: price.and_then(|p| p.average_daily_volume3_month.clone()),
average_volume: summary_detail.and_then(|s| s.average_volume.clone()),
average_volume10days: summary_detail.and_then(|s| s.average_volume10days.clone()),
market_cap: price.and_then(|p| p.market_cap.clone()),
enterprise_value: key_stats.and_then(|k| k.enterprise_value.clone()),
enterprise_to_revenue: key_stats.and_then(|k| k.enterprise_to_revenue.clone()),
enterprise_to_ebitda: key_stats.and_then(|k| k.enterprise_to_ebitda.clone()),
price_to_book: key_stats.and_then(|k| k.price_to_book.clone()),
price_to_sales_trailing12_months: summary_detail
.and_then(|s| s.price_to_sales_trailing12_months.clone()),
forward_pe: summary_detail
.and_then(|s| s.forward_pe.clone())
.or_else(|| key_stats.and_then(|k| k.forward_pe.clone())),
trailing_pe: summary_detail.and_then(|s| s.trailing_pe.clone()),
beta: summary_detail
.and_then(|s| s.beta.clone())
.or_else(|| key_stats.and_then(|k| k.beta.clone())),
fifty_two_week_high: summary_detail.and_then(|s| s.fifty_two_week_high.clone()),
fifty_two_week_low: summary_detail.and_then(|s| s.fifty_two_week_low.clone()),
fifty_day_average: summary_detail.and_then(|s| s.fifty_day_average.clone()),
two_hundred_day_average: summary_detail.and_then(|s| s.two_hundred_day_average.clone()),
week_52_change: key_stats.and_then(|k| k.week_52_change.clone()),
sand_p_52_week_change: key_stats.and_then(|k| k.sand_p_52_week_change.clone()),
dividend_rate: summary_detail.and_then(|s| s.dividend_rate.clone()),
dividend_yield: summary_detail.and_then(|s| s.dividend_yield.clone()),
trailing_annual_dividend_rate: summary_detail
.and_then(|s| s.trailing_annual_dividend_rate.clone()),
trailing_annual_dividend_yield: summary_detail
.and_then(|s| s.trailing_annual_dividend_yield.clone()),
five_year_avg_dividend_yield: summary_detail
.and_then(|s| s.five_year_avg_dividend_yield.clone()),
ex_dividend_date: summary_detail.and_then(|s| s.ex_dividend_date.clone()),
payout_ratio: summary_detail.and_then(|s| s.payout_ratio.clone()),
last_dividend_value: key_stats.and_then(|k| k.last_dividend_value.clone()),
last_dividend_date: key_stats.and_then(|k| k.last_dividend_date.clone()),
bid: summary_detail.and_then(|s| s.bid.clone()),
bid_size: summary_detail.and_then(|s| s.bid_size.clone()),
ask: summary_detail.and_then(|s| s.ask.clone()),
ask_size: summary_detail.and_then(|s| s.ask_size.clone()),
shares_outstanding: key_stats.and_then(|k| k.shares_outstanding.clone()),
float_shares: key_stats.and_then(|k| k.float_shares.clone()),
implied_shares_outstanding: key_stats
.and_then(|k| k.implied_shares_outstanding.clone()),
held_percent_insiders: key_stats.and_then(|k| k.held_percent_insiders.clone()),
held_percent_institutions: key_stats.and_then(|k| k.held_percent_institutions.clone()),
shares_short: key_stats.and_then(|k| k.shares_short.clone()),
shares_short_prior_month: key_stats.and_then(|k| k.shares_short_prior_month.clone()),
short_ratio: key_stats.and_then(|k| k.short_ratio.clone()),
short_percent_of_float: key_stats.and_then(|k| k.short_percent_of_float.clone()),
shares_percent_shares_out: key_stats.and_then(|k| k.shares_percent_shares_out.clone()),
date_short_interest: key_stats.and_then(|k| k.date_short_interest.clone()),
current_price: financial_data.and_then(|f| f.current_price.clone()),
target_high_price: financial_data.and_then(|f| f.target_high_price.clone()),
target_low_price: financial_data.and_then(|f| f.target_low_price.clone()),
target_mean_price: financial_data.and_then(|f| f.target_mean_price.clone()),
target_median_price: financial_data.and_then(|f| f.target_median_price.clone()),
recommendation_mean: financial_data.and_then(|f| f.recommendation_mean.clone()),
recommendation_key: financial_data.and_then(|f| f.recommendation_key.clone()),
number_of_analyst_opinions: financial_data
.and_then(|f| f.number_of_analyst_opinions.clone()),
total_cash: financial_data.and_then(|f| f.total_cash.clone()),
total_cash_per_share: financial_data.and_then(|f| f.total_cash_per_share.clone()),
ebitda: financial_data.and_then(|f| f.ebitda.clone()),
total_debt: financial_data.and_then(|f| f.total_debt.clone()),
total_revenue: financial_data.and_then(|f| f.total_revenue.clone()),
net_income_to_common: key_stats.and_then(|k| k.net_income_to_common.clone()),
debt_to_equity: financial_data.and_then(|f| f.debt_to_equity.clone()),
revenue_per_share: financial_data.and_then(|f| f.revenue_per_share.clone()),
return_on_assets: financial_data.and_then(|f| f.return_on_assets.clone()),
return_on_equity: financial_data.and_then(|f| f.return_on_equity.clone()),
free_cashflow: financial_data.and_then(|f| f.free_cashflow.clone()),
operating_cashflow: financial_data.and_then(|f| f.operating_cashflow.clone()),
profit_margins: financial_data.and_then(|f| f.profit_margins.clone()),
gross_margins: financial_data.and_then(|f| f.gross_margins.clone()),
ebitda_margins: financial_data.and_then(|f| f.ebitda_margins.clone()),
operating_margins: financial_data.and_then(|f| f.operating_margins.clone()),
gross_profits: financial_data.and_then(|f| f.gross_profits.clone()),
earnings_growth: financial_data.and_then(|f| f.earnings_growth.clone()),
revenue_growth: financial_data.and_then(|f| f.revenue_growth.clone()),
earnings_quarterly_growth: key_stats.and_then(|k| k.earnings_quarterly_growth.clone()),
current_ratio: financial_data.and_then(|f| f.current_ratio.clone()),
quick_ratio: financial_data.and_then(|f| f.quick_ratio.clone()),
trailing_eps: key_stats.and_then(|k| k.trailing_eps.clone()),
forward_eps: key_stats.and_then(|k| k.forward_eps.clone()),
book_value: key_stats.and_then(|k| k.book_value.clone()),
sector: asset_profile
.and_then(|a| a.sector.clone())
.or_else(|| summary_profile.and_then(|s| s.sector.clone())),
sector_key: asset_profile.and_then(|a| a.sector_key.clone()),
sector_disp: asset_profile.and_then(|a| a.sector_disp.clone()),
industry: asset_profile
.and_then(|a| a.industry.clone())
.or_else(|| summary_profile.and_then(|s| s.industry.clone())),
industry_key: asset_profile.and_then(|a| a.industry_key.clone()),
industry_disp: asset_profile.and_then(|a| a.industry_disp.clone()),
long_business_summary: asset_profile
.and_then(|a| a.long_business_summary.clone())
.or_else(|| summary_profile.and_then(|s| s.long_business_summary.clone())),
address1: asset_profile
.and_then(|a| a.address1.clone())
.or_else(|| summary_profile.and_then(|s| s.address1.clone())),
city: asset_profile
.and_then(|a| a.city.clone())
.or_else(|| summary_profile.and_then(|s| s.city.clone())),
state: asset_profile
.and_then(|a| a.state.clone())
.or_else(|| summary_profile.and_then(|s| s.state.clone())),
zip: asset_profile
.and_then(|a| a.zip.clone())
.or_else(|| summary_profile.and_then(|s| s.zip.clone())),
country: asset_profile
.and_then(|a| a.country.clone())
.or_else(|| summary_profile.and_then(|s| s.country.clone())),
phone: asset_profile
.and_then(|a| a.phone.clone())
.or_else(|| summary_profile.and_then(|s| s.phone.clone())),
full_time_employees: asset_profile
.and_then(|a| a.full_time_employees)
.or_else(|| summary_profile.and_then(|s| s.full_time_employees)),
website: asset_profile
.and_then(|a| a.website.clone())
.or_else(|| summary_profile.and_then(|s| s.website.clone())),
ir_website: summary_profile.and_then(|s| s.ir_website.clone()),
category: key_stats.and_then(|k| k.category.clone()),
fund_family: key_stats.and_then(|k| k.fund_family.clone()),
audit_risk: asset_profile.and_then(|a| a.audit_risk),
board_risk: asset_profile.and_then(|a| a.board_risk),
compensation_risk: asset_profile.and_then(|a| a.compensation_risk),
shareholder_rights_risk: asset_profile.and_then(|a| a.shareholder_rights_risk),
overall_risk: asset_profile.and_then(|a| a.overall_risk),
time_zone_full_name: quote_type.and_then(|q| q.time_zone_full_name.clone()),
time_zone_short_name: quote_type.and_then(|q| q.time_zone_short_name.clone()),
gmt_off_set_milliseconds: quote_type.and_then(|q| q.gmt_off_set_milliseconds),
first_trade_date_epoch_utc: quote_type.and_then(|q| q.first_trade_date_epoch_utc),
message_board_id: quote_type.and_then(|q| q.message_board_id.clone()),
exchange_data_delayed_by: price.and_then(|p| p.exchange_data_delayed_by),
nav_price: summary_detail.and_then(|s| s.nav_price.clone()),
total_assets: summary_detail.and_then(|s| s.total_assets.clone()),
yield_value: summary_detail.and_then(|s| s.yield_value.clone()),
last_split_factor: key_stats.and_then(|k| k.last_split_factor.clone()),
last_split_date: key_stats.and_then(|k| k.last_split_date.clone()),
last_fiscal_year_end: key_stats.and_then(|k| k.last_fiscal_year_end.clone()),
next_fiscal_year_end: key_stats.and_then(|k| k.next_fiscal_year_end.clone()),
most_recent_quarter: key_stats.and_then(|k| k.most_recent_quarter.clone()),
price_hint: price.and_then(|p| p.price_hint.clone()),
tradeable: summary_detail.and_then(|s| s.tradeable),
financial_currency: financial_data.and_then(|f| f.financial_currency.clone()),
company_officers: asset_profile.map(|a| a.company_officers.clone()),
earnings: response.earnings.clone(),
calendar_events: response.calendar_events.clone(),
recommendation_trend: response.recommendation_trend.clone(),
upgrade_downgrade_history: response.upgrade_downgrade_history.clone(),
earnings_history: response.earnings_history.clone(),
earnings_trend: response.earnings_trend.clone(),
insider_holders: response.insider_holders.clone(),
insider_transactions: response.insider_transactions.clone(),
institution_ownership: response.institution_ownership.clone(),
fund_ownership: response.fund_ownership.clone(),
major_holders_breakdown: response.major_holders_breakdown.clone(),
net_share_purchase_activity: response.net_share_purchase_activity.clone(),
sec_filings: response.sec_filings.clone(),
balance_sheet_history: None,
balance_sheet_history_quarterly: None,
cashflow_statement_history: None,
cashflow_statement_history_quarterly: None,
income_statement_history: None,
income_statement_history_quarterly: None,
equity_performance: response.equity_performance.clone(),
index_trend: response.index_trend.clone(),
industry_trend: response.industry_trend.clone(),
sector_trend: response.sector_trend.clone(),
fund_profile: response.fund_profile.clone(),
fund_performance: response.fund_performance.clone(),
top_holdings: response.top_holdings.clone(),
}
}
pub fn live_price(&self) -> Option<f64> {
if self.market_state.as_deref() == Some("POST") {
self.post_market_price
.as_ref()
.and_then(|p| p.raw)
.or_else(|| self.regular_market_price.as_ref()?.raw)
} else if self.market_state.as_deref() == Some("PRE") {
self.pre_market_price
.as_ref()
.and_then(|p| p.raw)
.or_else(|| self.regular_market_price.as_ref()?.raw)
} else {
self.regular_market_price.as_ref()?.raw
}
}
pub fn day_range(&self) -> Option<(f64, f64)> {
let low = self.regular_market_day_low.as_ref()?.raw?;
let high = self.regular_market_day_high.as_ref()?.raw?;
Some((low, high))
}
pub fn week_52_range(&self) -> Option<(f64, f64)> {
let low = self.fifty_two_week_low.as_ref()?.raw?;
let high = self.fifty_two_week_high.as_ref()?.raw?;
Some((low, high))
}
pub fn is_market_open(&self) -> bool {
self.market_state.as_deref() == Some("REGULAR")
}
pub fn is_pre_market(&self) -> bool {
self.market_state.as_deref() == Some("PRE")
}
pub fn is_post_market(&self) -> bool {
self.market_state.as_deref() == Some("POST")
}
pub(crate) fn from_batch_response(json: &serde_json::Value) -> Result<Self, String> {
let symbol = json
.get("symbol")
.and_then(|v| v.as_str())
.ok_or("Missing symbol")?
.to_string();
let get_f64 = |key: &str| -> Option<super::FormattedValue<f64>> {
json.get(key).and_then(|v| {
if let Some(raw) = v.as_f64() {
Some(super::FormattedValue {
raw: Some(raw),
fmt: None,
long_fmt: None,
})
} else if v.is_object() {
Some(super::FormattedValue {
raw: v.get("raw").and_then(|r| r.as_f64()),
fmt: v.get("fmt").and_then(|f| f.as_str()).map(|s| s.to_string()),
long_fmt: v
.get("longFmt")
.and_then(|f| f.as_str())
.map(|s| s.to_string()),
})
} else {
None
}
})
};
let get_i64 = |key: &str| -> Option<super::FormattedValue<i64>> {
json.get(key).and_then(|v| {
if let Some(raw) = v.as_i64() {
Some(super::FormattedValue {
raw: Some(raw),
fmt: None,
long_fmt: None,
})
} else if v.is_object() {
Some(super::FormattedValue {
raw: v.get("raw").and_then(|r| r.as_i64()),
fmt: v.get("fmt").and_then(|f| f.as_str()).map(|s| s.to_string()),
long_fmt: v
.get("longFmt")
.and_then(|f| f.as_str())
.map(|s| s.to_string()),
})
} else {
None
}
})
};
let get_str = |key: &str| -> Option<String> {
json.get(key)
.and_then(|v| v.as_str())
.map(|s| s.to_string())
};
Ok(Self {
symbol,
logo_url: get_str("logoUrl"),
company_logo_url: get_str("companyLogoUrl"),
short_name: get_str("shortName"),
long_name: get_str("longName"),
exchange: get_str("exchange"),
exchange_name: get_str("fullExchangeName"),
quote_type: get_str("quoteType"),
currency: get_str("currency"),
currency_symbol: get_str("currencySymbol"),
underlying_symbol: get_str("underlyingSymbol"),
from_currency: get_str("fromCurrency"),
to_currency: get_str("toCurrency"),
regular_market_price: get_f64("regularMarketPrice"),
regular_market_change: get_f64("regularMarketChange"),
regular_market_change_percent: get_f64("regularMarketChangePercent"),
regular_market_time: json.get("regularMarketTime").and_then(|v| v.as_i64()),
regular_market_day_high: get_f64("regularMarketDayHigh"),
regular_market_day_low: get_f64("regularMarketDayLow"),
regular_market_open: get_f64("regularMarketOpen"),
regular_market_previous_close: get_f64("regularMarketPreviousClose"),
regular_market_volume: get_i64("regularMarketVolume"),
market_state: get_str("marketState"),
day_high: get_f64("dayHigh"),
day_low: get_f64("dayLow"),
open: get_f64("open"),
previous_close: get_f64("previousClose"),
volume: get_i64("volume"),
all_time_high: None,
all_time_low: None,
pre_market_price: get_f64("preMarketPrice"),
pre_market_change: get_f64("preMarketChange"),
pre_market_change_percent: get_f64("preMarketChangePercent"),
pre_market_time: json.get("preMarketTime").and_then(|v| v.as_i64()),
post_market_price: get_f64("postMarketPrice"),
post_market_change: get_f64("postMarketChange"),
post_market_change_percent: get_f64("postMarketChangePercent"),
post_market_time: json.get("postMarketTime").and_then(|v| v.as_i64()),
average_daily_volume10_day: get_i64("averageDailyVolume10Day"),
average_daily_volume3_month: get_i64("averageDailyVolume3Month"),
average_volume: get_i64("averageVolume"),
average_volume10days: get_i64("averageVolume10days"),
market_cap: get_i64("marketCap"),
enterprise_value: None,
enterprise_to_revenue: None,
enterprise_to_ebitda: None,
price_to_book: get_f64("priceToBook"),
price_to_sales_trailing12_months: None,
forward_pe: get_f64("forwardPE"),
trailing_pe: get_f64("trailingPE"),
beta: None,
fifty_two_week_high: get_f64("fiftyTwoWeekHigh"),
fifty_two_week_low: get_f64("fiftyTwoWeekLow"),
fifty_day_average: get_f64("fiftyDayAverage"),
two_hundred_day_average: get_f64("twoHundredDayAverage"),
week_52_change: get_f64("52WeekChange"),
sand_p_52_week_change: None,
dividend_rate: get_f64("dividendRate"),
dividend_yield: get_f64("dividendYield"),
trailing_annual_dividend_rate: get_f64("trailingAnnualDividendRate"),
trailing_annual_dividend_yield: get_f64("trailingAnnualDividendYield"),
five_year_avg_dividend_yield: None,
ex_dividend_date: None,
payout_ratio: None,
last_dividend_value: None,
last_dividend_date: None,
bid: get_f64("bid"),
bid_size: get_i64("bidSize"),
ask: get_f64("ask"),
ask_size: get_i64("askSize"),
shares_outstanding: get_i64("sharesOutstanding"),
float_shares: None,
implied_shares_outstanding: None,
held_percent_insiders: None,
held_percent_institutions: None,
shares_short: None,
shares_short_prior_month: None,
short_ratio: None,
short_percent_of_float: None,
shares_percent_shares_out: None,
date_short_interest: None,
current_price: None,
target_high_price: None,
target_low_price: None,
target_mean_price: None,
target_median_price: None,
recommendation_mean: None,
recommendation_key: None,
number_of_analyst_opinions: None,
total_cash: None,
total_cash_per_share: None,
ebitda: None,
total_debt: None,
total_revenue: None,
net_income_to_common: None,
debt_to_equity: None,
revenue_per_share: None,
return_on_assets: None,
return_on_equity: None,
free_cashflow: None,
operating_cashflow: None,
profit_margins: None,
gross_margins: None,
ebitda_margins: None,
operating_margins: None,
gross_profits: None,
earnings_growth: None,
revenue_growth: None,
earnings_quarterly_growth: None,
current_ratio: None,
quick_ratio: None,
trailing_eps: get_f64("trailingEps"),
forward_eps: get_f64("forwardEps"),
book_value: get_f64("bookValue"),
sector: get_str("sector"),
sector_key: get_str("sectorKey"),
sector_disp: get_str("sectorDisp"),
industry: get_str("industry"),
industry_key: get_str("industryKey"),
industry_disp: get_str("industryDisp"),
long_business_summary: None,
website: None,
ir_website: None,
address1: None,
city: None,
state: None,
zip: None,
country: None,
phone: None,
full_time_employees: None,
category: None,
fund_family: None,
audit_risk: None,
board_risk: None,
compensation_risk: None,
shareholder_rights_risk: None,
overall_risk: None,
time_zone_full_name: get_str("exchangeTimezoneName"),
time_zone_short_name: get_str("exchangeTimezoneShortName"),
gmt_off_set_milliseconds: json.get("gmtOffSetMilliseconds").and_then(|v| v.as_i64()),
first_trade_date_epoch_utc: json
.get("firstTradeDateMilliseconds")
.and_then(|v| v.as_i64()),
message_board_id: get_str("messageBoardId"),
exchange_data_delayed_by: json
.get("exchangeDataDelayedBy")
.and_then(|v| v.as_i64())
.map(|v| v as i32),
nav_price: get_f64("navPrice"),
total_assets: get_i64("totalAssets"),
yield_value: get_f64("yield"),
last_split_factor: None,
last_split_date: None,
last_fiscal_year_end: None,
next_fiscal_year_end: None,
most_recent_quarter: None,
price_hint: get_i64("priceHint"),
tradeable: json.get("tradeable").and_then(|v| v.as_bool()),
financial_currency: get_str("financialCurrency"),
company_officers: None,
earnings: None,
calendar_events: None,
recommendation_trend: None,
upgrade_downgrade_history: None,
earnings_history: None,
earnings_trend: None,
insider_holders: None,
insider_transactions: None,
institution_ownership: None,
fund_ownership: None,
major_holders_breakdown: None,
net_share_purchase_activity: None,
sec_filings: None,
balance_sheet_history: None,
balance_sheet_history_quarterly: None,
cashflow_statement_history: None,
cashflow_statement_history_quarterly: None,
income_statement_history: None,
income_statement_history_quarterly: None,
equity_performance: None,
index_trend: None,
industry_trend: None,
sector_trend: None,
fund_profile: None,
fund_performance: None,
top_holdings: None,
})
}
}