
Learn Real Estate Investing from Wharton's Best Minds
In just 8 weeks, learn institutional-grade real estate analysis and modeling from Wharton faculty and seasoned investors.
You’ll gain:
Insider insights on how top firms like Blackstone and KKR evaluate deals
Exclusive invites to recruiting and networking events
Direct access to Wharton faculty and a certificate that signals credibility
Join a thriving community of 5,000+ graduates for ongoing career development, networking, and deal flow.
Use code SAVE300 at checkout to save $300 on tuition.
Program starts February 9.
Elite Quant Plan – 14-Day Free Trial (This Week Only)
No card needed. Cancel anytime. Zero risk.
You get immediate access to:
Full code from every article (including today’s HMM notebook)
Private GitHub repos & templates
All premium deep dives (3–5 per month)
2 × 1-on-1 calls with me
One custom bot built/fixed for you
Try the entire Elite experience for 14 days — completely free.
→ Start your free trial now 👇
(Doors close in 7 days or when the post goes out of the spotlight — whichever comes first.)
See you on the inside.
👉 Upgrade Now →
🔔 Limited-Time Holiday Deal: 20% Off Our Complete 2026 Playbook! 🔔
Level up before the year ends!
AlgoEdge Insights: 30+ Python-Powered Trading Strategies – The Complete 2026 Playbook
30+ battle-tested algorithmic trading strategies from the AlgoEdge Insights newsletter – fully coded in Python, backtested, and ready to deploy. Your full arsenal for dominating 2026 markets.
Special Promo: Use code JANUARY2026 for 20% off
Valid only until January 25, 2025 — act fast!
👇 Buy Now & Save 👇
Instant access to every strategy we've shared, plus exclusive extras.
— AlgoEdge Insights Team
Premium Members – Your Full Notebook Is Ready
The complete Google Colab notebook from today’s article (with live data, full Hidden Markov Model, interactive charts, statistics, and one-click CSV export) is waiting for you.
Preview of what you’ll get:

Inside:
How the Merton Model works in simple terms
How to estimate asset value and default risk using market data
How to apply this model in Python for real-time credit risk assessment.
Beautiful interactive Plotly charts
Regime duration & performance tables
Ready-to-use CSV export
Free readers – you already got the full breakdown and visuals in the article. Paid members – you get the actual tool.
Not upgraded yet? Fix that in 10 seconds here👇
Google Collab Notebook With Full Code Is Available In the End Of The Article Behind The Paywall 👇 (For Paid Subs Only)
Traditional credit ratings are slow to react. The market moves faster. Investors need a real-time measure of financial distress based on market conditions, not outdated financial statements.
The Merton ‘Distance-to-Default’ Model offers a solution. It treats a company’s equity as a call option on its assets and allows us to estimate default risk directly from market prices.
The key output quantifies how close a firm is to financial distress. It’s both a predictive risk tool and an explanatory framework.
End-to-end Implementation Python Notebook provided below.

In this guide, we’ll show:
How the Merton Model works in simple terms
How to estimate asset value and default risk using market data
How to apply this model in Python for real-time credit risk assessment.
But what can you actually DO about the proclaimed ‘AI bubble’? Billionaires know an alternative…
Sure, if you held your stocks since the dotcom bubble, you would’ve been up—eventually. But three years after the dot-com bust the S&P 500 was still far down from its peak. So, how else can you invest when almost every market is tied to stocks?
Lo and behold, billionaires have an alternative way to diversify: allocate to a physical asset class that outpaced the S&P by 15% from 1995 to 2025, with almost no correlation to equities. It’s part of a massive global market, long leveraged by the ultra-wealthy (Bezos, Gates, Rockefellers etc).
Contemporary and post-war art.
Masterworks lets you invest in multimillion-dollar artworks featuring legends like Banksy, Basquiat, and Picasso—without needing millions. Over 70,000 members have together invested more than $1.2 billion across over 500 artworks. So far, 25 sales have delivered net annualized returns like 14.6%, 17.6%, and 17.8%.*
Want access?
Investing involves risk. Past performance not indicative of future returns. Reg A disclosures at masterworks.com/cd
1. The Merton Model
Why Traditional Credit Risk Models Fall Short
Traditional credit risk models rely on accounting-based indicators. Metrics like debt ratios, interest coverage, and credit ratings are at the core.
The problem is that they’re backward-looking. Financial statements are updated quarterly at best, while credit ratings often lag market conditions by months.
By the time distress appears in the books, the market has already reacted.
Market-Based Risk Models: A Better Approach
A firm’s stock price incorporates all ‘available information’. It reacts instantly to new risks, economic shifts, and investor sentiment.
The Merton Model leverages this assumption by treating a company’s equity as a call option on its assets.
Instead of relying on stale financials, the Merton Model extracts risk directly from:
Market capitalization (E) — reflects investor expectations
Stock volatility (σ_E) — captures uncertainty in the firm’s value
Debt levels (D) — provides the default threshold
The Merton Model Explained
Merton’s results: Equity is a call option on the firm’s assets:
Shareholders own the firm’s assets (A).
They must repay debt (D) at maturity (T).
If A > D, they keep the difference. If A < D, they default, and bondholders take over.
This follows the Black-Scholes-Merton option pricing framework, where equity value is modelled follows:

where:

Each term represents:
A = Asset value (firm’s total economic value)
D = Default point (debt obligation)
E = Market value of equity
σA = Asset volatility (uncertainty in firm’s total value)
r = Risk-free rate
N(d) = Cumulative probability from the standard normal distribution
Linking Equity Volatility to Asset Volatility
Since we observe equity (E) in the market but not assets (A) directly, we solve for A and σA using this additional equation:

σE = Observed equity volatility
σA = Unobserved asset volatility (to be estimated)
These two equations form a nonlinear system and can be solved numerically to estimate A and σA.
Distance to Default: Measuring Default Risk
Once we estimate A and σA, we compute Distance to Default. DTD measures of how many standard deviations a firm’s assets are from default:

Higher DTD → If large, the firm has a financial cushion.
Lower DTD → If close to zero, the firm is near default.
2. Implementation in Python
Now, let’s apply the Merton Model in Python to estimate default risk.
Extracting Market Data for Model Inputs
To implement the model, we first need to fetch data on: equity value, stock volatility, and debt levels. We can get this using the yfinance library.
Lets start with Debt. Different approaches to measuring debt can impact the DTD calculation and the firm’s perceived financial risk:
Total Debt: Uses the total reported debt. This assumes that all obligations contribute equally to default risk.
Short-Term + Long-Term Debt: Adds both components directly. Gives a more detailed breakdown but assumes all reported liabilities are fully relevant.
Short-Term + Half of Long-Term Debt: A common approximation method.This reflects that long-term obligations may be partially refinanced or restructured before default.
Via the method argument in the function below, you can adjust for your preferred method.
When choosing the method, consider the following:
A higher debt value (D) leads to a lower DTD. This indicates higher default risk.
A lower debt value may underestimate risk. This makes the firm seem safer than it actually is.
Choosing the appropriate debt measure depends on the context of financial analysis and the firm’s borrowing structure.
# Extract debt from balance sheet with multiple methods
def get_debt_from_balance_sheet(balance_sheet, info, method="STplusHalfLT", fallback=1e6):
"""
Fetches debt based on the selected method.
Methods:
"TOTAL" -> Uses the 'Total Debt' value from the balance sheet.
"STplusHalfLT" -> Short-term debt + 0.5 * long-term debt (default).
"STplusLT" -> Short-term debt + long-term debt.
If debt data is unavailable, falls back to info["totalDebt"] or a predefined fallback value.
Inputs:
balance_sheet = DataFrame containing balance sheet data.
info = Dictionary with company metadata.
method = Debt extraction method.
fallback = Default debt value if no valid data is found.
Returns:
debt_val = Computed debt amount.
"""
debt_val = None
short_debt = 0.0
long_debt = 0.0
total_debt = 0.0
# Extract short-term debt
try:
short_debt = balance_sheet.loc["Short Term Debt"][0]
if not np.isfinite(short_debt) or short_debt < 0:
short_debt = 0.0
except:
pass
# Extract long-term debt
try:
long_debt = balance_sheet.loc["Long Term Debt"][0]
if not np.isfinite(long_debt) or long_debt < 0:
long_debt = 0.0
except:
pass
# Extract total debt
try:
total_debt = balance_sheet.loc["Total Debt"][0]
if not np.isfinite(total_debt) or total_debt < 0:
total_debt = 0.0
except:
pass
# If all values are missing, fallback to info["totalDebt"]
if short_debt == 0.0 and long_debt == 0.0 and total_debt == 0.0:
fallback_debt = info.get("totalDebt", 0)
if fallback_debt is not None and fallback_debt > 0:
total_debt = fallback_debt
# Compute debt based on the selected method
if method.upper() == "TOTAL":
debt_val = total_debt
elif method.upper() == "STPLUSLT":
debt_val = short_debt + long_debt
else: # Default or "STplusHalfLT"
debt_val = short_debt + 0.5 * long_debt
# Final check to ensure a valid debt value
if debt_val is None or debt_val <= 0:
debt_val = fallback
return debt_valDefine the key parameters used in the Merton Model.
The Headlines Traders Need Before the Bell
Tired of missing the trades that actually move?
In under five minutes, Elite Trade Club delivers the top stories, market-moving headlines, and stocks to watch — before the open.
Join 200K+ traders who start with a plan, not a scroll.
Modify these settings to analyze different companies and market conditions. The parameters include:
Ticker Symbol: The stock to analyze.
Time Horizon (T): The forecast period in years.
Risk-Free Rate (r): The assumed risk-free interest rate.
Debt Extraction Method: The approach used to estimate total debt.
Volatility Calculation Period: The historical period for computing stock volatility.
ticker_symbol = "AAPL" # Change this to analyze a different stock
# Model Parameters
T = 1.0 # Time horizon in years
r = 0.04 # Risk-free interest rate (annual)
debt_method = "STplusHalfLT" # Options: "TOTAL", "STplusHalfLT", "STplusLT"
debt_factor = 1.0 # Scaling factor applied to debt. Higher factor = lower DTD (higher default risk)
# Volatility Calculation
historical_period = "1y" # Period used to compute equity volatility (e.g. "1y", "2y", "6mo")
annualization_factor = 252 # Annualization factor for daily stock returnsNext, we get:
Stock price (
regularMarketPrice)Shares outstanding (
sharesOutstanding)Historical stock prices (for volatility estimation)
The stock’s market cap (E) is calculated from price × shares outstanding.
Equity volatility (σE) is estimated from historical log returns, annualized over 252 trading days.
# Fetch market data for the selected stock
ticker = yf.Ticker(ticker_symbol)
info = ticker.info
# Get stock price
share_price = info.get("regularMarketPrice", None)
if share_price is None:
raise ValueError("Market price not available.")
# Get total shares outstanding
shares_out = info.get("sharesOutstanding", 0)
if shares_out <= 0:
raise ValueError("Shares outstanding missing or invalid.")
# Compute total equity market value
E = share_price * shares_out # Market capitalization
# Fetch historical stock prices
hist = ticker.history(period=historical_period).dropna(subset=["Close"])
close_prices = hist["Close"].values
# Compute log returns and annualized equity volatility
log_returns = np.log(close_prices[1:] / close_prices[:-1])
sigma_E = np.std(log_returns) * np.sqrt(annualization_factor)
# Fetch debt from balance sheet
balance_sheet = ticker.balance_sheet
raw_debt = get_debt_from_balance_sheet(
balance_sheet=balance_sheet,
info=info,
method=debt_method,
fallback=1e6 # Default value if no valid debt data is found
)
D = raw_debt * debt_factorSolving for Asset Value and Asset Volatility
Since A and σA are unknown, we solve the Merton Model equations numerically using scipy.optimize.fsolve:
# Solve for Asset Value (A) and Asset Volatility (sigma_A) using Merton Model
def solve_merton(E, sigma_E, D, T, r):
"""
Solve for (A, sigma_A) using the Merton Model.
Inputs:
E = Market value of equity (USD)
sigma_E = Equity volatility
D = Debt level (Default point)
T = Time horizon in years
r = Risk-free interest rate (annual)
Returns:
(A, sigma_A) = Estimated asset value and asset volatility
"""
def equations(vars_):
A_, sigmaA_ = vars_
d1_ = (np.log(A_/D) + (r + 0.5*sigmaA_**2)*T) / (sigmaA_*np.sqrt(T))
d2_ = d1_ - sigmaA_*np.sqrt(T)
# Equation 1: Equity as a call option on firm assets
eq1 = A_ * norm.cdf(d1_) - D * np.exp(-r*T) * norm.cdf(d2_) - E
# Equation 2: Relationship between equity and asset volatility
eq2 = sigma_E - (A_/E) * norm.cdf(d1_) * sigmaA_
return (eq1, eq2)
# Initial guesses for A and sigma_A
A_guess = E + D # Initial estimate of asset value
sigmaA_guess = sigma_E * (E / (E + D)) # Approximate initial asset volatility
# Solve the nonlinear system of equations
A_star, sigmaA_star = fsolve(equations, x0=[A_guess, sigmaA_guess], maxfev=2000)
return A_star, sigmaA_starWe implement the function below using the previously defined parameters:
# Solve Merton Model equations to estimate Asset Value (A) and Asset Volatility (σ_A)
A_star, sigmaA_star = solve_merton(E=E, sigma_E=sigma_E, D=D, T=T, r=r)This outputs the firm’s total asset value and asset volatility.
Calculating Distance to Default
The function below calculates d2, i.e. DTD as previously defined:
# Compute Distance to Default
def distance_to_default(A, D, T, r, sigmaA):
"""
Compute the Distance to Default.
Formula:
d2 = [ln(A/D) + (r - 0.5 * sigmaA^2) * T] / [sigmaA * sqrt(T)]
Inputs:
A = Estimated asset value
D = Default point (debt level)
T = Time horizon in years
r = Risk-free interest rate (annual)
sigmaA = Estimated asset volatility
Returns:
d2 (Distance to Default)
"""
return (np.log(A / D) + (r - 0.5 * sigmaA**2) * T) / (sigmaA * np.sqrt(T))We call the distance_to_default function to get the single numeric value representing DTD:
# Compute Distance to Default
dtd_value = distance_to_default(A_star, D, T, r, sigmaA_star)Display Key Results
Finally, we can print all of the results:
# Display results
print("============================================================")
print(f" Ticker: {ticker_symbol}")
print(f" Horizon (T): {T} years")
print(f" Risk-free rate (r): {r:.2%}")
print(f" Debt method: {debt_method}, factor={debt_factor}")
print(f" Historical period for volatility: {historical_period}")
print(f" Annualization factor: {annualization_factor}")
print("------------------------------------------------------------")
print(f" Share Price: ${share_price:,.2f}")
print(f" Shares Outstanding: {shares_out:,.0f}")
print(f" Total Equity Market Value (E): ${E:,.2f}")
print(f" Equity Volatility (σ_E): {sigma_E:.4f}")
print(f" Debt (D): ${D:,.2f}")
print("------------------------------------------------------------")
print("SOLVED MERTON MODEL RESULTS:")
print(f" Asset Value (A): ${A_star:,.2f}")
print(f" Asset Volatility (σ_A): {sigmaA_star:.4f}")
print(f" Distance-to-Default (d2): {dtd_value:.4f}")
print("============================================================")============================================================
Ticker: AAPL
Horizon (T): 1.0 years
Risk-free rate (r): 4.00%
Debt method: STplusHalfLT, factor=1.0
Historical period for volatility: 1y
Annualization factor: 252
------------------------------------------------------------
Share Price: $239.07
Shares Outstanding: 15,022,100,480
Total Equity Market Value (E): $3,591,333,561,753.60
Equity Volatility (σ_E): 0.2377
Debt (D): $42,875,000,000.00
------------------------------------------------------------
SOLVED MERTON MODEL RESULTS:
Asset Value (A): $3,632,527,408,957.26
Asset Volatility (σ_A): 0.2350
Distance-to-Default (d2): 18.9404
============================================================As expected for Apple, the DTD of ~18 indicates an extremely low probability of financial distress.
This is because Apple’s assets significantly exceed its debt obligations.
3. Sensitivity Analysis: DTD and Market Conditions
How does DTD respond to changes in asset value or debt levels? Let’s explore that.
Simulating Market Shocks
Firms’ asset values fluctuate due to revenue changes, economic downturns, and unexpected events.
To understand how these fluctuations affect default risk, we simulate 10,000 alternative scenarios for asset value and compute the resulting DTD values.
Key insights:
The spread of DTD values shows uncertainty in default risk.
Wider distributions indicate firms with higher market volatility are more sensitive to financial stress.
The red dashed line marks the original computed DTD.
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use("dark_background")
# Number of simulations
num_simulations = 10000
# Simulate asset values assuming a normal distribution
A_simulated = np.random.normal(A_star, sigmaA_star * A_star, num_simulations)
# Ensure asset values remain positive to avoid invalid log(A/D)
A_simulated = np.where(A_simulated > 0, A_simulated, np.nan)
# Compute simulated DTD values, ensuring no NaNs
DTD_simulated = (np.log(A_simulated / D) + (r - 0.5 * sigmaA_star**2) * T) / (sigmaA_star * np.sqrt(T))
DTD_simulated = DTD_simulated[~np.isnan(DTD_simulated)] # Remove NaNs
# Plot histogram of simulated DTD values
plt.figure(figsize=(25, 6))
sns.histplot(DTD_simulated, bins=50, kde=True, color="cyan", alpha=0.7)
# Add vertical line for actual computed DTD
plt.axvline(x=dtd_value, color="red", linestyle="dashed", linewidth=2, label=f"Actual DTD = {dtd_value:.2f}")
# Labels and title
plt.title("Distribution of Simulated Distance-to-Default", fontsize=14)
plt.xlabel("Distance-to-Default", fontsize=12)
plt.ylabel("Frequency", fontsize=12)
plt.legend()
# Show plot
plt.show()
Figure 1. Distribution of simulated DTD based on 10,000 Monte Carlo simulations. The red dashed line represents the actual computed DTD of 18.94.
Most simulated DTD values fall between 18 and 20. This further confirms low default risk.
There is minimal left-tail risk, i.e. there is a low probability mass for financial distress.
DTD vs Changes in Asset Value
What happens if a firm’s asset value declines? If assets shrink toward debt levels, default risk increases.
Steps below:
Vary asset value from the debt level (D) up to 110% of A_star.
Compute DTD for each asset value.
Plot the sensitivity curve.
import matplotlib.pyplot as plt
# Generate a range of asset values
asset_range = np.linspace(D, 1.1 * A_star, 200)
# Compute DTD for each asset value in the range
dtd_asset = (np.log(asset_range / D) + (r - 0.5 * sigmaA_star**2) * T) / (sigmaA_star * np.sqrt(T))
# Create a wider figure
plt.figure(figsize=(25, 6)) # Wider figure for better visualization
# Plot DTD vs. Asset Value
plt.plot(asset_range, dtd_asset, linestyle="-", color="cyan", linewidth=2, label="DTD vs. Asset Value")
# Mark the estimated asset value
plt.axvline(x=A_star, color="red", linestyle="dashed", linewidth=2, label=f"Estimated A = {A_star:,.2f}")
# Labels and title
plt.title("Sensitivity of Distance to Default to Asset Value", fontsize=14)
plt.xlabel("Asset Value (A)", fontsize=12)
plt.ylabel("Distance-to-Default", fontsize=12)
plt.xscale("log") # Log scale for better visualization
plt.legend()
# Show grid for clarity
plt.grid(color="gray", linestyle="dotted", linewidth=0.5)
# Show plot
plt.show()
Figure 2. Sensitivity of DTD to asset value. The red dashed line marks the estimated asset value of $3.63T.
To reach a DTD near zero, Apple’s asset value would need to fall from around $3.6 trillion to below $50 billion. A drop of over 98%.
DTD vs Changes in Debt Level
While debt can fuel growth, excessive borrowing increases default risk.
Key Insights provided below:
Higher debt → Lower DTD → Higher probability of financial distress.
When D approaches A, DTD collapses and signals critical risk.
import matplotlib.pyplot as plt
# Generate a range of debt values
debt_range = np.linspace(0.1 * D, 1.2 * A_star, 300)
# Compute DTD for each debt value in the range
dtd_debt = (np.log(A_star / debt_range) + (r - 0.5 * sigmaA_star**2) * T) / (sigmaA_star * np.sqrt(T))
# Create a wider figure
plt.figure(figsize=(25, 6)) # Wider figure for better visualization
# Plot DTD vs. Debt Level
plt.plot(debt_range, dtd_debt, linestyle="-", color="lime", linewidth=2, label="DTD vs. Debt")
# Mark the estimated debt level
plt.axvline(x=D, color="red", linestyle="dashed", linewidth=2, label=f"Estimated D = {D:,.2f}")
# Labels and title
plt.title("Sensitivity of Distance to Default to Debt Level", fontsize=14)
plt.xlabel("Debt (D)", fontsize=12)
plt.ylabel("Distance-to-Default", fontsize=12)
plt.xscale("log") # Log scale for better visualization
plt.legend()
# Show grid for clarity
plt.grid(color="gray", linestyle="dotted", linewidth=0.5)
# Show plot
plt.show()
Figure 3. Sensitivity of DTD to debt level. The red dashed line represents the estimated debt level of $42.87B.
To push Apple’s DTD near zero, its debt would need to rise from around $43 billion to over $3.5 trillion to match its total asset value.
Asset Value vs Debt
A simple bar chart helps visualize how A compares to D.
# Define values for the bar chart
categories = ["Asset Value (A)", "Debt (D)"]
values = [A_star, D]
colors = ["cyan", "orange"] # Custom colors for better contrast
# Create a wider figure
plt.figure(figsize=(25, 6))
# Plot bar chart
plt.bar(categories, values, color=colors, alpha=0.8)
# Labels and title
plt.title("Asset Value vs. Default Point (Debt)", fontsize=14)
plt.ylabel("Value (USD)", fontsize=12)
# Display values on bars
for i, v in enumerate(values):
plt.text(i, v * 1.02, f"${v:,.2f}", color="white", fontsize=12, ha="center")
# Show grid for better readability
plt.grid(axis="y", color="gray", linestyle="dotted", linewidth=0.5)
# Show plot
plt.show()
Figure 4. Comparison of asset value and debt. Apple’s asset value is $3.63T, and its debt is $42.87B.
Apple’s asset value is significantly higher than its debt. The large gap between assets and debt further indicates low default probability.
4. Limitations & Improvements
Key Limitations
The Merton Model assumes asset values follow a lognormal distribution, which may not hold during extreme market events.
Debt is treated as a single maturity obligation, ignoring complex capital structures and varying debt maturities.
The model assumes constant risk-free rates and no early default. This oversimplifies real-world credit risk dynamics.
Potential Improvements
Incorporating a jump-diffusion process can account for sudden asset value shocks.
Adjusting for stochastic interest rates can improve accuracy in volatile macroeconomic environments.
Refining debt estimation methods using firm-specific capital structure data can enhance default threshold precision.
Integrating market-implied default probabilities from credit spreads can provide a more comprehensive risk assessment.
Concluding Thoughts
The Merton Model offers a fast and real-time measure of default risk. It captures market conditions credit ratings miss.
While the model simplifies real-world complexities, It’s still a valuable tool for the serious investor’s toolkit.
Subscribe to our premium content to read the rest.
Become a paying subscriber to get access to this post and other subscriber-only content.
Upgrade






