aboutsummaryrefslogtreecommitdiff
path: root/services/data_service.py
blob: fa9b02679d246c6a10ebe97ba2a8974537a00230 (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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
"""yfinance wrapper — price history, financial statements, company info."""
import yfinance as yf
import pandas as pd
import streamlit as st


@st.cache_data(ttl=60)
def search_tickers(query: str) -> list[dict]:
    """Search for tickers by company name or symbol. Returns list of {symbol, name, exchange}."""
    if not query or len(query.strip()) < 2:
        return []
    try:
        results = yf.Search(query.strip(), max_results=8).quotes
        out = []
        for r in results:
            symbol = r.get("symbol", "")
            name = r.get("longname") or r.get("shortname") or symbol
            exchange = r.get("exchange") or r.get("exchDisp", "")
            if symbol:
                out.append({"symbol": symbol, "name": name, "exchange": exchange})
        return out
    except Exception:
        return []


@st.cache_data(ttl=300)
def get_company_info(ticker: str) -> dict:
    """Return company info dict from yfinance."""
    t = yf.Ticker(ticker.upper())
    info = t.info or {}
    return info


@st.cache_data(ttl=300)
def get_price_history(ticker: str, period: str = "1y") -> pd.DataFrame:
    """Return OHLCV price history."""
    t = yf.Ticker(ticker.upper())
    df = t.history(period=period)
    df.index = pd.to_datetime(df.index)
    return df


@st.cache_data(ttl=3600)
def get_income_statement(ticker: str, quarterly: bool = False) -> pd.DataFrame:
    t = yf.Ticker(ticker.upper())
    df = t.quarterly_income_stmt if quarterly else t.income_stmt
    return df if df is not None else pd.DataFrame()


@st.cache_data(ttl=3600)
def get_balance_sheet(ticker: str, quarterly: bool = False) -> pd.DataFrame:
    t = yf.Ticker(ticker.upper())
    df = t.quarterly_balance_sheet if quarterly else t.balance_sheet
    return df if df is not None else pd.DataFrame()


@st.cache_data(ttl=3600)
def get_cash_flow(ticker: str, quarterly: bool = False) -> pd.DataFrame:
    t = yf.Ticker(ticker.upper())
    df = t.quarterly_cashflow if quarterly else t.cashflow
    return df if df is not None else pd.DataFrame()


@st.cache_data(ttl=300)
def get_market_indices() -> dict:
    """Return latest price + day change % for major indices."""
    symbols = {
        "S&P 500": "^GSPC",
        "NASDAQ": "^IXIC",
        "DOW": "^DJI",
        "VIX": "^VIX",
    }
    result = {}
    for name, sym in symbols.items():
        try:
            t = yf.Ticker(sym)
            hist = t.history(period="2d")
            if len(hist) >= 2:
                prev_close = hist["Close"].iloc[-2]
                last = hist["Close"].iloc[-1]
                pct_change = (last - prev_close) / prev_close
            elif len(hist) == 1:
                last = hist["Close"].iloc[-1]
                pct_change = 0.0
            else:
                result[name] = {"price": None, "change_pct": None}
                continue
            result[name] = {"price": float(last), "change_pct": float(pct_change)}
        except Exception:
            result[name] = {"price": None, "change_pct": None}
    return result


@st.cache_data(ttl=3600)
def get_free_cash_flow_series(ticker: str) -> pd.Series:
    """Return annual Free Cash Flow series (most recent first)."""
    t = yf.Ticker(ticker.upper())
    cf = t.cashflow
    if cf is None or cf.empty:
        return pd.Series(dtype=float)
    if "Free Cash Flow" in cf.index:
        return cf.loc["Free Cash Flow"].dropna()
    # Compute from operating CF - capex
    try:
        op = cf.loc["Operating Cash Flow"]
        capex = cf.loc["Capital Expenditure"]
        return (op + capex).dropna()
    except KeyError:
        return pd.Series(dtype=float)