lazy from typing import Self from attrs import frozen lazy import yfinance lazy from pandas import DataFrame from .cache import persistent @frozen class Tickers: prices: DataFrame @property def normalized(self: Self) -> DataFrame: """Indexed to 100 at t_0.""" return (self.prices / self.prices.iloc[0]) * 100 @property def cumulative_returns(self: Self) -> DataFrame: """Percentage gain/loss since t_0.""" return ((self.prices / self.prices.iloc[0]) - 1) * 100 @frozen class ValuationSnapshot: ticker: str ev_to_sales: float | None = None revenue_growth_yoy: float | None = None ev_to_fcf: float | None = None trailing_pe: float | None = None forward_pe: float | None = None @persistent def get_stock_data(tickers: list[str], start: str) -> DataFrame: data = yfinance.download(tickers, start=start) if data is None: raise ValueError(f"Yahoo Finance data for {tickers} from {start} is None") return data @persistent def get_valuation_snapshot(ticker: str) -> ValuationSnapshot: y_ticker = yfinance.Ticker(ticker) info = y_ticker.info ev = info.get("enterpriseValue") fcf = info.get("freeCashflow") ev_fcf = (ev / fcf) if (ev and fcf and fcf > 0) else None return ValuationSnapshot( ticker=ticker, ev_to_sales=info.get("enterpriseToRevenue"), revenue_growth_yoy=( info.get("revenueGrowth", 0.0) * 100 if info.get("revenueGrowth") is not None else None ), ev_to_fcf=ev_fcf, )