diff options
| author | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-18 02:21:03 -0700 |
|---|---|---|
| committer | Tyler Hoang <tyler@tylerhoang.xyz> | 2026-05-18 02:21:03 -0700 |
| commit | b24b745dbb047674c1ac05f2531cab83f45c2291 (patch) | |
| tree | d229f0f40b5567c8dca61d13228a4920fa2f59bf /backend/tests/test_api.py | |
| parent | ce1b9bcab6474f678155da1b6f0133bb6800346e (diff) | |
Add ratios service and tests
Diffstat (limited to 'backend/tests/test_api.py')
| -rw-r--r-- | backend/tests/test_api.py | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index af43975..94c9c20 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -19,6 +19,8 @@ def clear_service_caches() -> None: data_service.RATIO_CACHE.clear() data_service.FINANCIALS_CACHE.clear() data_service.VALUATION_CACHE.clear() + data_service.HIST_RATIOS_CACHE.clear() + data_service.RATIOS_ENDPOINT_CACHE.clear() def quarterly_frame(rows: dict[str, list[float]]) -> pd.DataFrame: @@ -940,3 +942,133 @@ def test_valuation_route_returns_structure(monkeypatch) -> None: assert result["dcf"]["intrinsic_value_per_share"] == 182.0 assert result["ev_ebitda"]["multiple_used"] == 20.0 assert result["ev_revenue"]["available"] is False + + +def test_compute_historical_ratios_margins(monkeypatch) -> None: + clear_service_caches() + monkeypatch.setattr( + data_service, + "get_income_statement", + lambda symbol, quarterly=False: annual_frame( + { + "Total Revenue": [100.0, 90.0, 80.0, 70.0], + "Gross Profit": [50.0, 45.0, 40.0, 35.0], + "Operating Income": [20.0, 18.0, 16.0, 14.0], + "Net Income": [10.0, 9.0, 8.0, 7.0], + "EBITDA": [25.0, 22.5, 20.0, 17.5], + } + ), + ) + monkeypatch.setattr( + data_service, + "get_balance_sheet", + lambda symbol, quarterly=False: annual_frame( + { + "Stockholders Equity": [50.0, 45.0, 40.0, 35.0], + "Total Assets": [100.0, 90.0, 80.0, 70.0], + "Total Debt": [20.0, 18.0, 16.0, 14.0], + "Current Assets": [30.0, 27.0, 24.0, 21.0], + "Current Liabilities": [15.0, 13.5, 12.0, 10.5], + "Cash And Cash Equivalents": [5.0, 4.5, 4.0, 3.5], + } + ), + ) + monkeypatch.setattr(data_service, "get_cash_flow", lambda symbol, quarterly=False: annual_frame({"Operating Cash Flow": [1.0, 1.0, 1.0, 1.0]})) + monkeypatch.setattr(data_service, "get_shares_outstanding", lambda symbol: 1_000.0) + monkeypatch.setattr(data_service, "get_price_history", lambda symbol, period="5y": []) + + result = data_service.compute_historical_ratios("AAPL") + + assert result["gross_margin"] == [0.5, 0.5, 0.5, 0.5] + assert result["operating_margin"] == [0.2, 0.2, 0.2, 0.2] + assert result["net_margin"] == [0.1, 0.1, 0.1, 0.1] + assert result["ebitda_margin"] == [0.25, 0.25, 0.25, 0.25] + assert result["roe"] == [0.2, 0.2, 0.2, 0.2] + assert result["roa"] == [0.1, 0.1, 0.1, 0.1] + assert result["debt_to_equity"] == [0.4, 0.4, 0.4, 0.4] + assert result["current_ratio"] == [2.0, 2.0, 2.0, 2.0] + assert result["trailing_pe"] == [None, None, None, None] + assert result["ev_to_ebitda"] == [None, None, None, None] + assert result["price_to_book"] == [None, None, None, None] + assert result["price_to_sales"] == [None, None, None, None] + + +def test_compute_historical_ratios_empty_income(monkeypatch) -> None: + clear_service_caches() + monkeypatch.setattr(data_service, "get_income_statement", lambda symbol, quarterly=False: pd.DataFrame()) + monkeypatch.setattr(data_service, "get_balance_sheet", lambda symbol, quarterly=False: pd.DataFrame()) + monkeypatch.setattr(data_service, "get_cash_flow", lambda symbol, quarterly=False: pd.DataFrame()) + monkeypatch.setattr(data_service, "get_shares_outstanding", lambda symbol: None) + monkeypatch.setattr(data_service, "get_price_history", lambda symbol, period="5y": []) + + result = data_service.compute_historical_ratios("AAPL") + + assert result == {} + + +def test_get_ratios_quick_ratio(monkeypatch) -> None: + clear_service_caches() + monkeypatch.setattr(data_service, "compute_ttm_ratios", lambda symbol: {"market_cap": 1_000.0, "current_ratio": 2.0}) + monkeypatch.setattr( + data_service, + "compute_historical_ratios", + lambda symbol: { + "current_ratio": [1.4, 1.5, 1.6, 1.8], + }, + ) + monkeypatch.setattr(data_service, "get_company_info", lambda symbol: {}) + monkeypatch.setattr( + data_service, + "get_income_statement", + lambda symbol, quarterly=True: quarterly_frame( + { + "Total Revenue": [100.0, 100.0, 100.0, 100.0], + } + ), + ) + monkeypatch.setattr( + data_service, + "get_balance_sheet", + lambda symbol, quarterly=True: quarterly_frame( + { + "Current Assets": [200.0, 0.0, 0.0, 0.0], + "Current Liabilities": [100.0, 0.0, 0.0, 0.0], + "Inventory": [30.0, 0.0, 0.0, 0.0], + } + ), + ) + monkeypatch.setattr(data_service, "get_cash_flow", lambda symbol, quarterly=True: quarterly_frame({"Operating Cash Flow": [1.0, 1.0, 1.0, 1.0]})) + monkeypatch.setattr(data_service, "get_price_history", lambda symbol, period="5y": []) + + result = data_service.get_ratios("AAPL") + + assert result["quick_ratio"]["value"] == 1.7 + assert result["quick_ratio"]["spark"] == [] + assert result["current_ratio"]["value"] == 2.0 + assert result["current_ratio"]["spark"] == [1.4, 1.5, 1.6, 1.8] + + +def test_get_ratios_interest_coverage(monkeypatch) -> None: + clear_service_caches() + monkeypatch.setattr(data_service, "compute_ttm_ratios", lambda symbol: {"market_cap": 1_000.0}) + monkeypatch.setattr(data_service, "compute_historical_ratios", lambda symbol: {}) + monkeypatch.setattr(data_service, "get_company_info", lambda symbol: {}) + monkeypatch.setattr( + data_service, + "get_income_statement", + lambda symbol, quarterly=True: quarterly_frame( + { + "Total Revenue": [100.0, 100.0, 100.0, 100.0], + "EBIT": [42.5, 42.5, 42.5, 42.5], + "Interest Expense": [5.0, 5.0, 5.0, 5.0], + } + ), + ) + monkeypatch.setattr(data_service, "get_balance_sheet", lambda symbol, quarterly=True: quarterly_frame({})) + monkeypatch.setattr(data_service, "get_cash_flow", lambda symbol, quarterly=True: quarterly_frame({"Operating Cash Flow": [1.0, 1.0, 1.0, 1.0]})) + monkeypatch.setattr(data_service, "get_price_history", lambda symbol, period="5y": []) + + result = data_service.get_ratios("AAPL") + + assert result["interest_coverage"]["value"] == 8.5 + assert result["interest_coverage"]["spark"] == [] |
