Introduction
The WACC (Weighted Average Cost of Capital) is a company's weighted average cost of capital, essential for DCF valuations, investment decisions, and financial analysis. It combines the cost of equity (via CAPM) and the cost of debt, weighted by their proportions in the balance sheet.
Why automate it in Python? Spreadsheets like Excel fall short on real-time data (beta, risk-free rates). With yfinance to fetch stock data and Streamlit for a web interface, you get a scalable, accurate, shareable tool. This intermediate tutorial guides you step by step: from the theoretical formula to deploying a full app. By the end, calculate WACC for any listed stock in 30 seconds. Time savings: up to 80% vs. manual methods. (128 words)
Prerequisites
- Python 3.10+
- pip install streamlit yfinance pandas numpy
- Basic finance knowledge (CAPM, balance sheet)
- Free Yahoo Finance account (optional for API)
- VS Code or PyCharm for editing
Installing Dependencies
pip install streamlit yfinance pandas numpy
streamlit helloInstall the essential libraries: Streamlit for the interface, yfinance for stock data, Pandas/NumPy for calculations. Test with streamlit hello to verify installation. Use a virtualenv to avoid conflicts.
Breaking Down the WACC Formula
WACC = (E/V) × Re + (D/V) × Rd × (1 - Tc)
- E: Equity value (market cap)
- D: Net debt (enter manually)
- V: E + D
- Re: Cost of equity = Rf + β × (Rm - Rf) (CAPM)
- Rd: Cost of debt (interest rate)
- Tc: Effective tax rate
CAPM Function for Cost of Equity
import yfinance as yf
import numpy as np
def capm_cost_equity(ticker, risk_free_rate=0.043, market_premium=0.06):
stock = yf.Ticker(ticker)
beta = stock.info.get('beta', 1.0)
re = risk_free_rate + beta * market_premium
return re, beta
# Exemple
re, beta = capm_cost_equity('AAPL')
print(f"Beta: {beta:.2f}, Re: {re:.2%}")This function fetches beta via yfinance and calculates Re using Rf=4.3% (US 10Y 2026) and market premium=6%. Pitfall: missing beta → fallback to 1.0. Test with AAPL to validate (beta ~1.2, Re ~11%).
Cost of Debt and WACC Functions
def cost_debt(rating='BBB'):
rates = {'AAA': 0.035, 'AA': 0.038, 'A': 0.042, 'BBB': 0.048}
return rates.get(rating, 0.05)
def calculate_wacc(equity_value, debt_value, re, rd, tax_rate=0.21):
total_value = equity_value + debt_value
weight_equity = equity_value / total_value
weight_debt = debt_value / total_value
wacc = (weight_equity * re) + (weight_debt * rd * (1 - tax_rate))
return wacc
# Exemple statique
re = 0.11
rd = cost_debt('A')
wacc = calculate_wacc(1000, 400, re, rd)
print(f"WACC: {wacc:.2%}")cost_debt uses a rating-based lookup (Moody's approx 2026). WACC weights precisely. Pitfall: debt=0 → avoids div/0 implicitly, but validate V>0. Example: E=1000B$, D=400B$ → WACC ~8.5%.
Integrating yfinance Data
Combine CAPM with market cap. Debt: enter from 10-K (not always in API). Dynamic Rf via '^TNX' (10Y Treasury), fixed or historical Rm-Rf.
Main Script with Market Cap Fetch
import yfinance as yf
from capm import capm_cost_equity # Import des fonctions précédentes
from wacc_functions import cost_debt, calculate_wacc
def full_wacc(ticker, debt_value, rating='BBB', tax_rate=0.21):
stock = yf.Ticker(ticker)
equity_value = stock.info.get('marketCap', 0) / 1e9 # En milliards
rf = yf.download('^TNX', period='1d')['Close'].iloc[-1] / 100
re, beta = capm_cost_equity(ticker, rf)
rd = cost_debt(rating)
wacc = calculate_wacc(equity_value, debt_value, re, rd, tax_rate)
return {
'ticker': ticker,
'equity': equity_value,
'debt': debt_value,
're': re,
'rd': rd,
'wacc': wacc
}
# Usage
result = full_wacc('MSFT', debt_value=800)
print(result)Full script: fetches marketCap, live Rf, computes everything. Pitfall: marketCap in USD, divide by 1e9 for billions; handle NaN with .get(). Example MSFT: equity ~3T$, debt~800B$, WACC ~7-9%.
Complete Streamlit App
import streamlit as st
import yfinance as yf
import numpy as np
from capm import capm_cost_equity
from wacc_functions import cost_debt, calculate_wacc
def full_wacc(ticker, debt_value, rating, tax_rate):
stock = yf.Ticker(ticker)
equity_value = stock.info.get('marketCap', 0) / 1e9
rf = yf.download('^TNX', period='1d')['Close'].iloc[-1] / 100
re, beta = capm_cost_equity(ticker, rf)
rd = cost_debt(rating)
wacc = calculate_wacc(equity_value, debt_value, re, rd, tax_rate)
return {
'Equity (B$)': equity_value,
'Debt (B$)': debt_value,
'Beta': beta,
'Re (%)': re * 100,
'Rd (%)': rd * 100,
'Tax Rate (%)': tax_rate * 100,
'WACC (%)': wacc * 100
}
st.title('Calculateur WACC 2026')
col1, col2 = st.columns(2)
with col1:
ticker = st.text_input('Ticker (ex: AAPL)', 'AAPL')
debt = st.number_input('Dette nette (B$)', min_value=0.0, value=500.0)
with col2:
rating = st.selectbox('Rating dette', ['AAA', 'AA', 'A', 'BBB'])
tax = st.number_input('Taux impôt (%)', min_value=0.0, max_value=0.5, value=0.21)
if st.button('Calculer WACC'):
with st.spinner('Fetch données...'):
result = full_wacc(ticker, debt, rating, tax)
st.success('WACC calculé !')
st.metric('WACC', f"{result['WACC (%)']:.2f}%")
st.table(result)
st.caption('Données live via yfinance. Mise à jour 2026.')Full-stack Streamlit app: interactive inputs, live fetches, results table, visual metric. Run with streamlit run app.py. Pitfall: yfinance timeouts → add try/except in production; use localtunnel to share.
Best Practices
- Validate data: Always cross-check beta/marketCap manually vs. Yahoo (discrepancies ~5%).
- Dynamic Rf: Use '^TNX' or ECB for Europe.
- Sensitivity: Add Monte Carlo sliders for ±1% beta.
- Cache API calls: Use
@st.cache_dataon full_wacc for performance. - Deploy on Streamlit Cloud: Free, auto HTTPS.
Common Errors to Avoid
- Ignoring debt: Without D, WACC=Re (overvalues). Always estimate from balance sheets.
- Static Rf: Ignores 2026 inflation (~3%) → underestimates Re.
- Unadjusted beta: Raw from Yahoo; adjust for levered/unlevered.
- Inconsistent units: MarketCap in shares*price, debt absolute → unify to billions.
Next Steps
Integrate Black-Scholes for warrants/options or full DCF. Resources: