From 0bcc5be35b6a99109bd04925c22890347c9d6e88 Mon Sep 17 00:00:00 2001 From: Mikka Petterson Date: Wed, 14 Oct 2026 07:45:20 +0200 Subject: Initial commit --- src/teradata/stock.py | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/teradata/stock.py (limited to 'src/teradata/stock.py') diff --git a/src/teradata/stock.py b/src/teradata/stock.py new file mode 100644 index 0000000..58d34b4 --- /dev/null +++ b/src/teradata/stock.py @@ -0,0 +1,62 @@ +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, + ) -- cgit v1.2.3-70-g09d2