生生不息

生生不息

AI使用蒙特卡洛模拟对欧洲期权定价

2025-06-29
AI使用蒙特卡洛模拟对欧洲期权定价

提示词

需要演示使用模拟方法对欧洲期权进行定价,请给出示例代码。

答案

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import norm
from tqdm import tqdm

class OptionPricing:
    """
    欧洲期权定价工具类
    支持蒙特卡洛模拟、Black-Scholes解析解和二叉树模型
    """
  
    def __init__(self, S0, K, T, r, sigma, option_type='call'):
        """
        初始化期权参数
        S0: 标的资产现价
        K: 行权价
        T: 到期时间(年)
        r: 无风险利率
        sigma: 年化波动率
        option_type: 'call'或'put'
        """
        self.S0 = S0
        self.K = K
        self.T = T
        self.r = r
        self.sigma = sigma
        self.option_type = option_type.lower()
      
        # 验证期权类型
        if self.option_type not in ['call', 'put']:
            raise ValueError("期权类型必须是'call'或'put'")
          
    def black_scholes(self):
        """计算Black-Scholes期权价格"""
        d1 = (np.log(self.S0 / self.K) + (self.r + 0.5 * self.sigma**2) * self.T) / (self.sigma * np.sqrt(self.T))
        d2 = d1 - self.sigma * np.sqrt(self.T)
      
        if self.option_type == 'call':
            price = self.S0 * norm.cdf(d1) - self.K * np.exp(-self.r * self.T) * norm.cdf(d2)
        else:  # put
            price = self.K * np.exp(-self.r * self.T) * norm.cdf(-d2) - self.S0 * norm.cdf(-d1)
          
        return price
  
    def monte_carlo(self, n_simulations=10000, n_steps=252):
        """
        蒙特卡洛模拟期权定价
        n_simulations: 模拟路径数量
        n_steps: 路径步数
        """
        dt = self.T / n_steps
        price_paths = np.zeros((n_simulations, n_steps + 1))
        price_paths[:, 0] = self.S0
      
        # 生成布朗运动
        rv = np.random.normal(size=(n_simulations, n_steps))
      
        # 模拟价格路径
        for t in range(1, n_steps + 1):
            z = rv[:, t-1]
            price_paths[:, t] = price_paths[:, t-1] * np.exp(
                (self.r - 0.5 * self.sigma**2) * dt + 
                self.sigma * np.sqrt(dt) * z
            )
      
        # 计算到期日收益
        if self.option_type == 'call':
            payoffs = np.maximum(price_paths[:, -1] - self.K, 0)
        else:  # put
            payoffs = np.maximum(self.K - price_paths[:, -1], 0)
      
        # 贴现收益
        option_price = np.exp(-self.r * self.T) * np.mean(payoffs)
        stderr = np.exp(-self.r * self.T) * np.std(payoffs) / np.sqrt(n_simulations)
      
        # 保存模拟路径供可视化使用
        self.price_paths = price_paths
        self.payoffs = payoffs
        self.mc_price = option_price
      
        return option_price, stderr
  
    def binomial_tree(self, n_steps=100):
        """
        二叉树模型期权定价
        n_steps: 二叉树步数
        """
        dt = self.T / n_steps
        u = np.exp(self.sigma * np.sqrt(dt))        # 上涨因子
        d = 1 / u                                  # 下跌因子
        p = (np.exp(self.r * dt) - d) / (u - d)     # 风险中性概率
      
        # 初始化到期收益
        stock_prices = np.zeros(n_steps + 1)
        option_values = np.zeros(n_steps + 1)
      
        # 计算到期时的股票价格和期权价值
        for j in range(n_steps + 1):
            stock_prices[j] = self.S0 * (u ** j) * (d ** (n_steps - j))
          
            if self.option_type == 'call':
                option_values[j] = max(stock_prices[j] - self.K, 0)
            else:  # put
                option_values[j] = max(self.K - stock_prices[j], 0)
      
        # 向后推导期权当前价值
        for i in range(n_steps - 1, -1, -1):
            for j in range(i + 1):
                option_values[j] = np.exp(-self.r * dt) * (p * option_values[j + 1] + (1 - p) * option_values[j])
      
        return option_values[0]
  
    def visualize(self):
        """可视化蒙特卡洛模拟结果"""
        if not hasattr(self, 'price_paths') or not hasattr(self, 'payoffs'):
            raise RuntimeError("请先运行蒙特卡洛模拟")
          
        # 创建画布
        plt.figure(figsize=(15, 10))
      
        # 1. 模拟价格路径
        plt.subplot(2, 2, 1)
        for i in range(min(100, len(self.price_paths))):
            plt.plot(self.price_paths[i], lw=1, alpha=0.3, color='blue')
        plt.axhline(self.K, color='red', linestyle='--', label='行权价 (K)')
        plt.title(f'蒙特卡洛模拟路径 ({len(self.price_paths)}条路径)')
        plt.xlabel('时间步')
        plt.ylabel('价格')
        plt.legend()
        plt.grid(alpha=0.2)
      
        # 2. 到期价格分布
        plt.subplot(2, 2, 2)
        plt.hist(self.price_paths[:, -1], bins=50, color='skyblue', edgecolor='black', alpha=0.8)
        plt.axvline(self.K, color='red', linestyle='--', label='行权价 (K)')
        plt.title('到期价格分布')
        plt.xlabel('到期价格')
        plt.ylabel('频数')
        plt.legend()
        plt.grid(alpha=0.2)
      
        # 3. 收益分布
        plt.subplot(2, 2, 3)
        plt.hist(self.payoffs, bins=50, color='lightgreen', edgecolor='black', alpha=0.8)
        plt.title('期权收益分布')
        plt.xlabel('收益')
        plt.ylabel('频数')
        plt.grid(alpha=0.2)
      
        # 4. 收敛分析
        plt.subplot(2, 2, 4)
        cum_avg_payoffs = np.cumsum(self.payoffs) / np.arange(1, len(self.payoffs) + 1)
        discount_factor = np.exp(-self.r * self.T)
        cumulative_price = discount_factor * cum_avg_payoffs
      
        bs_price = self.black_scholes()
      
        plt.plot(cumulative_price, label='蒙特卡洛价格')
        plt.axhline(bs_price, color='red', linestyle='--', label='Black-Scholes价格')
        plt.axhline(self.mc_price, color='purple', linestyle='-', label='最终蒙特卡洛价格')
        plt.title(f'蒙特卡洛收敛性 (N={len(self.payoffs)})')
        plt.xlabel('模拟次数')
        plt.ylabel('期权价格')
        plt.legend()
        plt.grid(alpha=0.2)
      
        plt.tight_layout()
        plt.savefig('option_pricing_monte_carlo.png', dpi=300)
        plt.show()

# ==========================================
# 测试与比较
# ==========================================

# 设置参数
params = {
    'S0': 100,      # 当前股票价格
    'K': 105,        # 行权价
    'T': 1,          # 到期时间(年)
    'r': 0.05,       # 无风险利率
    'sigma': 0.2,    # 年化波动率
    'option_type': 'call'  # 期权类型: call或put
}

# 创建定价器
option_pricer = OptionPricing(**params)

# 计算Black-Scholes价格
bs_price = option_pricer.black_scholes()
print(f"Black-Scholes价格: {bs_price:.4f}")

# 蒙特卡洛模拟
mc_price, stderr = option_pricer.monte_carlo(n_simulations=10000)
print(f"蒙特卡洛价格: {mc_price:.4f} (标准差: {stderr:.4f})")
print(f"与BS模型的差异: {abs(bs_price - mc_price):.4f} ({abs(bs_price - mc_price)/bs_price*100:.2f}%)")

# 二叉树模型
binomial_price = option_pricer.binomial_tree(n_steps=500)
print(f"二叉树价格(500步): {binomial_price:.4f}")

# 可视化蒙特卡洛结果
option_pricer.visualize()

# ==========================================
# 高级分析: 不同方法收敛性对比
# ==========================================

# 1. 不同模拟次数下的蒙特卡洛价格
simulations = np.arange(100, 100001, 1000)
mc_prices = []
bs_price = option_pricer.black_scholes()

print("\n蒙特卡洛收敛分析...")
for n in tqdm(simulations):
    price, _ = option_pricer.monte_carlo(n_simulations=n)
    mc_prices.append(price)

# 2. 不同步数下的二叉树价格
steps = np.arange(10, 1001, 10)
binomial_prices = []

print("\n二叉树收敛分析...")
for n in tqdm(steps):
    price = option_pricer.binomial_tree(n_steps=n)
    binomial_prices.append(price)

# 3. 可视化收敛性比较
plt.figure(figsize=(14, 10))

# 蒙特卡洛收敛
plt.subplot(2, 1, 1)
plt.plot(simulations, mc_prices, 'b-', label='蒙特卡洛价格')
plt.axhline(bs_price, color='r', linestyle='--', label='Black-Scholes')
plt.title('蒙特卡洛模拟收敛性')
plt.xlabel('模拟路径数量')
plt.ylabel('期权价格')
plt.legend()
plt.grid(alpha=0.2)

# 二叉树收敛
plt.subplot(2, 1, 2)
plt.plot(steps, binomial_prices, 'g-', label='二叉树价格')
plt.axhline(bs_price, color='r', linestyle='--', label='Black-Scholes')
plt.title('二叉树模型收敛性')
plt.xlabel('二叉树步数')
plt.ylabel('期权价格')
plt.legend()
plt.grid(alpha=0.2)

plt.tight_layout()
plt.savefig('option_pricing_convergence.png', dpi=300)
plt.show()

# ==========================================
# 希腊字母计算
# ==========================================

def calculate_greeks(option, epsilon=0.01):
    """计算期权的Delta和Gamma"""
    # 原始价格
    price = option.black_scholes()
  
    # Delta计算
    opt_up = OptionPricing(
        S0=option.S0 * (1 + epsilon),
        K=option.K,
        T=option.T,
        r=option.r,
        sigma=option.sigma,
        option_type=option.option_type
    )
    price_up = opt_up.black_scholes()
  
    opt_down = OptionPricing(
        S0=option.S0 * (1 - epsilon),
        K=option.K,
        T=option.T,
        r=option.r,
        sigma=option.sigma,
        option_type=option.option_type
    )
    price_down = opt_down.black_scholes()
  
    delta = (price_up - price_down) / (2 * epsilon * option.S0)
  
    # Gamma计算
    gamma = (price_up - 2 * price + price_down) / (epsilon**2 * option.S0**2)
  
    return {'Delta': delta, 'Gamma': gamma}

# 计算示例希腊字母
greeks = calculate_greeks(option_pricer)
print(f"\n期权希腊字母:")
print(f"Delta: {greeks['Delta']:.4f}")
print(f"Gamma: {greeks['Gamma']:.4f}")

# ==========================================
# 参数敏感性分析
# ==========================================

def sensitivity_analysis(option, param, values):
    """计算不同参数下的期权价格"""
    prices = []
    orig_value = getattr(option, param)
  
    for val in values:
        setattr(option, param, val)
        prices.append(option.black_scholes())
  
    # 恢复原始值
    setattr(option, param, orig_value)
    return prices

# 波动率敏感性分析
vols = np.linspace(0.1, 0.5, 50)
vol_prices = sensitivity_analysis(option_pricer, 'sigma', vols)

# 到期时间敏感性分析
times = np.linspace(0.1, 2, 50)
time_prices = sensitivity_analysis(option_pricer, 'T', times)

# 可视化
plt.figure(figsize=(14, 6))

plt.subplot(1, 2, 1)
plt.plot(vols, vol_prices, 'b-')
plt.title('波动率敏感性')
plt.xlabel('波动率(σ)')
plt.ylabel('期权价格')
plt.grid(alpha=0.3)

plt.subplot(1, 2, 2)
plt.plot(times, time_prices, 'g-')
plt.title('到期时间敏感性')
plt.xlabel('到期时间(年)')
plt.ylabel('期权价格')
plt.grid(alpha=0.3)

plt.tight_layout()
plt.savefig('option_sensitivity.png', dpi=300)
plt.show()

结果:

提示词没有写模拟方法,好家伙,3种方法。不错。