summaryrefslogtreecommitdiff
path: root/src/teradata/stock.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/teradata/stock.py')
-rw-r--r--src/teradata/stock.py62
1 files changed, 62 insertions, 0 deletions
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,
+ )