期权定价Python实现

期权定价公式

期权定价通常使用BSM公式,如下所示:
C ( S t , K , t , T , r , σ ) = S t N ( d 1 ) − e − r ( T − t ) K N ( d 2 ) (1.1) C(S_{t}, K, t, T, r, \sigma)=S_{t} N(d_{1})-e^{-r(T-t)}KN(d_{2}) \tag{1.1} C(St,K,t,T,r,σ)=StN(d1)er(Tt)KN(d2)(1.1)
其中 N ( d ) N(d) N(d)为标准正态分布 x ∼ N ( 0 , 1 ) x \sim N(0,1) xN(0,1) x < d x<d x<d时的概率,如下所示:
N ( d ) = 1 2 π ∫ − ∞ d e − 1 2 x 2 d x (1.2) N(d)=\frac{1}{\sqrt{2\pi}} \int^{d}_{-\infty} e^{-\frac{1}{2}x^{2}}dx \tag{1.2} N(d)=2π 1de21x2dx(1.2)
式中 d 1 d_{1} d1 d 2 d_{2} d2为两个常数,定义为:
d 1 = log ⁡ S t K + ( r + σ 2 2 ) ( T − t ) σ T − t (1.3) d_{1}=\frac{\log \frac{S_{t}}{K} + (r+\frac{\sigma^{2}}{2})(T-t) }{\sigma \sqrt{T-t}} \tag{1.3} d1=σTt logKSt+(r+2σ2)(Tt)(1.3)
d 2 = log ⁡ S t K + ( r − σ 2 2 ) ( T − t ) σ T − t (1.4) d_{2}=\frac{\log \frac{S_{t}}{K} + (r-\frac{\sigma ^{2}}{2})(T-t)}{\sigma \sqrt{T-t}} \tag{1.4} d2=σTt logKSt+(r2σ2)(Tt)(1.4)
上式中的符号说明如下所示:

  • C:期权价格;
  • S t S_{t} St:标的资产价格;
  • K:期权行权价格;
  • t:当前时间;
  • T:期权到期时间;
  • r:短期无风险利率;
  • σ \sigma σ:波动率;

迭代法

我们在初始状态下,先设定 σ \sigma σ为一个随机值 σ 0 i m p \sigma ^{imp}_{0} σ0imp,其中上标imp代表是隐含波动率,下标代表是第0步。将 σ 0 i m p \sigma ^{imp} _{0} σ0imp代入上面的公式,就可以求出一个期权的计算值 C ^ \hat{C} C^,其与当前市场上的期权价格 C C C的差值可以用来代表我们当初随机取的 σ 0 i m p \sigma ^{imp}_{0} σ0imp接近真实值的程度。
我们可以通过牛顿法来求解,我们首先求出 ∂ C ∂ σ \frac{\partial{C}}{\partial{\sigma}} σC
v e g a = ∂ C ∂ σ = S t N ′ ( d 1 ) ) T − t (1.5) vega=\frac{\partial{C}}{\partial{\sigma}} = S_{t} N'(d_{1})) \sqrt{T-t} \tag{1.5} vega=σC=StN(d1))Tt (1.5)
我们可以通过迭代法来找到更好的 σ \sigma σ的值:
σ n + 1 i m p = σ n i m p − 1 v e g a ( C ^ n − C ) (1.6) \sigma ^{imp}_{n+1} = \sigma ^{imp}_{n} - \frac{1}{vega}(\hat{C}_{n}-C) \tag{1.6} σn+1imp=σnimpvega1(C^nC)(1.6)
上式中 n n n代表迭代步数,C代表期权的真实价格, C ^ n \hat{C}_{n} C^n为当前的计算值。

代码实现

我们首先定义期权估值的BS模型类,如下所示:

#
import math
from enum import Enum,unique
from scipy.stats import norm

@unique
class DMode(Enum):
    D1 = 1
    D2 = 2

class BsModel(object):
    def __init__(self):
        self.name = 'fak.option.bs_model.BsModel'
    
    @staticmethod
    def calculate_C(St, K, t, T, r, sigma):
        '''
        已知波动率计算期权价格的BS公式
        '''
        d1 = BsModel.calculate_d1(St, K, t, T, r, sigma)
        d2 = BsModel.calculate_d2(St, K, t, T, r, sigma)
        nd1 = BsModel.calculate_nd(d1)
        nd2 = BsModel.calculate_nd(d2)
        return St * nd1 - math.exp(-r*(T-t)) * K * nd2

    @staticmethod
    def calculate_vega(St, K, t, T, r, sigma):
        '''
        计算期权价格对d1的导数,即希腊字母vega的值
        '''
        d1 = BsModel.calculate_d1(St, K, t, T, r, sigma)
        nd1p = BsModel.calculate_ndp(d1)
        return St * nd1p * math.sqrt(T-t)

    @staticmethod
    def calculate_d1(St, K, t, T, r, sigma):
        return BsModel.calculate_d(St, K, t, T, r, sigma, DMode.D1)

    @staticmethod
    def calculate_d2(St, K, t, T, r, sigma):
        return BsModel.calculate_d(St, K, t, T, r, sigma, DMode.D2)

    @staticmethod
    def calculate_d(St, K, t, T, r, sigma, mode):
        if DMode.D1 == mode:
            numerator = math.log(St / K)  + (r + sigma*sigma / 2.0)*(T - t)
        else:
            numerator = math.log(St / K) + (r - sigma*sigma / 2.0)*(T - t)
        denominator = sigma * math.sqrt(T-t)
        return numerator / denominator
        
    @staticmethod
    def calculate_nd(x0, mu=0.0, sigma=1.0):
        '''
        计算由均值为mu,方差为sigma的正态分布下,x<=x0时的概率
        '''
        return norm.cdf(x0, loc=mu, scale=sigma)

    @staticmethod
    def calculate_ndp(x0, mu=0.0, sigma=1.0):
        return norm.pdf(x0, loc=mu, scale=sigma)

代码解读如下所示:

  • 第6~9行:定义枚举类来表示BS公式中计算d1还是d2;
  • 第16~24行:根据式 ( 1.1 ) (1.1) (1.1)计算给定波动率条件下期权价格;
  • 第27~33行:根据式 ( 1.5 ) (1.5) (1.5)计算期权价格对波动率的导数,即希腊字母vega;
  • 第36~50行:计算BS公式中的d1和d2的值,通过mode来区分;
  • 第53~57行:计算式 ( 1.1 ) (1.1) (1.1) N ( d ) N(d) N(d),该项表示正态分布下值小于d的概率,需要用norm.cdf来计算;
  • 第60、61行:计算式 ( 1.5 ) (1.5) (1.5)中N(d)的导数,我们用norm.pdf来计算;
    接下来,我们先假设我们知道波动率 σ \sigma σ,然后可以得到对应的期权价格 C C C,然后我们随机给一个 σ \sigma σ的值,就可以计算出期权价格的计算值 C t i m p C^{imp}_{t} Ctimp,接下来我们求出vega,然后根据式 ( 1.6 ) (1.6) (1.6)求出新的波动率 σ t i m p \sigma^{imp}_{t} σtimp,循环上述过程,我们就可以求出波动率的真实值,在本例中就是我们初始时给定的波动率的值,代码如下所示:
    def startup(self):
        # 初始化变量
        St = 2100.0 # 标的资产价格
        K = 2200.0 # 行权价格
        t = 10 # 当前时间
        T = 30 # 到期时间
        r = 0.01 # 短期无风险利率
        sigma = 0.3 # 波动率
        # 假设已知波动率的情况下计算期权价格和vega
        C = BsModel.calculate_C(St, K, t, T, r, sigma)
        vega = BsModel.calculate_vega(St, K, t, T, r, sigma)
        # 随机给出一个波动率,用迭代法求出波动率真值
        sigma_t = 1200
        for n in range(10000):
            C_t = BsModel.calculate_C(St, K, t, T, r, sigma_t)
            vega_t = BsModel.calculate_vega(St, K, t, T, r, sigma_t)
            sigma_t = sigma_t - 1/vega * (C_t - C)
            print('C0={0}; vega={1}; sigma={2};'.format(C_t, vega_t, sigma_t))
        print('真实值:C={0}; vega={1};'.format(C, vega))

运行结果如下所示:
在这里插入图片描述
如上图所示,我们经过1000步迭代, σ \sigma σ从我们最初给定的随机值1200,逐步收敛到我们初始时设定的真实0.3,这正是我们想要的结果。

  • 13
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值