Skip to content

Option Prices (Using Formula)

Background

Black Scholes Prices Formula

Educational script demonstrating black scholes prices formula concepts.


Code

```python """ Black Scholes Prices Formula

Educational script demonstrating black scholes prices formula concepts. """

============================================================================

black_scholes_OPTION_PRICES_USING_FORMULA.py

============================================================================

import black_scholes as bs import matplotlib.pyplot as plt import numpy as np

if name == "main": S = np.linspace(50, 150, 100) K = 100 T = 1 / 12 r = 0.05 sigma = 0.3

# ===========================================================
# Loop-based computation of Black-Scholes call and put prices
# ===========================================================

call_prices = []
put_prices = []
for s in S:
    call_price, put_price = bs.BlackScholes(s, K, T, r, sigma).price_analytical()
    call_prices.append(call_price)
    put_prices.append(put_price)
call = np.array(call_prices)
put = np.array(put_prices)

print(f"\n{'='*80}")
print("BASIC OPTION PRICING RESULTS")
print("="*80,end="\n"*2)
print(f"Stock price range: ${S[0]:.0f} - ${S[-1]:.0f}")
print(f"Strike price: ${K}")
print(f"Number of price points calculated: {len(S)}")
print(f"")
print(f"At-the-money option prices (S = ${K}):")
idx_atm = np.argmin(np.abs(S - K))
print(f"  Call price: ${call[idx_atm]:.2f}")
print(f"  Put price:  ${put[idx_atm]:.2f}")
print(f"")
print(f"Price range summary:")
print(f"  Call prices: ${call.min():.2f} - ${call.max():.2f}")
print(f"  Put prices:  ${put.min():.2f} - ${put.max():.2f}")
print(f"")
print("Note: Loop-based calculation completed successfully!")

fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(12, 4))

fig.suptitle(
    "\n\n" +
    f"Black-Scholes Option Pricing\n" +
    f"Strike $K$ = {K}, Time to Maturity $T$ = {T:.2f} yr, " +
    f"Rate $r$ = {r:.2%}, Volatility $\sigma$ = {sigma:.2%}" + "\n\n",
    fontsize=12, y=1.05
)

ax0.plot(S, call, '-r', linewidth=2, label='Call Price')
ax0.plot(S, np.maximum(S - K, 0), '--b', linewidth=2, label='Call Payoff', alpha=0.6)
ax0.axvline(x=K, color='gray', linestyle=':', alpha=0.7, label=f'Strike K = {K}')
ax0.set_title('Call Option')
ax0.set_xlabel('Underlying Price $S$')
ax0.set_ylabel('Price')
ax0.legend()
ax0.grid(True, alpha=0.3)

ax1.plot(S, put, '-r', linewidth=2, label='Put Price')
ax1.plot(S, np.maximum(K - S, 0), '--b', linewidth=2, label='Put Payoff', alpha=0.6)
ax1.axvline(x=K, color='gray', linestyle=':', alpha=0.7, label=f'Strike K = {K}')
ax1.set_title('Put Option')
ax1.set_xlabel('Underlying Price $S$')
ax1.set_ylabel('Price')
ax1.legend()
ax1.grid(True, alpha=0.3)

plt.tight_layout()
plt.subplots_adjust(top=0.8)
plt.show()

# ===========================================================
# Vectorized computation of Black-Scholes call and put prices
# ===========================================================

print("\n" + "="*80)
print("ALTERNATIVE: Using Black-Scholes utilities for vectorization")
print("="*80,end="\n"*2)

call_vectorized = bs.bs_call_price(S, K, T, r, sigma)
put_vectorized = bs.bs_put_price(S, K, T, r, sigma)

print(f"Call prices match: {np.allclose(call, call_vectorized)}")
print(f"Put prices match: {np.allclose(put, put_vectorized)}")
print(f"Max difference (call): {np.max(np.abs(call - call_vectorized)):.2e}")
print(f"Max difference (put): {np.max(np.abs(put - put_vectorized)):.2e}")

def create_enhanced_option_plot(S_range, K, T, r, sigma, save_path=None):
    S = np.linspace(S_range[0], S_range[1], 200)

    call_prices = bs.bs_call_price(S, K, T, r, sigma)
    put_prices = bs.bs_put_price(S, K, T, r, sigma)

    call_intrinsic = np.maximum(S - K, 0)
    put_intrinsic = np.maximum(K - S, 0)

    call_time_value = call_prices - call_intrinsic
    put_time_value = put_prices - put_intrinsic

    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 10))

    fig.suptitle(
        f"Comprehensive Black-Scholes Option Analysis\n" +
        f"K = ${K}, T = {T:.3f} yr, r = {r:.1%}, σ = {sigma:.1%}",
        fontsize=14, y=0.98
    )

    ax1.plot(S, call_prices, '-r', linewidth=2, label='Call Price')
    ax1.plot(S, call_intrinsic, '--k', linewidth=2, label='Intrinsic Value', alpha=0.7)
    ax1.fill_between(S, call_intrinsic, call_prices, alpha=0.3, color='green', label='Time Value')
    ax1.axvline(x=K, color='gray', linestyle=':', alpha=0.7, label=f'Strike = ${K}')
    ax1.set_title('Call Option Components')
    ax1.set_xlabel('Stock Price ($)')
    ax1.set_ylabel('Option Value ($)')
    ax1.legend()
    ax1.grid(True, alpha=0.3)

    ax2.plot(S, put_prices, '-b', linewidth=2, label='Put Price')
    ax2.plot(S, put_intrinsic, '--k', linewidth=2, label='Intrinsic Value', alpha=0.7)
    ax2.fill_between(S, put_intrinsic, put_prices, alpha=0.3, color='orange', label='Time Value')
    ax2.axvline(x=K, color='gray', linestyle=':', alpha=0.7, label=f'Strike = ${K}')
    ax2.set_title('Put Option Components')
    ax2.set_xlabel('Stock Price ($)')
    ax2.set_ylabel('Option Value ($)')
    ax2.legend()
    ax2.grid(True, alpha=0.3)

    ax3.plot(S, call_time_value, '-g', linewidth=2, label='Call Time Value')
    ax3.plot(S, put_time_value, color='orange', linestyle='-', linewidth=2, label='Put Time Value')
    ax3.axvline(x=K, color='gray', linestyle=':', alpha=0.7, label=f'Strike = ${K}')
    ax3.set_title('Time Value Comparison')
    ax3.set_xlabel('Stock Price ($)')
    ax3.set_ylabel('Time Value ($)')
    ax3.legend()
    ax3.grid(True, alpha=0.3)

    current_call_price = bs.bs_call_price(K, K, T, r, sigma)
    current_put_price = bs.bs_put_price(K, K, T, r, sigma)

    call_pnl = call_intrinsic - current_call_price
    put_pnl = put_intrinsic - current_put_price

    ax4.plot(S, call_pnl, color='red', linestyle='-', linewidth=2, label=f'Call P&L (paid ${current_call_price:.2f})')
    ax4.plot(S, put_pnl, color='blue', linestyle='-', linewidth=2, label=f'Put P&L (paid ${current_put_price:.2f})')
    ax4.axhline(y=0, color='black', linestyle='-', alpha=0.5)
    ax4.axvline(x=K, color='gray', linestyle=':', alpha=0.7, label=f'Strike = ${K}')
    ax4.set_title('Profit & Loss at Expiration')
    ax4.set_xlabel('Stock Price at Expiration ($)')
    ax4.set_ylabel('Profit/Loss ($)')
    ax4.legend()
    ax4.grid(True, alpha=0.3)

    for ax in [ax1, ax2, ax3, ax4]:
        ax.spines["top"].set_visible(False)
        ax.spines["right"].set_visible(False)
        ax.spines["bottom"].set_position("zero")
        ax.spines["left"].set_position(('data', K))

    plt.tight_layout()

    if save_path:
        plt.savefig(save_path, dpi=300, bbox_inches='tight')
        print(f"Plot saved to: {save_path}")

    plt.show()

    print(f"\nKey Statistics at Current Stock Price (S = ${K}):")
    print(f"  Call Option Price:    ${current_call_price:.2f}")
    print(f"  Put Option Price:     ${current_put_price:.2f}")
    print(f"  Call Time Value:      ${current_call_price:.2f}")
    print(f"  Put Time Value:       ${current_put_price:.2f}")
    print(f"  Put-Call Parity:      C - P = S - K*e^(-rT)")

    parity_left = current_call_price - current_put_price
    parity_right = K - K * np.exp(-r * T)
    print(f"    Left side:          ${parity_left:.2f}")
    print(f"    Right side:         ${parity_right:.2f}")
    print(f"    Difference:         ${abs(parity_left - parity_right):.2f}")

print(f"\n{'='*80}")
print("ENHANCED OPTION ANALYSIS")
print("="*80)

create_enhanced_option_plot(
    S_range=(50, 150),
    K=100,
    T=1/12,
    r=0.05,
    sigma=0.3
)

print(f"\n{'='*80}")
print("WRAPPER USAGE EXAMPLES")
print("="*80,end="\n"*2)

print("Example 1: At-the-money options (S = $100)")
atm_model = bs.BlackScholes(S0=100, K=100, T=1/12, r=0.05, sigma=0.3)
call_atm, put_atm = atm_model.price_analytical()
greeks_atm = atm_model.calculate_greeks()

print(f"  Call price: ${call_atm:.2f}")
print(f"  Put price:  ${put_atm:.2f}")
print(f"  Call delta: {greeks_atm['delta_call']:.4f}")
print(f"  Put delta:  {greeks_atm['delta_put']:.4f}")
print(f"  Gamma:      {greeks_atm['gamma']:.4f}")
print(f"  Vega:       {greeks_atm['vega']:.4f}")

print(f"\nExample 2: In-the-money call (S = $120)")
itm_call_model = bs.BlackScholes(S0=120, K=100, T=1/12, r=0.05, sigma=0.3)
call_itm, put_itm = itm_call_model.price_analytical()
greeks_itm = itm_call_model.calculate_greeks()

print(f"  Call price: ${call_itm:.2f}")
print(f"  Put price:  ${put_itm:.2f}")
print(f"  Call delta: {greeks_itm['delta_call']:.4f}")
print(f"  Intrinsic:  ${max(120-100, 0):.2f}")
print(f"  Time value: ${call_itm - max(120-100, 0):.2f}")

print(f"\nExample 3: Out-of-the-money put (S = $120)")
print(f"  Put price:  ${put_itm:.2f}")
print(f"  Put delta:  {greeks_itm['delta_put']:.4f}")
print(f"  Intrinsic:  ${max(100-120, 0):.2f}")
print(f"  Time value: ${put_itm - max(100-120, 0):.2f}")
print()

```

Exercises

Exercise 1. Plot the Black-Scholes call price \(C(S)\) as a function of stock price \(S\) for \(K = 100\), \(T = 1\), \(r = 0.05\), \(\sigma = 0.20\). Describe the shape and identify the intrinsic value and time value components.

Solution to Exercise 1

The call price \(C(S)\) is a smooth, increasing, convex curve. For \(S \ll K\), \(C \approx 0\) (deep OTM). For \(S \gg K\), \(C \approx S - Ke^{-rT}\) (deep ITM, approaching the discounted intrinsic value).

The intrinsic value is \(\max(S - K, 0)\) -- the payoff if exercised immediately. The time value is \(C(S) - \max(S - K, 0)\), which is always non-negative. Time value is maximized near ATM (\(S \approx K\)) and approaches zero deep ITM or OTM. It represents the optionality: the chance of favorable stock movements before expiration.


Exercise 2. How does the call price curve change as \(T\) decreases toward zero? Sketch \(C(S)\) for \(T = 1\), \(T = 0.1\), and \(T = 0.01\).

Solution to Exercise 2

As \(T \to 0\), the call price converges to the intrinsic value \(\max(S - K, 0)\), forming a hockey-stick shape. The time value (the smooth rounding near \(S = K\)) shrinks:

  • \(T = 1\): Broad, smooth curve with significant time value.
  • \(T = 0.1\): Sharper bend near \(S = K\), less time value.
  • \(T = 0.01\): Nearly a piecewise linear function matching the payoff.

This convergence reflects the decreasing probability of large moves as time runs out.


Exercise 3. For a fixed \(S = 100\) and \(K = 100\), plot the call price as a function of volatility \(\sigma \in [0.05, 0.80]\). Is the relationship linear?

Solution to Exercise 3

The call price increases monotonically with \(\sigma\) (positive vega). The relationship is approximately linear for moderate \(\sigma\) but becomes sublinear for very high \(\sigma\).

Near \(\sigma = 0\): \(C \approx \max(S - Ke^{-rT}, 0)\) (deterministic limit). As \(\sigma \to \infty\): \(C \to S\) (the option is worth the entire stock price because massive upside is possible). The curve is concave (increasing at a decreasing rate).


Exercise 4. Explain the financial interpretation of the BS call price as \(C = S\mathcal{N}(d_1) - Ke^{-rT}\mathcal{N}(d_2)\). What do \(\mathcal{N}(d_1)\) and \(\mathcal{N}(d_2)\) represent?

Solution to Exercise 4
  • \(\mathcal{N}(d_2)\) is the risk-neutral probability that the option expires in the money: \(P^Q(S_T > K)\).
  • \(Ke^{-rT}\mathcal{N}(d_2)\) is the present value of the expected payment if the option is exercised.
  • \(\mathcal{N}(d_1)\) is the delta-weighted probability (under the stock numeraire measure): it accounts for the correlation between the stock price and the exercise event.
  • \(S\mathcal{N}(d_1)\) is the present value of receiving the stock conditional on exercise.

The call price is the difference: what you expect to receive (stock) minus what you expect to pay (strike), both conditional on exercise and discounted.