CH
CalcHub
Back to Guides
Advanced

Black-Scholes: The Formula That Changed Finance

The Black-Scholes model changed finance forever. Published in 1973, it provides a formula to calculate the โ€œfairโ€ price of a European option. You do not need to derive it โ€” you need to understand what it tells you and how to use it.

What Black-Scholes Does

Given five inputs (stock price, strike price, time to expiry, risk-free rate, and volatility), Black-Scholes outputs the theoretical fair value of a call or put option. If the market price is higher than Black-Scholes says it should be, the option might be overpriced.

The Five Inputs

  • S โ€” Current stock price (e.g., $200)
  • K โ€” Strike price of the option (e.g., $210)
  • T โ€” Time to expiry in years (e.g., 30 days = 30/365 = 0.082)
  • r โ€” Risk-free interest rate (e.g., 5% = 0.05, typically the treasury rate)
  • sigma โ€” Volatility of the stock (e.g., 25% = 0.25, annualised standard deviation of returns)

The Formula

Here it is. Do not panic. We will break it down piece by piece.

d1 = [ln(S/K) + (r + sigmaยฒ/2) * T] / (sigma * sqrt(T))
d2 = d1 - sigma * sqrt(T)

Call = S * N(d1) - K * e^(-rT) * N(d2)
Put = K * e^(-rT) * N(-d2) - S * N(-d1)

Breaking It Down

  • ln(S/K) โ€” How far the stock is from the strike (in log terms). Positive if stock is above strike.
  • sigma * sqrt(T) โ€” How much the stock could move before expiry. More time or more volatility = bigger number.
  • N(d1), N(d2) โ€” The cumulative normal distribution. Converts d1 and d2 into probabilities between 0 and 1.
  • e^(-rT) โ€” Discount factor. A dollar in the future is worth less than a dollar today.

Python Implementation

import numpy as np from scipy.stats import norm def black_scholes(S, K, T, r, sigma, option_type="call"): """ Calculate Black-Scholes option price. Parameters: S     - Current stock price K     - Strike price T     - Time to expiry (in years) r     - Risk-free rate (annual) sigma - Volatility (annual) option_type - 'call' or 'put' Returns: Option price """ d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T)) d2 = d1 - sigma * np.sqrt(T) if option_type == "call": price = S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2) else: price = K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1) return price # Example: AAPL at $200, strike $210, 30 days to expiry, 5% rate, 25% vol call_price = black_scholes(S=200, K=210, T=30/365, r=0.05, sigma=0.25) put_price = black_scholes(S=200, K=210, T=30/365, r=0.05, sigma=0.25, option_type="put") print(f"Call price: ${{call_price:.2f}}") print(f"Put price:  ${{put_price:.2f}}")

Calculating the Greeks

def greeks(S, K, T, r, sigma): """Calculate the main Greeks for a call option.""" d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T)) d2 = d1 - sigma * np.sqrt(T) delta = norm.cdf(d1) gamma = norm.pdf(d1) / (S * sigma * np.sqrt(T)) theta = (-(S * norm.pdf(d1) * sigma) / (2 * np.sqrt(T)) - r * K * np.exp(-r * T) * norm.cdf(d2)) / 365  # per day vega = S * norm.pdf(d1) * np.sqrt(T) / 100  # per 1% vol change return delta, gamma, theta, vega d, g, t, v = greeks(S=200, K=210, T=30/365, r=0.05, sigma=0.25) print(f"Delta: {{d:.4f}}  (option moves ${{d:.2f}} per $1 stock move)") print(f"Gamma: {{g:.4f}}  (delta changes by {{g:.4f}} per $1 stock move)") print(f"Theta: {{t:.4f}}  (option loses ${{abs(t):.2f}} per day)") print(f"Vega:  {{v:.4f}}  (option gains ${{v:.2f}} per 1% vol increase)")

Implied Volatility

If you know the market price of an option, you can reverse-engineer the volatility the market is expecting. This is called implied volatility (IV) โ€” and it is one of the most important numbers in options trading.

from scipy.optimize import brentq def implied_volatility(market_price, S, K, T, r, option_type="call"): """Find the volatility that makes Black-Scholes match the market price.""" def objective(sigma): return black_scholes(S, K, T, r, sigma, option_type) - market_price # Search for sigma between 1% and 500% try: iv = brentq(objective, 0.01, 5.0) return iv except ValueError: return None # Example: option is trading at $3.50 in the market market_price = 3.50 iv = implied_volatility(market_price, S=200, K=210, T=30/365, r=0.05) print(f"Implied volatility: {{iv:.2%}}") # This is what the market THINKS the volatility will be over the next 30 days

You Don't Need to Derive It

The point of Black-Scholes is not to memorise the formula. The point is to understand: options have a fair value based on 5 inputs, and the most important input (volatility) is the one nobody knows for sure. That is why options trading is really about trading volatility, not direction.

Assumptions and Limitations

  • Constant volatility โ€” In reality, volatility changes constantly. The model pretends it does not.
  • Normal distribution โ€” Assumes log-normal returns. Real markets have fat tails and crashes.
  • No dividends โ€” The basic model ignores dividends (extensions exist that include them).
  • European options only โ€” Cannot handle early exercise (American options need different models).
  • No transaction costs โ€” Assumes you can trade for free with no market impact.