summaryrefslogtreecommitdiff
path: root/backend/app
diff options
context:
space:
mode:
Diffstat (limited to 'backend/app')
-rw-r--r--backend/app/services/data_service.py48
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)