diff options
Diffstat (limited to 'backend/app/services/data_service.py')
| -rw-r--r-- | backend/app/services/data_service.py | 48 |
1 files changed, 45 insertions, 3 deletions
diff --git a/backend/app/services/data_service.py b/backend/app/services/data_service.py index 220accc..e356cb4 100644 --- a/backend/app/services/data_service.py +++ b/backend/app/services/data_service.py @@ -645,8 +645,17 @@ def compute_historical_ratios(symbol: str) -> dict[str, list[float | None]]: return {} years = list(inc_a.columns[: min(len(inc_a.columns), 4)]) - shares = get_shares_outstanding(sym) price_history = get_price_history(sym, period="5y") + current_shares = get_shares_outstanding(sym) + + try: + shares_history_raw = yf.Ticker(sym).get_shares_full(start="2000-01-01") + if isinstance(shares_history_raw, pd.Series): + shares_history = pd.to_numeric(shares_history_raw, errors="coerce").dropna().sort_index() + else: + shares_history = pd.Series(dtype=float) + except Exception: + shares_history = pd.Series(dtype=float) result: dict[str, list[float | None]] = {k: [] for k in [ "gross_margin", "operating_margin", "net_margin", "ebitda_margin", @@ -654,6 +663,38 @@ def compute_historical_ratios(symbol: str) -> dict[str, list[float | None]]: "trailing_pe", "ev_to_ebitda", "price_to_book", "price_to_sales", ]} + def _balance_shares(period_date: pd.Timestamp) -> float | None: + if bal_a is None or bal_a.empty or period_date not in bal_a.columns: + return None + for label in _SHARE_LABELS: + if label not in bal_a.index: + continue + shares_value = _safe_float(bal_a.loc[label, period_date]) + if shares_value is not None and shares_value > 0: + return shares_value + return None + + def _historical_shares_for_date(period_date: pd.Timestamp) -> float | None: + direct_balance_shares = _balance_shares(period_date) + if direct_balance_shares is not None: + return direct_balance_shares + if not shares_history.empty: + target = pd.Timestamp(period_date) + index = shares_history.index + if getattr(index, "tz", None) is not None and target.tzinfo is None: + target = target.tz_localize(index.tz) + elif getattr(index, "tz", None) is None and target.tzinfo is not None: + target = target.tz_localize(None) + + deltas = pd.Series(index - target, index=index).abs() + if not deltas.empty: + nearest_idx = deltas.idxmin() + if abs(pd.Timestamp(nearest_idx) - target) <= pd.Timedelta(days=180): + shares_value = _safe_float(shares_history.loc[nearest_idx]) + if shares_value is not None and shares_value > 0: + return shares_value + return current_shares + for col in years: col_dt = pd.Timestamp(col) @@ -674,10 +715,11 @@ def compute_historical_ratios(symbol: str) -> dict[str, list[float | None]]: ebitda = _inc("EBITDA") or _inc("Normalized EBITDA") equity = _bal("Stockholders Equity") or _bal("Common Stock Equity") total_assets = _bal("Total Assets") - total_debt = _bal("Total Debt") + total_debt = _bal("Total Debt") or _bal("Long Term Debt And Capital Lease Obligation") current_assets = _bal("Current Assets") current_liabilities = _bal("Current Liabilities") cash = _bal("Cash And Cash Equivalents") or _bal("Cash Cash Equivalents And Short Term Investments") or 0.0 + period_shares = _historical_shares_for_date(col_dt) rev = revenue if revenue and revenue > 0 else None result["gross_margin"].append(_cap_ratio(gross_profit / rev, -5, 5) if rev and gross_profit is not None else None) @@ -690,7 +732,7 @@ def compute_historical_ratios(symbol: str) -> dict[str, list[float | None]]: result["current_ratio"].append(current_assets / current_liabilities if current_liabilities and current_liabilities > 0 and current_assets is not None else None) price = _find_price_at_date(price_history, col_dt) - market_cap = price * shares if price and shares else None + market_cap = price * period_shares if price and period_shares else None ev = market_cap + (total_debt or 0.0) - cash if market_cap else None result["trailing_pe"].append(_cap_ratio(market_cap / net_income, 0, 500) if market_cap and net_income and net_income > 0 else None) |
