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
|
import pandas as pd
import pytest
from app import main
from app.services import data_service
def quarterly_frame(rows: dict[str, list[float]]) -> pd.DataFrame:
columns = pd.to_datetime(["2025-12-31", "2025-09-30", "2025-06-30", "2025-03-31"])
return pd.DataFrame(rows, index=columns).T
def test_ticker_wacc_computes_capm_wacc_when_data_is_available(monkeypatch: pytest.MonkeyPatch) -> None:
# Given: WACC inputs matching the reference Excel sheet.
data_service.WACC_CACHE.clear()
monkeypatch.setattr(
data_service,
"get_company_info",
lambda symbol: {"beta": 0.98, "marketCap": 45_500_000_000.0},
)
monkeypatch.setattr(data_service, "get_fast_info", lambda symbol: {})
monkeypatch.setattr(
data_service,
"get_balance_sheet",
lambda symbol, quarterly=False: quarterly_frame(
{
"Total Debt": [724_000_000.0] * 4,
"Cash And Cash Equivalents": [795_000_000.0] * 4,
}
),
)
monkeypatch.setattr(
data_service,
"get_income_statement",
lambda symbol, quarterly=False: quarterly_frame(
{
"Interest Expense": [8_145_000.0] * 4,
"Tax Provision": [25_000_000.0] * 4,
"Pretax Income": [100_000_000.0] * 4,
}
),
)
# When: the public route function is called.
result = main.ticker_wacc("ref")
# Then: it returns the CAPM WACC and all major inputs without live yfinance calls.
assert result["symbol"] == "REF"
assert result["available"] is True
assert result["wacc"] == pytest.approx(0.0979)
assert result["cost_of_equity"] == pytest.approx(0.0979)
assert result["cost_of_debt"] == pytest.approx(0.045)
assert result["tax_rate"] == pytest.approx(0.25)
assert result["currency"] == "USD"
assert result["currency_warning"] is None
assert result["market_cap"] == 45_500_000_000.0
assert result["total_debt"] == 724_000_000.0
assert result["cash"] == 795_000_000.0
def test_ticker_wacc_returns_unavailable_when_market_cap_is_missing(monkeypatch: pytest.MonkeyPatch) -> None:
# Given: yfinance helpers return no usable capitalization data.
data_service.WACC_CACHE.clear()
monkeypatch.setattr(data_service, "get_company_info", lambda symbol: {})
monkeypatch.setattr(data_service, "get_fast_info", lambda symbol: {})
monkeypatch.setattr(data_service, "get_balance_sheet", lambda symbol, quarterly=False: pd.DataFrame())
monkeypatch.setattr(data_service, "get_income_statement", lambda symbol, quarterly=False: pd.DataFrame())
# When: the public route function is called for an invalid symbol.
result = main.ticker_wacc("missing")
# Then: the API responds gracefully with fallback assumptions and no WACC.
assert result["symbol"] == "MISSING"
assert result["available"] is False
assert result["wacc"] is None
assert result["risk_free_rate"] == 0.044
assert result["beta"] == 1.0
assert result["equity_risk_premium"] == 0.055
assert result["cost_of_debt"] == 0.05
assert result["tax_rate"] == 0.21
assert result["currency"] == "USD"
assert result["currency_warning"] is None
assert result["market_cap"] is None
def test_wacc_currency_warning_when_mismatch(monkeypatch: pytest.MonkeyPatch) -> None:
data_service.WACC_CACHE.clear()
monkeypatch.setattr(
data_service,
"get_company_info",
lambda symbol: {"financialCurrency": "CAD", "currency": "USD", "marketCap": 45_500_000_000.0, "beta": 0.98},
)
monkeypatch.setattr(data_service, "get_fast_info", lambda symbol: {"currency": "USD"})
monkeypatch.setattr(data_service, "get_balance_sheet", lambda symbol, quarterly=False: pd.DataFrame())
monkeypatch.setattr(data_service, "get_income_statement", lambda symbol, quarterly=False: pd.DataFrame())
result = main.ticker_wacc("CCJ")
assert result["currency"] == "CAD"
assert isinstance(result["currency_warning"], str)
assert "USD" in result["currency_warning"]
assert "CAD" in result["currency_warning"]
|