diff options
| author | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-18 01:32:48 -0700 |
|---|---|---|
| committer | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-18 01:32:48 -0700 |
| commit | 0662a31869ad60dbb76b2f534fe294d15b3b3492 (patch) | |
| tree | 6328b0bc7275e403fd39808b7af4824fca857ee5 | |
| parent | b877047a76cd4b661dccfd1dc8c0e7f2aa6a346c (diff) | |
feat: add GET /api/tickers/{symbol}/valuation route
| -rw-r--r-- | backend/app/main.py | 7 | ||||
| -rw-r--r-- | backend/tests/test_api.py | 43 |
2 files changed, 49 insertions, 1 deletions
diff --git a/backend/app/main.py b/backend/app/main.py index eb5fa40..1cc127e 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -9,7 +9,7 @@ from fastapi import FastAPI, HTTPException, Query, status from fastapi.middleware.cors import CORSMiddleware from app.db import watchlist -from app.schemas import FinancialsResponse, HistoryPoint, MarketIndex, SearchResult, TickerOverview, WatchlistResponse +from app.schemas import FinancialsResponse, HistoryPoint, MarketIndex, SearchResult, TickerOverview, ValuationResponse, WatchlistResponse from app.services import data_service load_dotenv() @@ -70,6 +70,11 @@ def ticker_financials(symbol: str, period: str = Query(default="annual", pattern return data_service.get_financials(symbol, period=period) +@app.get("/api/tickers/{symbol}/valuation", response_model=ValuationResponse) +def ticker_valuation(symbol: str) -> dict: + return data_service.get_valuation(symbol) + + @app.get("/api/watchlist", response_model=WatchlistResponse) def get_watchlist() -> dict: items = [] diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 66c021f..af43975 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -897,3 +897,46 @@ def test_get_valuation_missing_multiples_data(monkeypatch) -> None: assert result["ev_ebitda"]["available"] is False assert result["ev_revenue"]["available"] is False assert result["price_to_book"]["available"] is False + + +def test_valuation_route_returns_structure(monkeypatch) -> None: + monkeypatch.setattr( + main.data_service, + "get_valuation", + lambda symbol: { + "symbol": "AAPL", + "current_price": 150.0, + "shares_outstanding": 15_000_000_000.0, + "dcf": { + "available": True, + "intrinsic_value_per_share": 182.0, + "enterprise_value": 2_800_000_000_000.0, + "equity_value": 2_750_000_000_000.0, + "net_debt": 50_000_000_000.0, + "cash_and_equivalents": 100_000_000_000.0, + "total_debt": 150_000_000_000.0, + "terminal_value_pv": 2_000_000_000_000.0, + "fcf_pv_sum": 800_000_000_000.0, + "growth_rate_used": 0.082, + "base_fcf": 110_000_000_000.0, + "wacc": 0.10, + "terminal_growth": 0.03, + "error": None, + }, + "ev_ebitda": { + "available": True, + "implied_price_per_share": 178.0, + "implied_ev": 2_700_000_000_000.0, + "equity_value": 2_650_000_000_000.0, + "net_debt": 50_000_000_000.0, + "multiple_used": 20.0, + }, + "ev_revenue": {"available": False}, + "price_to_book": {"available": False}, + }, + ) + result = main.ticker_valuation("AAPL") + assert result["symbol"] == "AAPL" + assert result["dcf"]["intrinsic_value_per_share"] == 182.0 + assert result["ev_ebitda"]["multiple_used"] == 20.0 + assert result["ev_revenue"]["available"] is False |
