summaryrefslogtreecommitdiff
path: root/src/teradata/stock.py
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,
    )