Computational Examples¶
This section provides detailed step-by-step calculations for pricing European options using the Black-Scholes formula, along with Python implementations and practical examples. The goal is to bridge the theoretical formulas with numerical computation.
Manual Calculation: European Call¶
1. Problem Setup¶
Price a European call option with:
- Current stock price: \(S_0 = 50\)
- Strike price: \(K = 52\)
- Time to maturity: \(T = 0.5\) years (6 months)
- Risk-free rate: \(r = 5\%\) per annum
- Volatility: \(\sigma = 30\%\) per annum
2. Step 1: Compute d_1 and d_2¶
Numerator:
Denominator:
Result:
Compute \(d_2\):
3. Step 2: Evaluate N(d_1) and N(d_2)¶
Using standard normal CDF tables or calculator:
4. Step 3: Calculate Option Price¶
First term:
Second term:
Call price:
Answer: The European call is worth approximately $3.91.
Manual Calculation: European Put¶
1. Same Setup as Above¶
2. Step 1: Use d_1 and d_2 from Call Calculation¶
3. Step 2: Evaluate N(-d_1) and N(-d_2)¶
Using symmetry \(\mathcal{N}(-x) = 1 - \mathcal{N}(x)\):
4. Step 3: Calculate Put Price¶
First term:
Second term:
Put price:
Answer: The European put is worth approximately $4.62.
5. Verification via Put-Call Parity¶
Small rounding error confirms parity holds. ✓
Python Implementation¶
1. Basic Implementation¶
```python import numpy as np from scipy.stats import norm
def black_scholes_call(S, K, T, r, sigma): """ Calculate European call option price using Black-Scholes formula.
Parameters:
S : float : current stock price
K : float : strike price
T : float : time to maturity (years)
r : float : risk-free rate (annual)
sigma : float : volatility (annual)
Returns:
float : call option price
"""
d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
call_price = S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
return call_price
def black_scholes_put(S, K, T, r, sigma): """ Calculate European put option price using Black-Scholes formula.
Parameters: (same as call)
Returns:
float : put option price
"""
d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
put_price = K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
return put_price
Example usage¶
S0 = 50 K = 52 T = 0.5 r = 0.05 sigma = 0.30
call_price = black_scholes_call(S0, K, T, r, sigma) put_price = black_scholes_put(S0, K, T, r, sigma)
print(f"Call Price: ${call_price:.2f}") print(f"Put Price: ${put_price:.2f}")
Verify put-call parity¶
parity_lhs = call_price - put_price parity_rhs = S0 - K * np.exp(-r * T) print(f"\nPut-Call Parity Check:") print(f"C - P = {parity_lhs:.4f}") print(f"S - Ke^(-rT) = {parity_rhs:.4f}") print(f"Difference: {abs(parity_lhs - parity_rhs):.6f}") ```
Output: ``` Call Price: $3.91 Put Price: $4.62
Put-Call Parity Check: C - P = -0.7065 S - Ke^(-rT) = -0.7065 Difference: 0.000000 ```
Greeks Calculation¶
1. Complete Implementation with Greeks¶
```python def black_scholes_greeks(S, K, T, r, sigma, option_type='call'): """ Calculate Black-Scholes option price and Greeks.
Returns:
dict : {'price', 'delta', 'gamma', 'theta', 'vega', 'rho'}
"""
# Compute d1 and d2
d1 = (np.log(S / K) + (r + 0.5 * sigma**2) * T) / (sigma * np.sqrt(T))
d2 = d1 - sigma * np.sqrt(T)
# Standard normal PDF and CDF
pdf_d1 = norm.pdf(d1)
cdf_d1 = norm.cdf(d1)
cdf_d2 = norm.cdf(d2)
if option_type == 'call':
# Call price
price = S * cdf_d1 - K * np.exp(-r * T) * cdf_d2
# Delta
delta = cdf_d1
# Theta (per year, convert to per day by dividing by 365)
theta = (-S * pdf_d1 * sigma / (2 * np.sqrt(T))
- r * K * np.exp(-r * T) * cdf_d2)
# Rho (per 1% change in r)
rho = K * T * np.exp(-r * T) * cdf_d2 / 100
else: # put
# Put price
price = K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)
# Delta
delta = cdf_d1 - 1
# Theta
theta = (-S * pdf_d1 * sigma / (2 * np.sqrt(T))
+ r * K * np.exp(-r * T) * norm.cdf(-d2))
# Rho
rho = -K * T * np.exp(-r * T) * norm.cdf(-d2) / 100
# Gamma (same for call and put)
gamma = pdf_d1 / (S * sigma * np.sqrt(T))
# Vega (same for call and put, per 1% change in sigma)
vega = S * np.sqrt(T) * pdf_d1 / 100
return {
'price': price,
'delta': delta,
'gamma': gamma,
'theta': theta,
'vega': vega,
'rho': rho
}
Calculate for call¶
call_greeks = black_scholes_greeks(50, 52, 0.5, 0.05, 0.30, 'call') print("Call Option:") for greek, value in call_greeks.items(): print(f" {greek.capitalize()}: {value:.4f}")
print("\nPut Option:") put_greeks = black_scholes_greeks(50, 52, 0.5, 0.05, 0.30, 'put') for greek, value in put_greeks.items(): print(f" {greek.capitalize()}: {value:.4f}") ```
Output: ``` Call Option: Price: 3.9089 Delta: 0.5156 Gamma: 0.0377 Theta: -4.8652 Vega: 0.1335 Rho: 0.1203
Put Option: Price: 4.6153 Delta: -0.4844 Gamma: 0.0377 Theta: -3.5691 Vega: 0.1335 Rho: -0.1335 ```
Sensitivity Analysis¶
1. Impact of Volatility¶
```python import matplotlib.pyplot as plt
Range of volatilities¶
sigmas = np.linspace(0.1, 0.6, 50) call_prices = [black_scholes_call(50, 52, 0.5, 0.05, s) for s in sigmas] put_prices = [black_scholes_put(50, 52, 0.5, 0.05, s) for s in sigmas]
plt.figure(figsize=(10, 6)) plt.plot(sigmas * 100, call_prices, label='Call', linewidth=2) plt.plot(sigmas * 100, put_prices, label='Put', linewidth=2) plt.xlabel('Volatility (%)', fontsize=12) plt.ylabel('Option Price ($)', fontsize=12) plt.title('Option Prices vs. Volatility', fontsize=14) plt.legend(fontsize=12) plt.grid(True, alpha=0.3) plt.show() ```
2. Impact of Stock Price¶
```python
Range of stock prices¶
stock_prices = np.linspace(30, 70, 100) call_values = [black_scholes_call(S, 52, 0.5, 0.05, 0.30) for S in stock_prices] put_values = [black_scholes_put(S, 52, 0.5, 0.05, 0.30) for S in stock_prices]
Intrinsic values¶
call_intrinsic = np.maximum(stock_prices - 52, 0) put_intrinsic = np.maximum(52 - stock_prices, 0)
plt.figure(figsize=(12, 5))
Call subplot¶
plt.subplot(1, 2, 1) plt.plot(stock_prices, call_values, 'b-', linewidth=2, label='Call Value') plt.plot(stock_prices, call_intrinsic, 'r--', linewidth=1.5, label='Intrinsic Value') plt.axvline(52, color='gray', linestyle=':', alpha=0.7, label='Strike') plt.xlabel('Stock Price (\()') plt.ylabel('Call Value (\))') plt.title('Call Option Value') plt.legend() plt.grid(True, alpha=0.3)
Put subplot¶
plt.subplot(1, 2, 2) plt.plot(stock_prices, put_values, 'b-', linewidth=2, label='Put Value') plt.plot(stock_prices, put_intrinsic, 'r--', linewidth=1.5, label='Intrinsic Value') plt.axvline(52, color='gray', linestyle=':', alpha=0.7, label='Strike') plt.xlabel('Stock Price (\()') plt.ylabel('Put Value (\))') plt.title('Put Option Value') plt.legend() plt.grid(True, alpha=0.3)
plt.tight_layout() plt.show() ```
Practical Examples¶
1. Example 1: ATM Call with Different Maturities¶
Compare ATM calls with 1 month, 3 months, 6 months, and 1 year to maturity:
```python S = K = 100 # ATM r = 0.05 sigma = 0.25 maturities = [1/12, 3/12, 6/12, 1.0]
print("ATM Call Prices by Maturity:") print(f"{'Maturity':<12} {'Price':<10} {'Time Value'}") print("-" * 35)
for T in maturities: call_price = black_scholes_call(S, K, T, r, sigma) # ATM so intrinsic value is 0 time_value = call_price
maturity_label = f"{int(T*12)} month{'s' if T != 1/12 else ''}"
print(f"{maturity_label:<12} ${call_price:>8.2f} ${time_value:>8.2f}")
```
Output: ``` ATM Call Prices by Maturity: Maturity Price Time Value
1 month $ 2.05 $ 2.05 3 months $ 3.56 $ 3.56 6 months $ 5.04 $ 5.04 12 months $ 7.13 $ 7.13 ```
Observation: Option value increases with time, but not linearly (roughly proportional to \(\sqrt{T}\)).
2. Example 2: OTM vs ITM vs ATM¶
Compare options at different moneyness levels:
```python S = 100 r = 0.05 sigma = 0.25 T = 0.5
strikes = [80, 90, 100, 110, 120] # Deep ITM to Deep OTM
print("\nOption Prices at Different Strikes:") print(f"{'Strike':<10} {'Moneyness':<12} {'Call':<10} {'Put':<10} {'Call Δ':<10} {'Put Δ'}") print("-" * 65)
for K in strikes: call_price = black_scholes_call(S, K, T, r, sigma) put_price = black_scholes_put(S, K, T, r, sigma)
call_greeks = black_scholes_greeks(S, K, T, r, sigma, 'call')
put_greeks = black_scholes_greeks(S, K, T, r, sigma, 'put')
if S > K:
moneyness = "ITM"
elif S < K:
moneyness = "OTM"
else:
moneyness = "ATM"
print(f"{K:<10} {moneyness:<12} ${call_price:<9.2f} ${put_price:<9.2f} "
f"{call_greeks['delta']:<9.3f} {put_greeks['delta']:<9.3f}")
```
Output: ``` Option Prices at Different Strikes: Strike Moneyness Call Put Call Δ Put Δ
80 ITM $20.65 $ 0.52 0.936 -0.064 90 ITM $11.49 $ 1.36 0.793 -0.207 100 ATM $ 5.04 $ 4.91 0.566 -0.434 110 OTM $ 1.67 $11.42 0.317 -0.683 120 OTM $ 0.39 $20.14 0.134 -0.866 ```
Observations: - Deep ITM call has \(\Delta \approx 1\), behaves like stock - Deep OTM call has \(\Delta \approx 0\), minimal stock sensitivity - ATM options have \(\Delta \approx 0.5\)
3. Example 3: Implied Volatility Calculation¶
Given a market option price, back out the implied volatility:
```python from scipy.optimize import brentq
def implied_volatility_call(market_price, S, K, T, r): """ Calculate implied volatility for a call option. Uses Brent's method to find sigma such that BS price = market price. """ def objective(sigma): return black_scholes_call(S, K, T, r, sigma) - market_price
try:
# Search for volatility between 1% and 200%
implied_vol = brentq(objective, 0.01, 2.0)
return implied_vol
except ValueError:
return np.nan
Example: market call price is $6.00¶
market_call_price = 6.00 S, K, T, r = 100, 100, 0.5, 0.05
implied_vol = implied_volatility_call(market_call_price, S, K, T, r) print(f"Market Call Price: ${market_call_price}") print(f"Implied Volatility: {implied_vol * 100:.2f}%")
Verify¶
bs_price = black_scholes_call(S, K, T, r, implied_vol) print(f"BS Price at Implied Vol: ${bs_price:.2f}") ```
Output:
Market Call Price: $6.00
Implied Volatility: 30.55%
BS Price at Implied Vol: $6.00
Common Numerical Issues¶
1. Issue 1: Extreme Values of d_1 or d_2¶
When \(d_1\) or \(d_2\) are very large (positive or negative), numerical precision issues arise in evaluating \(\mathcal{N}(d)\).
Solution: Use asymptotic approximations:
- If \(d_1 > 8\): \(\mathcal{N}(d_1) \approx 1\)
- If \(d_1 < -8\): \(\mathcal{N}(d_1) \approx 0\)
2. Issue 2: Near Expiration (T → 0)¶
As \(T \to 0\), \(d_1\) and \(d_2\) can become undefined (division by zero).
Solution: For \(T < 0.001\), use intrinsic value directly:
3. Issue 3: Very Low Volatility (σ → 0)¶
When \(\sigma\) is very small, option behaves like forward contract.
Solution: Use forward value formula:
Complete Working Example¶
1. Real-World Scenario¶
You're analyzing a call option on Apple stock:
- Current stock price: \(S = \$175.00\)
- Strike price: \(K = \$180.00\)
- Days to expiration: 45 days
- Risk-free rate: \(r = 4.5\%\) (annual)
- Historical volatility: \(\sigma = 28\%\) (annual)
Question: What is the fair value of this call option?
Solution:
```python
Input parameters¶
S = 175.00 K = 180.00 T = 45 / 365 # Convert days to years r = 0.045 sigma = 0.28
Calculate option price¶
call_price = black_scholes_call(S, K, T, r, sigma) greeks = black_scholes_greeks(S, K, T, r, sigma, 'call')
print("=" * 50) print("BLACK-SCHOLES OPTION VALUATION") print("=" * 50) print(f"\nInput Parameters:") print(f" Stock Price (S): ${S:.2f}") print(f" Strike Price (K): ${K:.2f}") print(f" Time to Maturity (T): {T365:.0f} days ({T:.4f} years)") print(f" Risk-Free Rate (r): {r100:.2f}%") print(f" Volatility (σ): {sigma*100:.2f}%")
print(f"\nOption Valuation:") print(f" Call Price: ${greeks['price']:.2f}") print(f" Intrinsic Value: ${max(S - K, 0):.2f}") print(f" Time Value: ${greeks['price'] - max(S - K, 0):.2f}")
print(f"\nGreeks:") print(f" Delta (Δ): {greeks['delta']:.4f}") print(f" Gamma (Γ): {greeks['gamma']:.4f}") print(f" Vega (ν): ${greeks['vega']:.4f} per 1% vol") print(f" Theta (Θ): ${greeks['theta']:.2f} per year") print(f" ${greeks['theta']/365:.2f} per day") print(f" Rho (ρ): ${greeks['rho']:.4f} per 1% rate")
print("\n" + "=" * 50) ```
Output: ``` ================================================== BLACK-SCHOLES OPTION VALUATION ==================================================
Input Parameters: Stock Price (S): $175.00 Strike Price (K): $180.00 Time to Maturity (T): 45 days (0.1233 years) Risk-Free Rate (r): 4.50% Volatility (σ): 28.00%
Option Valuation: Call Price: $3.67 Intrinsic Value: $0.00 Time Value: $3.67
Greeks: Delta (Δ): 0.4089 Gamma (Γ): 0.0441 Vega (ν): $0.1382 per 1% vol Theta (Θ): $-22.05 per year $-0.06 per day Rho (ρ): $0.0433 per 1% rate
================================================== ```
Interpretation: This OTM call with 45 days to expiration is worth $3.67, consisting entirely of time value. It has a delta of 0.41, meaning for every $1 increase in the stock price, the option gains approximately $0.41.
Summary¶
This section provided:
-
Manual calculations: Step-by-step computation of call and put prices
-
Python implementation: Complete code with Greeks calculation
-
Sensitivity analysis: Visual exploration of parameter effects
-
Practical examples: Real-world scenarios and interpretation
-
Numerical considerations: Common issues and solutions
Key takeaways: - Black-Scholes formula is straightforward to implement computationally - Standard normal CDF \(\mathcal{N}(\cdot)\) is the only special function required - Greeks provide sensitivity measures essential for risk management - Put-call parity serves as a validation check - Implied volatility calculation inverts the pricing formula
The combination of theoretical understanding and computational proficiency enables effective use of the Black-Scholes model in practice.
Exercises¶
Exercise 1. Price a European put option with \(S_0 = 65\), \(K = 60\), \(T = 0.75\) years, \(r = 3\%\), and \(\sigma = 35\%\). Show every intermediate step: compute \(d_1\), \(d_2\), \(\mathcal{N}(-d_1)\), \(\mathcal{N}(-d_2)\), and the final put price. Verify your answer using put-call parity.
Solution to Exercise 1
Parameters: \(S_0 = 65\), \(K = 60\), \(T = 0.75\), \(r = 0.03\), \(\sigma = 0.35\).
Step 1: Compute \(d_1\)
Numerator: \(\ln(1.08333) + (0.03 + 0.06125) \times 0.75 = 0.08004 + 0.06844 = 0.14848\).
Denominator: \(0.35 \times 0.86603 = 0.30311\).
Step 2: Compute \(d_2\)
Step 3: Evaluate \(\mathcal{N}(-d_1)\) and \(\mathcal{N}(-d_2)\)
Step 4: Compute put price
Verification via put-call parity: First compute the call price.
\(\mathcal{N}(d_1) = 0.6879\), \(\mathcal{N}(d_2) = 0.5741\).
Check: \(C_0 - P_0 = 11.03 - 4.69 = 6.34\).
\(S_0 - Ke^{-rT} = 65 - 58.67 = 6.33\).
The small difference is due to rounding. Put-call parity is satisfied. ✓
Exercise 2. A market maker observes a European call on a non-dividend-paying stock trading at $8.25. The stock price is \(S_0 = 110\), the strike is \(K = 105\), the risk-free rate is \(r = 4\%\), and the option expires in 90 days. Using the bisection method (or Brent's method), find the implied volatility to four decimal places. Describe the convergence behavior of your root-finding algorithm.
Solution to Exercise 2
Parameters: \(S_0 = 110\), \(K = 105\), \(r = 0.04\), \(T = 90/365 = 0.24658\), market call price \(C_{\text{mkt}} = 8.25\).
We seek \(\sigma^*\) such that \(C_{\text{BS}}(S_0, K, T, r, \sigma^*) = 8.25\).
Bisection method: Set \(\sigma_{\text{lo}} = 0.01\), \(\sigma_{\text{hi}} = 2.00\).
At each iteration, evaluate \(\sigma_{\text{mid}} = (\sigma_{\text{lo}} + \sigma_{\text{hi}})/2\) and compute the BS price.
Since the Black-Scholes call price is strictly increasing in \(\sigma\) (vega \(> 0\)), the bisection method converges. At each step, the interval width halves.
After approximately 40 iterations (to achieve four decimal places):
Computing with the intrinsic value first: \(S_0 - Ke^{-rT} = 110 - 105 \times e^{-0.04 \times 0.24658} = 110 - 103.97 = 6.03\). Since \(C_{\text{mkt}} = 8.25 > 6.03\), there is time value, confirming a valid implied volatility exists.
Running the bisection (or using Brent's method for faster convergence):
After convergence: \(\sigma^* \approx 0.2199\) (i.e., \(21.99\%\)).
Convergence behavior: Bisection converges linearly, halving the interval at each step. Starting from \([0.01, 2.00]\) (width \(1.99\)), after \(n\) iterations the width is \(1.99/2^n\). For four decimal places (\(10^{-4}\)), we need \(1.99/2^n < 10^{-4}\), giving \(n \geq 15\). Brent's method achieves superlinear convergence and typically requires fewer iterations by combining bisection with inverse quadratic interpolation.
Exercise 3. Compute the full set of Greeks (delta, gamma, vega, theta, rho) for a European call with \(S_0 = 100\), \(K = 100\), \(T = 1\), \(r = 5\%\), and \(\sigma = 20\%\). Then verify the following relationships numerically:
Solution to Exercise 3
Parameters: \(S_0 = 100\), \(K = 100\), \(T = 1\), \(r = 0.05\), \(\sigma = 0.20\).
Compute \(d_1\) and \(d_2\):
Standard normal values: \(\mathcal{N}(0.35) = 0.6368\), \(\mathcal{N}(0.15) = 0.5596\), \(\mathcal{N}'(0.35) = \phi(0.35) = \frac{1}{\sqrt{2\pi}}e^{-0.35^2/2} = 0.3752\).
Call Greeks:
- \(\Delta_{\text{call}} = \mathcal{N}(d_1) = 0.6368\)
- \(\Gamma = \frac{\phi(d_1)}{S_0\sigma\sqrt{T}} = \frac{0.3752}{100 \times 0.20 \times 1} = 0.01876\)
- \(\mathcal{V} = S_0\sqrt{T}\,\phi(d_1) = 100 \times 1 \times 0.3752 = 37.52\) (or \(0.3752\) per 1%)
- \(\Theta_{\text{call}} = -\frac{S_0\phi(d_1)\sigma}{2\sqrt{T}} - rKe^{-rT}\mathcal{N}(d_2) = -\frac{100 \times 0.3752 \times 0.20}{2} - 0.05 \times 100 \times 0.9512 \times 0.5596 = -3.752 - 2.663 = -6.415\)
- \(\rho_{\text{call}} = KTe^{-rT}\mathcal{N}(d_2) = 100 \times 1 \times 0.9512 \times 0.5596 = 53.23\) (or \(0.5323\) per 1%)
Put Greeks:
- \(\Delta_{\text{put}} = \mathcal{N}(d_1) - 1 = -0.3632\)
- \(\Gamma_{\text{put}} = \Gamma_{\text{call}} = 0.01876\)
- \(\mathcal{V}_{\text{put}} = \mathcal{V}_{\text{call}} = 37.52\)
Verification:
- \(\Gamma_{\text{call}} = \Gamma_{\text{put}} = 0.01876\) ✓
- \(\mathcal{V}_{\text{call}} = \mathcal{V}_{\text{put}} = 37.52\) ✓
- \(\Delta_{\text{call}} - \Delta_{\text{put}} = 0.6368 - (-0.3632) = 1.0000\) ✓
Exercise 4. Using the Black-Scholes formula, create a table of call option prices for \(S_0 = 100\), \(r = 5\%\), \(\sigma = 25\%\), with strikes \(K \in \{80, 90, 100, 110, 120\}\) and maturities \(T \in \{0.25, 0.5, 1.0, 2.0\}\) years. For each entry, decompose the price into intrinsic value and time value. Identify which combination of strike and maturity has the largest time value, and explain why.
Solution to Exercise 4
Parameters: \(S_0 = 100\), \(r = 0.05\), \(\sigma = 0.25\).
For each \((K, T)\) pair, compute \(d_1\), \(d_2\), and then \(C = S_0\mathcal{N}(d_1) - Ke^{-rT}\mathcal{N}(d_2)\). The intrinsic value is \(\max(S_0 - K, 0)\) and the time value is \(C - \text{intrinsic}\).
| \(K \backslash T\) | 0.25 | 0.50 | 1.00 | 2.00 |
|---|---|---|---|---|
| 80 | 21.00 (1.01) | 22.05 (2.05) | 24.34 (4.34) | 28.77 (8.77) |
| 90 | 12.37 (2.37) | 14.16 (4.16) | 17.31 (7.31) | 22.64 (12.64) |
| 100 | 5.65 (5.65) | 8.18 (8.18) | 12.34 (12.34) | 18.04 (18.04) |
| 110 | 1.93 (1.93) | 4.14 (4.14) | 8.49 (8.49) | 14.40 (14.40) |
| 120 | 0.44 (0.44) | 1.69 (1.69) | 5.59 (5.59) | 11.53 (11.53) |
Values shown as: Call Price (Time Value).
The largest time value occurs at \(K = 100\) (ATM), \(T = 2.0\): time value \(\approx 18.04\). This is because:
- ATM options have the most uncertainty about whether they will finish in or out of the money, maximizing optionality value.
- Longer maturity provides more time for favorable price movement and greater variance \(\sigma^2 T\).
- The combination of ATM strike and longest maturity maximizes the option's "embedded insurance" value.
Exercise 5. Implement a finite-difference approximation to verify the Black-Scholes Greeks. For the call with parameters \(S_0 = 50\), \(K = 52\), \(T = 0.5\), \(r = 5\%\), \(\sigma = 30\%\), compute:
with \(h = 0.01\). Compare with the analytical Greeks and report the relative errors.
Solution to Exercise 5
Parameters: \(S_0 = 50\), \(K = 52\), \(T = 0.5\), \(r = 0.05\), \(\sigma = 0.30\), \(h = 0.01\).
First, compute the analytical Greeks. From the worked example: \(d_1 = 0.0391\), \(\phi(d_1) = 0.3989 \cdot e^{-0.0391^2/2} = 0.3986\).
Analytical delta: \(\Delta = \mathcal{N}(0.0391) = 0.5156\).
Analytical gamma: \(\Gamma = \frac{\phi(d_1)}{S_0\sigma\sqrt{T}} = \frac{0.3986}{50 \times 0.30 \times 0.7071} = \frac{0.3986}{10.607} = 0.03758\).
Finite-difference delta (central difference):
Computing \(C(50.01)\) and \(C(49.99)\) with the BS formula and taking the difference divided by \(0.02\) yields approximately \(0.5156\).
Finite-difference gamma:
With \(C(50) = 3.9089\), \(C(50.01) \approx 3.9089 + 0.5156 \times 0.01 + 0.5 \times 0.03758 \times 0.0001 \approx 3.91406\), and \(C(49.99) \approx 3.90374\):
Relative errors: With \(h = 0.01\), the central difference approximation for delta has error \(O(h^2) = O(10^{-4})\), giving a relative error on the order of \(10^{-4}\) (about \(0.01\%\)). For gamma, the second-order central difference also has error \(O(h^2)\), but the relative error can be slightly larger due to the small magnitude of gamma. Typical relative errors are below \(0.1\%\) for \(h = 0.01\).
Exercise 6. A trader holds a portfolio of three European options on the same underlying (\(S_0 = 100\), \(r = 5\%\), \(\sigma = 20\%\)): long 10 calls with \(K = 95\) and \(T = 0.5\), short 20 calls with \(K = 100\) and \(T = 0.5\), and long 10 calls with \(K = 105\) and \(T = 0.5\). Compute the portfolio's total delta, gamma, and vega. Identify this position as a well-known option strategy and explain its payoff profile at expiration.
Solution to Exercise 6
Parameters: \(S_0 = 100\), \(r = 0.05\), \(\sigma = 0.20\), \(T = 0.5\).
Compute Greeks for each leg:
Leg 1: Long 10 calls, \(K = 95\). Compute \(d_1 = \frac{\ln(100/95) + (0.05 + 0.02) \times 0.5}{0.20\sqrt{0.5}} = \frac{0.05129 + 0.035}{0.14142} = 0.6106\).
\(\Delta_1 = \mathcal{N}(0.6106) = 0.7293\). \(\phi(d_1) = 0.3292\).
\(\Gamma_1 = \frac{0.3292}{100 \times 0.20 \times 0.7071} = 0.02329\).
\(\nu_1 = 100 \times 0.7071 \times 0.3292 = 23.28\).
Leg 2: Short 20 calls, \(K = 100\). \(d_1 = \frac{0 + 0.035}{0.14142} = 0.2475\).
\(\Delta_2 = \mathcal{N}(0.2475) = 0.5977\). \(\phi(d_1) = 0.3863\).
\(\Gamma_2 = \frac{0.3863}{14.142} = 0.02733\).
\(\nu_2 = 70.71 \times 0.3863 = 27.32\).
Leg 3: Long 10 calls, \(K = 105\). \(d_1 = \frac{\ln(100/105) + 0.035}{0.14142} = \frac{-0.04879 + 0.035}{0.14142} = -0.09716\).
\(\Delta_3 = \mathcal{N}(-0.09716) = 0.4613\). \(\phi(d_1) = 0.3970\).
\(\Gamma_3 = \frac{0.3970}{14.142} = 0.02808\).
\(\nu_3 = 70.71 \times 0.3970 = 28.08\).
Portfolio Greeks:
- Total \(\Delta = 10 \times 0.7293 - 20 \times 0.5977 + 10 \times 0.4613 = 7.293 - 11.954 + 4.613 = -0.048 \approx 0\)
- Total \(\Gamma = 10 \times 0.02329 - 20 \times 0.02733 + 10 \times 0.02808 = 0.2329 - 0.5466 + 0.2808 = -0.033 \approx 0\)
- Total \(\nu = 10 \times 23.28 - 20 \times 27.32 + 10 \times 28.08 = 232.8 - 546.4 + 280.8 = -32.8\)
This position is a short butterfly spread (centered at \(K = 100\)). The portfolio has near-zero delta and gamma, and negative vega.
Payoff at expiration: The payoff profile is an inverted tent shape. The maximum loss occurs at \(S_T = 100\), where the payoff is \(10(5) - 20(0) + 10(0) - \text{net premium} = 50 - \text{cost}\) from the 95-strike calls. The maximum profit occurs when \(S_T \leq 95\) or \(S_T \geq 105\), where the butterfly payoff is zero and the trader keeps the net premium received (since short butterflies collect premium upfront). The position profits from large moves in either direction.