一、期权的概念

期权是一种金融衍生工具,赋予持有人在未来某一特定日期或之前以预定价格买入或卖出标的资产的权利,但不承担必须买卖的义务。期权主要分为看涨期权(Call Option)和看跌期权(Put Option)。

这个视频讲的不错:

【从零开始学期权】期权是什么?期权的基础概念和特性(第一集)

1.1 期权的基本要素

  1. 标的资产:期权合约所涉及的资产,可以是股票、指数、商品、货币等。
  2. 行权价格(Strike Price):期权持有人可以在期权到期日或之前买入或卖出标的资产的价格。
  3. 到期日(Expiration Date):期权合约的有效期限,超过此日期期权将失效。
  4. 期权费(Premium):购买期权时支付的费用,是期权的价格。

简单来说,(看涨期权)你可以拿钱去卖方买期权(相当于一张合约、一张合同),你买要花钱,这个就是期权的期权费。现在拥有了一个期权合约,这个合约的作用就是,约定了一个价格(成为行权价格、执行价格),在合约到期(到期日)之前,你有权利以这个价格向卖家买期权合约对应的标的物(标的物可以是股票、指数、商品等等东西),当然了你也可以不买;但是卖家(卖你期权的人)必须(有义务)满足你的权利(以约定的价格即执行价格购买标的物)。

你怎么赚钱呢?

  • 买期权的时候,期权合约不是约定了一个执行价格嘛,就是你可以以这个价格买标的物,但是你买了期权后过了几天,标的物的价格上涨(市场价格浮动嘛)了,那你就可以以低价买高价的东西了(卖家必须卖给你,他交了押金的);
  • 或者你不买标的物,你发现期权的价格上涨了,你就可以把期权卖给别人。
  • 总之就是赚差价,但要注意,期权合约是有时间限制的,超过了约定的到期日,这个合约就没用用了。

当然了,要是标的物价格下跌了、期权价格下跌了,你就亏啦,但最多也就亏个本金嘛,就是你买期权合约花的钱;理论上赚钱是无上限的。

1.2 看涨期权和看跌期权

  • 看涨期权(Call Option):赋予持有人在未来某一日期或之前以特定价格买入标的资产的权利。买入看涨期权的投资者预期标的资产价格将上涨。
  • 看跌期权(Put Option):赋予持有人在未来某一日期或之前以特定价格卖出标的资产的权利。买入看跌期权的投资者预期标的资产价格将下跌。

这两种期权又叫认购期权认沽期权

前面我详细讲解的是看涨期权。

认沽期权也是类似的,标的物价格下跌,你就可以以高价(执行价格)卖给期权卖家了;或者期权价格上涨,你也可以转差价。

欧式期权,只能在到期日行使权利(以执行价格买卖标的物),美式期权则不必。但大多数人,不选择行使权利,而是直接买卖期权(平仓),这样利润更大(不展开解释了)。

期权还有时间价值,简单来说就是离到期日越远,期权的价格、标的物的价格有更多的波动可能性,潜在的价值更高。

此外,你也可以做期权卖家,去卖这2种期权。但这时,你的亏损理论上无限大。

1.3 期权的操作策略

  1. 买入看涨期权(Long Call):预期标的资产价格会上涨,投资者买入看涨期权。
  2. 买入看跌期权(Long Put):预期标的资产价格会下跌,投资者买入看跌期权。
  3. 卖出看涨期权(Short Call):预期标的资产价格不会上涨,投资者卖出看涨期权。
  4. 卖出看跌期权(Short Put):预期标的资产价格不会下跌,投资者卖出看跌期权。

1.4 期权的定价模型

期权定价的主要模型有:

  1. Black-Scholes模型:这是最早也是最著名的期权定价模型,基于无套利理论和对数正态分布假设。
  2. 二叉树模型(Binomial Model):使用树状结构模拟标的资产价格变化,适用于美式期权。
  3. 蒙特卡罗模拟(Monte Carlo Simulation):通过大量随机模拟路径计算期权价格,适用于复杂期权。

1.5 期权的风险和收益

  • 风险:买入期权的最大损失是期权费,而卖出期权的潜在损失是无限的。
  • 收益:买入看涨期权的收益是标的资产价格减去行权价格和期权费;买入看跌期权的收益是行权价格减去标的资产价格和期权费。

二、期权定价模型和隐含波动率计算

常见的期权定价模型有 Black-Scholes 模型,其公式如下:

对于欧式看涨期权(Call Option)的定价公式是:

C = S 0 ⋅ N ( d 1 ) − K ⋅ e − r T ⋅ N ( d 2 ) C = S_0 \cdot N(d_1) - K \cdot e^{-rT} \cdot N(d_2) C=S0N(d1)KerTN(d2)

对于欧式看跌期权(Put Option)的定价公式是:

P = K ⋅ e − r T ⋅ N ( − d 2 ) − S 0 ⋅ N ( − d 1 ) P = K \cdot e^{-rT} \cdot N(-d_2) - S_0 \cdot N(-d_1) P=KerTN(d2)S0N(d1)

其中, d 1 d_1 d1 d 2 d_2 d2 的计算公式为:

d 1 = ln ⁡ ( S 0 K ) + ( r + σ 2 2 ) T σ T d_1 = \frac{\ln\left(\frac{S_0}{K}\right) + \left(r + \frac{\sigma^2}{2}\right)T}{\sigma\sqrt{T}} d1=σT ln(KS0)+(r+2σ2)T

d 2 = d 1 − σ T d_2 = d_1 - \sigma\sqrt{T} d2=d1σT

符号解释:

  • C C C: 看涨期权价格
  • P P P: 看跌期权价格
  • S 0 S_0 S0: 标的资产当前价格
  • K K K: 行权价格
  • r r r: 无风险利率
  • σ \sigma σ: 标的资产的波动率(隐含波动率)
  • T T T: 到期时间
  • N ( ⋅ ) N(\cdot) N(): 标准正态分布函数的累积分布函数

这些公式是基于以下假设的:

  1. 标的资产价格遵循几何布朗运动。
  2. 无风险利率和波动率在期权有效期内保持不变。
  3. 不考虑红利支付。
  4. 市场是无摩擦的(即没有交易成本、税收等)。

根据前面的公式就可以求解隐含波动率,常用的方法有牛顿法、二分法。

三、代码示例

以纳斯达克100指数期权为例,简写NDX。

假设今天是2024-6-15,标的物价格为:19659.8,无风险利率为5.53%,期权价格为 A s k + B i d 2 \frac{Ask+Bid}{2} 2Ask+Bid

到期日为2024-6-21的期权数据:(Skrike:执行价格,Ticker:合约代码,Bid:买方愿出最高价,Ask:买方可就是最低价,Last:最近成交价,Volm:交易量)

CallsPuts
StrikeTickerBidAskLastVolmStrikeTickerBidAskLastVolm
19530NDX 6/21/24 C19530214.1000061229.8000031165.3999939019530NDX 6/21/24 P195306365.4000015367.300003052
19540NDX 6/21/24 C19540206.6000061222160.6499939019540NDX 6/21/24 P1954059.7999992468.1999969570.099998473
19550NDX 6/21/24 C19550199.3000031214.3999939174.47999572919550NDX 6/21/24 P1955062.570.9000015372.699996952
19560NDX 6/21/24 C19560192.1999969207.6000061140219560NDX 6/21/24 P1956065.6999969573.8000030575.900001532
19570NDX 6/21/24 C19570184.8999939200.8999939132.3500061019570NDX 6/21/24 P1957068.5999984776.8000030579.199996958
19575NDX 6/21/24 C19575181.5197.51642519575NDX 6/21/24 P1957569.6999969578.4000015380.300003054
19580NDX 6/21/24 C19580178.1999969194.3000031159.6199951619580NDX 6/21/24 P1958071.300003058086.8499984710
19590NDX 6/21/24 C19590171.1999969187.5140.1999969119590NDX 6/21/24 P1959074.8000030583.4000015385.400001536
19600NDX 6/21/24 C19600164.6000061180.5158.72000121819600NDX 6/21/24 P1960077.8000030586.599998479554
19610NDX 6/21/24 C19610158173.8999939160619610NDX 6/21/24 P1961081.3000030590.1999969587.6299972520
19620NDX 6/21/24 C19620152161.5125.54119620NDX 6/21/24 P196208593.80000305103.110000643
19625NDX 6/21/24 C19625148.8999939158.30000311481819625NDX 6/21/24 P1962586.9000015395.80000305105.150001522
19630NDX 6/21/24 C19630145.6999969155.199996984.19999695019630NDX 6/21/24 P1963088.6999969597.5999984700
19640NDX 6/21/24 C19640139.6000061149135.27000431619640NDX 6/21/24 P1964092.59999847104.5106.510
19650NDX 6/21/24 C19650133.5142.8999939130.89999392119650NDX 6/21/24 P1965096.5105.800003110912
19660NDX 6/21/24 C19660128137.1000061126.5500031519660NDX 6/21/24 P19660100.6999969109.8000031130.51
19670NDX 6/21/24 C19670121.9000015131.1999969111.87999731819670NDX 6/21/24 P19670104.9000015114.0999985145.39999390
19675NDX 6/21/24 C19675119.5999985128.3000031109.1999969619675NDX 6/21/24 P19675107.1999969116.1999969157.39999390
19680NDX 6/21/24 C19680116.0999985125.590.41999817019680NDX 6/21/24 P19680109.6999969118.599998500
19690NDX 6/21/24 C19690110.699996912081.30000305019690NDX 6/21/24 P19690114.4000015123.099998500
19700NDX 6/21/24 C19700105.5114.599998595.470001221019700NDX 6/21/24 P19700119.0999985127.9000015160.55000316
19710NDX 6/21/24 C19710100.3000031109.400001591.80000305419710NDX 6/21/24 P19710123.9000015132.899993900
19720NDX 6/21/24 C1972096104.400001559.31999969019720NDX 6/21/24 P19720128.8999939137.8999939161.39999393
19725NDX 6/21/24 C1972593.19999695101.900001584.05000305119725NDX 6/21/24 P19725131.3999939140.300003100
19730NDX 6/21/24 C1973090.6999969599.580.09999847619730NDX 6/21/24 P19730134.1000061143.199996900
19740NDX 6/21/24 C1974086.3000030594.699996950019740NDX 6/21/24 P19740139.1999969148.500
19750NDX 6/21/24 C1975081.3000030590.0999984780.800003051219750NDX 6/21/24 P19750144.6999969154.1000061198.30000311
19760NDX 6/21/24 C1976076.9000015385.6999969564.5119760NDX 6/21/24 P19760150.1000061165.800003100
19770NDX 6/21/24 C1977073.1999969581.3000030563.40000153419770NDX 6/21/24 P19770155.8000031171.399993900
19775NDX 6/21/24 C1977571.0999984779.3000030568.40000153119775NDX 6/21/24 P19775158.6999969174.199996900
19780NDX 6/21/24 C197806977.1999969560119780NDX 6/21/24 P19780161.600006117700

import pandas as pd
import numpy as np
from scipy.stats import norm
from scipy.optimize import newton
from datetime import datetime
import matplotlib.pyplot as plt


class OptionAnalysis:
    def __init__(self, file_path):
        self.file_path = file_path
        self.S = 19659.80
        self.r = 0.0553
        self.current_date = datetime(2024, 6, 15)
        self.data = pd.read_excel(file_path, sheet_name=0, header=[0, 1])

    def bs_price(self, S, K, T, r, sigma, option_type='call'):
        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':
            return S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2)
        return K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1)

    def implied_volatility(self, option_price, S, K, T, r, option_type='call'):
        def difference(sigma):
            return self.bs_price(S, K, T, r, sigma, option_type) - option_price
        return newton(difference, 0.2, tol=1e-5)

    def process_data(self):
        strike_calls = self.data[('Calls', 'Strike')].dropna().values
        bid_calls = self.data[('Calls', 'Bid')].dropna().values
        ask_calls = self.data[('Calls', 'Ask')].dropna().values
        price_calls = (bid_calls + ask_calls) / 2

        strike_puts = self.data[('Puts', 'Strike')].dropna().values
        bid_puts = self.data[('Puts', 'Bid')].dropna().values
        ask_puts = self.data[('Puts', 'Ask')].dropna().values
        price_puts = (bid_puts + ask_puts) / 2

        expiry_date = datetime.strptime(self.data.index.name, '%m-%d-%y')
        T = (expiry_date - self.current_date).days / 365

        implied_vols_calls = [self.implied_volatility(price, self.S, K, T, self.r, 'call') for price, K in zip(price_calls, strike_calls)]
        implied_vols_puts = [self.implied_volatility(price, self.S, K, T, self.r, 'put') for price, K in zip(price_puts, strike_puts)]

        self.data[('Calls', 'Implied Volatility')] = pd.Series(implied_vols_calls, index=self.data.index[:len(implied_vols_calls)])
        self.data[('Puts', 'Implied Volatility')] = pd.Series(implied_vols_puts, index=self.data.index[:len(implied_vols_puts)])

        return strike_calls, implied_vols_calls, strike_puts, implied_vols_puts

    def plot_data(self):
        strike_calls, implied_vols_calls, strike_puts, implied_vols_puts = self.process_data()

        plt.figure(figsize=(14, 6))

        # Calls
        plt.subplot(1, 2, 1)
        plt.plot(strike_calls, implied_vols_calls, 'go-', label='Call Implied Volatility')
        plt.xlabel('Strike Price')
        plt.ylabel('Implied Volatility')
        plt.title('Call Implied Volatility vs Strike Prices')
        plt.grid(True)
        plt.legend()

        # Puts
        plt.subplot(1, 2, 2)
        plt.plot(strike_puts, implied_vols_puts, 'mo-', label='Put Implied Volatility')
        plt.xlabel('Strike Price')
        plt.ylabel('Implied Volatility')
        plt.title('Put Implied Volatility vs Strike Prices')
        plt.grid(True)
        plt.legend()

        plt.suptitle(f'NDX: Options Implied Volatility', color='blue')
        plt.tight_layout()
        plt.savefig('ndx_img/Options_volatility.png')


if __name__ == '__main__':
    file_path = 'NDX.xlsx'
    analysis = OptionAnalysis(file_path)
    analysis.plot_data()

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐