summaryrefslogtreecommitdiff
path: root/backend/app/services/wacc_service.py
blob: 2b4ee3e3f8a3f1fc8c92111d161e2f101311d18e (plain)
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
from __future__ import annotations

from typing import Final

from app.services import data_service

DEFAULT_RISK_FREE_RATE: Final = 0.044
DEFAULT_BETA: Final = 1.0
DEFAULT_EQUITY_RISK_PREMIUM: Final = 0.055
DEFAULT_COST_OF_DEBT: Final = 0.05
DEFAULT_TAX_RATE: Final = 0.21


def _unavailable(sym: str) -> dict[str, str | bool | float | None]:
    info = data_service.get_company_info(sym)
    fast_info = data_service.get_fast_info(sym)
    currency_metadata = data_service._currency_metadata(info, fast_info, prefer_reporting=True)

    return {
        "symbol": sym,
        "available": False,
        "wacc": None,
        "cost_of_equity": None,
        "after_tax_cost_of_debt": None,
        "risk_free_rate": DEFAULT_RISK_FREE_RATE,
        "beta": DEFAULT_BETA,
        "equity_risk_premium": DEFAULT_EQUITY_RISK_PREMIUM,
        "cost_of_debt": DEFAULT_COST_OF_DEBT,
        "tax_rate": DEFAULT_TAX_RATE,
        "market_cap": None,
        "total_debt": None,
        "cash": None,
        "net_debt": None,
        "equity_weight": None,
        "debt_weight": None,
        **currency_metadata,
    }


def compute_wacc_value(symbol: str) -> dict[str, str | bool | float | None]:
    sym = data_service.normalize_symbol(symbol)
    info = data_service.get_company_info(sym)
    fast_info = data_service.get_fast_info(sym)
    balance = data_service.get_balance_sheet(sym, quarterly=True)
    income = data_service.get_income_statement(sym, quarterly=True)

    market_cap = data_service._safe_float(info.get("marketCap")) or data_service._safe_float(
        fast_info.get("marketCap")
    )
    if market_cap is None or market_cap <= 0:
        return _unavailable(sym)

    beta = data_service._safe_float(info.get("beta"))
    if beta is None:
        beta = DEFAULT_BETA

    total_debt = data_service._balance_value(
        balance,
        "Total Debt",
        "Long Term Debt And Capital Lease Obligation",
    )
    if total_debt is None:
        total_debt = data_service._safe_float(info.get("totalDebt")) or 0.0

    cash = data_service._balance_value(
        balance,
        "Cash And Cash Equivalents",
        "Cash Cash Equivalents And Short Term Investments",
    )
    if cash is None:
        cash = data_service._safe_float(info.get("totalCash")) or 0.0

    interest_expense = data_service._statement_ttm(income, "Interest Expense")
    cost_of_debt = DEFAULT_COST_OF_DEBT
    if interest_expense is not None and total_debt > 0:
        computed_cost_of_debt = abs(interest_expense) / total_debt
        if 0 < computed_cost_of_debt < 1:
            cost_of_debt = computed_cost_of_debt

    tax_rate = DEFAULT_TAX_RATE
    tax_provision = data_service._statement_ttm(income, "Tax Provision")
    pretax_income = data_service._statement_ttm(income, "Pretax Income")
    if tax_provision is not None and pretax_income is not None and pretax_income > 0:
        computed_tax_rate = tax_provision / pretax_income
        if 0 <= computed_tax_rate <= 1:
            tax_rate = computed_tax_rate

    risk_free_rate = DEFAULT_RISK_FREE_RATE
    equity_risk_premium = DEFAULT_EQUITY_RISK_PREMIUM
    cost_of_equity = risk_free_rate + (beta * equity_risk_premium)
    after_tax_cost_of_debt = cost_of_debt * (1 - tax_rate)
    net_debt = max(total_debt - cash, 0.0)
    total_capital = market_cap + net_debt
    if total_capital <= 0:
        return _unavailable(sym)

    equity_weight = market_cap / total_capital
    debt_weight = net_debt / total_capital
    wacc = (cost_of_equity * equity_weight) + (after_tax_cost_of_debt * debt_weight)
    currency_metadata = data_service._currency_metadata(info, fast_info, prefer_reporting=True)

    return {
        "symbol": sym,
        "available": True,
        "wacc": wacc,
        "cost_of_equity": cost_of_equity,
        "after_tax_cost_of_debt": after_tax_cost_of_debt,
        "risk_free_rate": risk_free_rate,
        "beta": beta,
        "equity_risk_premium": equity_risk_premium,
        "cost_of_debt": cost_of_debt,
        "tax_rate": tax_rate,
        "market_cap": market_cap,
        "total_debt": total_debt,
        "cash": cash,
        "net_debt": net_debt,
        "equity_weight": equity_weight,
        "debt_weight": debt_weight,
        **currency_metadata,
    }