blob: 58d34b4f1bcdcecf5cc6793f61b5ccd3a154331c (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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,
)
|