怎么计算隐含波动率?

论坛 期权论坛 期权     
期权匿名问答   2022-11-18 13:59   8509   5
在Python里怎么计算implied volatility?
分享到 :
0 人收藏

5 个回复

倒序浏览
2#
期权匿名回答  16级独孤 | 2022-11-18 13:59:39 发帖IP地址来自 四川遂宁
可以第三方免费python库(QuantLib/Mibian)计算实时计算期权的隐含波动率(implied volatility)及其希腊值,包括:

  • iv
  • delta
  • gamma
  • theta
  • vega
  • rpho
源代码见本文最后。
实时绘制损益曲线及希腊值曲线。


例如,再用plotly 可视化引擎,可以实时做出漂亮的波动率曲面。


参见:阿岛格:基于人工智能的量化投资系统(9)数据指标及可视化 的第3, 以及本文最后的视频。
演示视频


  • (视频) 阿岛格:低门槛搭建你的个人量化平台-演示
  • (视频) 阿岛格:打造个人的量化平台(期权)
专栏


  • 阿岛格专栏:低门槛搭建个人量化平台
  • 阿岛格专栏: 基于人工智能的量化投资
如有问题,请看个人主页扫码“ 知shi星球(阿岛格)”加入讨论。



源代码如下:
#coding=utf-8

'''
MibianLib - Options Pricing Open Source Library - http://code.mibian.net/
Copyright (C) 2011 Yassine Maaroufi - <yassinemaaroufi@mibian.net>
Distributed under GPLv3 - http://www.gnu.org/copyleft/gpl.html
'''
#from numba import jit,autojit
#from scipy.optimize import minimize.basinhopping,root,fsolve,rosen,

from scipy import optimize
#from scipy.optimize import fsolve
import numpy as np
from math import log, e
try:
    from scipy.stats import norm
except ImportError:
    print('Mibian requires scipy to work properly')



# WARNING: All numbers should be floats -> x = 1.0
#@autojit

#defined and improved by adog 20190613
def impliedVolatility(className, args, callPrice=None, putPrice=None, high=500.0, low=0.0):
    def fun(x):
        bs=BS(args, volatility=x, performance=True)
      
        if callPrice:
            if bs.callPrice:
                return bs.callPrice-callPrice
            else:
                return 0.-callPrice
        elif putPrice:
            if bs.putPrice:
                return bs.putPrice-putPrice
            else:
                return 0.-putPrice
    iv=0.
    try:
        res = optimize.root(fun,[0.],method='df-sane')
        if res.x and len(res.x)==1:
            if res.x>0.:
                iv=res.x[0]
    except Exception,e:
        print(e)
        
    return iv


class GK:
    '''Garman-Kohlhagen
    Used for pricing European options on currencies
   
    GK([underlyingPrice, strikePrice, domesticRate, foreignRate, \
            daysToExpiration], volatility=x, callPrice=y, putPrice=z)

    eg:
        c = mibian.GK([1.4565, 1.45, 1, 2, 30], volatility=20)
        c.callPrice                # Returns the call price
        c.putPrice                # Returns the put price
        c.callDelta                # Returns the call delta
        c.putDelta                # Returns the put delta
        c.callDelta2            # Returns the call dual delta
        c.putDelta2                # Returns the put dual delta
        c.callTheta                # Returns the call theta
        c.putTheta                # Returns the put theta
        c.callRhoD                # Returns the call domestic rho
        c.putRhoD                # Returns the put domestic rho
        c.callRhoF                # Returns the call foreign rho
        c.putRhoF                # Returns the call foreign rho
        c.vega                    # Returns the option vega
        c.gamma                    # Returns the option gamma

        c = mibian.GK([1.4565, 1.45, 1, 2, 30], callPrice=0.0359)
        c.impliedVolatility        # Returns the implied volatility from the call price
        
        c = mibian.GK([1.4565, 1.45, 1, 2, 30], putPrice=0.03)
        c.impliedVolatility        # Returns the implied volatility from the put price
        
        c = mibian.GK([1.4565, 1.45, 1, 2, 30], callPrice=0.0359, putPrice=0.03)
        c.putCallParity            # Returns the put-call parity
    '''

    def __init__(self, args, volatility=None, callPrice=None, putPrice=None, \
            performance=None):
        self.underlyingPrice = float(args[0])
        self.strikePrice = float(args[1])
        self.domesticRate = float(args[2]) / 100
        self.foreignRate = float(args[3]) / 100
        self.daysToExpiration = float(args[4]) / 365

        for i in ['callPrice', 'putPrice', 'callDelta', 'putDelta', \
                'callDelta2', 'putDelta2', 'callTheta', 'putTheta', \
                'callRhoD', 'putRhoD', 'callRhoF', 'callRhoF', 'vega', \
                'gamma', 'impliedVolatility', 'putCallParity']:
            self.__dict__ = None
        
        if volatility:
            self.volatility = float(volatility) / 100

            self._a_ = self.volatility

* self.daysToExpiration**0.5
            self._d1_ = (log(self.underlyingPrice / self.strikePrice) + \
                (self.domesticRate - self.foreignRate + \
                (self.volatility**2)/2) * self.daysToExpiration) / self._a_
            self._d2_ = self._d1_ - self._a_
            # Reduces performance overhead when computing implied volatility
            if performance:        
                [self.callPrice, self.putPrice] = self._price()
            else:
                [self.callPrice, self.putPrice] = self._price()
                [self.callDelta, self.putDelta] = self._delta()
                [self.callDelta2, self.putDelta2] = self._delta2()
                [self.callTheta, self.putTheta] = self._theta()
                [self.callRhoD, self.putRhoD] = self._rhod()
                [self.callRhoF, self.putRhoF] = self._rhof()
                self.vega = self._vega()
                self.gamma = self._gamma()
                self.exerciceProbability = norm.cdf(self._d2_)
        if callPrice:
            self.callPrice = round(float(callPrice), 6)
            self.impliedVolatility = impliedVolatility(\
                    self.__class__.__name__, args, callPrice=self.callPrice)
        if putPrice and not callPrice:
            self.putPrice = round(float(putPrice), 6)
            self.impliedVolatility = impliedVolatility(\
                    self.__class__.__name__, args, putPrice=self.putPrice)
        if callPrice and putPrice:
            self.callPrice = float(callPrice)
            self.putPrice = float(putPrice)
            self.putCallParity = self._parity()

    def _price(self):
        '''Returns the option price: [Call price, Put price]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = max(0.0, self.underlyingPrice - self.strikePrice)
            put = max(0.0, self.strikePrice - self.underlyingPrice)
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            call = e**(-self.foreignRate * self.daysToExpiration) * \
                    self.underlyingPrice * norm.cdf(self._d1_) - \
                    e**(-self.domesticRate * self.daysToExpiration) * \
                    self.strikePrice * norm.cdf

(self._d2_)
            put = e**(-self.domesticRate * self.daysToExpiration) * \
                    self.strikePrice * norm.cdf(-self._d2_) - \
                    e**(-self.foreignRate * self.daysToExpiration) * \
                    self.underlyingPrice * norm.cdf(-self._d1_)
        return [call, put]

    def _delta(self):
        '''Returns the option delta: [Call delta, Put delta]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = 1.0 if self.underlyingPrice > self.strikePrice else 0.0
            put = -1.0 if self.underlyingPrice < self.strikePrice else 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            _b_ = e**-(self.foreignRate * self.daysToExpiration)
            call = norm.cdf(self._d1_) * _b_
            put = -norm.cdf(-self._d1_) * _b_
        return [call, put]

    def _delta2(self):
        '''Returns the dual delta: [Call dual delta, Put dual delta]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = -1.0 if self.underlyingPrice > self.strikePrice else 0.0
            put = 1.0 if self.underlyingPrice < self.strikePrice else 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            _b_ = e**-(self.domesticRate * self.daysToExpiration)
            call = -norm.cdf(self._d2_) * _b_
            put = norm.cdf(-self._d2_) * _b_
        return [call, put]

    def _vega(self):
        '''Returns the option vega'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            return 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            return self.underlyingPrice * e**-(self.foreignRate * \
                    self.daysToExpiration) * norm.pdf(self._d1_) * \
                    self.daysToExpiration**0.5

    def _theta(self):
        '''Returns the option theta: [Call theta, Put theta]'''
        _b_ = e**-(self.foreignRate * self.daysToExpiration)
        call = -self.underlyingPrice * _b_ * norm.pdf(self._d1_) * \
                self.volatility / (2 * self.daysToExpiration**0.5) + \
                self.foreignRate * self.underlyingPrice * _b_ * \
                norm.cdf(self._d1_) - self.domesticRate * self.strikePrice * \
                _b_ * norm.cdf(self._d2_)
        put = -self.underlyingPrice * _b_ * norm.pdf(self._d1_) * \
                self.volatility / (2 * self.daysToExpiration**0.5) - \
                self.foreignRate * self.underlyingPrice * _b_ * \
                norm.cdf(-self._d1_) + self.domesticRate * self.strikePrice * \
                _b_ * norm.cdf(-self._d2_)
        return [call / 365, put / 365]

    def _rhod(self):
        '''Returns the option domestic rho: [Call rho, Put rho]'''
        call = self.strikePrice * self.daysToExpiration * \
                e**(-self.domesticRate * self.daysToExpiration) * \
                norm.cdf(self._d2_) / 100
        put = -self.strikePrice * self.daysToExpiration * \
                e**(-self.domesticRate * self.daysToExpiration) * \
                norm.cdf(-self._d2_) / 100
        return [call, put]

    def _rhof(self):
        '''Returns the option foreign rho: [Call rho, Put rho]'''
        call = -self.underlyingPrice * self.daysToExpiration * \
                e**(-self.foreignRate * self.daysToExpiration) * \
                norm.cdf(self._d1_) / 100
        put = self.underlyingPrice * self.daysToExpiration * \
                e**(-self.foreignRate * self.daysToExpiration) * \
                norm.cdf(-self._d1_) / 100
        return [call, put]

    def _gamma(self):
        '''Returns the option gamma'''
        return (norm.pdf(self._d1_) * e**-(self.foreignRate * \
                self.daysToExpiration)) / (self.underlyingPrice * self._a_)

    def _parity(self):
        '''Returns the put-call parity'''
        return self.callPrice - self.putPrice - (self.underlyingPrice / \
                ((1 + self.foreignRate)**self.daysToExpiration)) + \
                (self.strikePrice / \
                ((1 + self.domesticRate)**self.daysToExpiration))

class BS:
    '''Black-Scholes
    Used for pricing European options on stocks without dividends

    BS([underlyingPrice, strikePrice, interestRate, daysToExpiration], \
            volatility=x, callPrice=y, putPrice=z)

    eg:
        c = mibian.BS([1.4565, 1.45, 1, 30], volatility=20)
        c.callPrice                # Returns the call price
        c.putPrice                # Returns the put price
        c.callDelta                # Returns the call delta
        c.putDelta                # Returns the put delta
        c.callDelta2            # Returns the call dual delta
        c.putDelta2                # Returns the put dual delta
        c.callTheta                # Returns the call theta
        c.putTheta                # Returns the put theta
        c.callRho                # Returns the call rho
        c.putRho                # Returns the put rho
        c.vega                    # Returns the option vega
        c.gamma                    # Returns the option gamma

        c = mibian.BS([1.4565, 1.45, 1, 30], callPrice=0.0359)
        c.impliedVolatility        # Returns the implied volatility from the call price
        
        c = mibian.BS([1.4565, 1.45, 1, 30], putPrice=0.0306)
        c.impliedVolatility        # Returns the implied volatility from the put price
        
        c = mibian.BS([1.4565, 1.45, 1, 30], callPrice=0.0359, putPrice=0.0306)
        c.putCallParity            # Returns the put-call parity
        '''

    def __init__(self, args, volatility=None, callPrice=None, putPrice=None, \
            performance=None):
        self.underlyingPrice = float(args[0])
        self.strikePrice = float(args[1])
        self.interestRate = float(args[2]) / 100
        self.daysToExpiration = float(args[3]) / 365

        for i in ['callPrice', 'putPrice', 'callDelta', 'putDelta', \
                'callDelta2', 'putDelta2', 'callTheta', 'putTheta', \
                'callRho', 'putRho', 'vega', 'gamma', 'impliedVolatility', \
                'putCallParity']:
            self.__dict__ = None
        
        if volatility:
            self.volatility = float(volatility) / 100

            self._a_ = self.volatility * self.daysToExpiration**0.5
            self._d1_ = (log(self.underlyingPrice / self.strikePrice) + \
                    (self.interestRate + (self.volatility**2) / 2) * \
                    self.daysToExpiration) / self._a_
            self._d2_ = self._d1_ - self._a_
            if performance:
                [self.callPrice, self.putPrice] = self._price()
            else:
                [self.callPrice, self.putPrice] = self._price()
                [self.callDelta, self.putDelta] = self._delta()
                [self.callDelta2, self.putDelta2] = self._delta2()
                [self.callTheta, self.putTheta] = self._theta()
                [self.callRho, self.putRho] = self._rho()
                self.vega = self._vega()
                self.gamma = self._gamma()
                self.exerciceProbability = norm.cdf(self._d2_)
        if callPrice:
            self.callPrice = round(float(callPrice), 6)
            self.impliedVolatility = impliedVolatility(\
                    self.__class__.__name__, args, callPrice=self.callPrice)
        if putPrice and not callPrice:
            self.putPrice = round(float(putPrice), 6)
            self.impliedVolatility = impliedVolatility(\
                    self.__class__.__name__, args, putPrice=self.putPrice)
        if callPrice and putPrice:
            self.callPrice = float(callPrice)
            self.putPrice = float(putPrice)
            self.putCallParity = self._parity()

    def _price(self):
        '''Returns the option price: [Call price, Put price]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = max(0.0, self.underlyingPrice - self.strikePrice)
            put = max(0.0, self.strikePrice - self.underlyingPrice)
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            call = self.underlyingPrice * norm.cdf(self._d1_) - \
                    self.strikePrice * e**(-self.interestRate * \
                    self.daysToExpiration) * norm.cdf(self._d2_)
            put = self.strikePrice * e**(-self.interestRate * \
                    self.daysToExpiration) * norm.cdf(-self._d2_) - \
                    self.underlyingPrice * norm.cdf(-self._d1_)
        return [call, put]

    def _delta(self):
        '''Returns the option delta: [Call delta, Put delta]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = 1.0 if self.underlyingPrice > self.strikePrice else 0.0
            put = -1.0 if self.underlyingPrice < self.strikePrice else 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            call = norm.cdf(self._d1_)
            put = -norm.cdf(-self._d1_)
        return [call, put]

    def _delta2(self):
        '''Returns the dual delta: [Call dual delta, Put dual delta]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = -1.0 if self.underlyingPrice > self.strikePrice else 0.0
            put = 1.0 if self.underlyingPrice < self.strikePrice else 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            _b_ = e**-(self.interestRate * self.daysToExpiration)
            call = -norm.cdf(self._d2_) * _b_
            put = norm.cdf(-self._d2_) * _b_
        return [call, put]

    def _vega(self):
        '''Returns the option vega'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            return 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            return self.underlyingPrice * norm.pdf(self._d1_) * \
                    self.daysToExpiration**0.5 / 100

    def _theta(self):
        '''Returns the option theta: [Call theta, Put theta]'''
        _b_ = e**-(self.interestRate * self.daysToExpiration)
        call = -self.underlyingPrice * norm.pdf(self._d1_) * self.volatility / \
                (2 * self.daysToExpiration**0.5) - self.interestRate * \
                self.strikePrice * _b_ * norm.cdf(self._d2_)
        put = -self.underlyingPrice * norm.pdf(self._d1_) * self.volatility / \
                (2 * self.daysToExpiration**0.5) + self.interestRate * \
                self.strikePrice * _b_ * norm.cdf(-self._d2_)
        return [call / 365, put / 365]

    def _rho(self):
        '''Returns the option rho: [Call rho, Put rho]'''
        _b_ = e**-(self.interestRate * self.daysToExpiration)
        call = self.strikePrice * self.daysToExpiration * _b_ * \
                norm.cdf(self._d2_) / 100
        put = -self.strikePrice * self.daysToExpiration * _b_ * \
                norm.cdf(-self._d2_) / 100
        return [call, put]

    def _gamma(self):
        '''Returns the option gamma'''
        return norm.pdf(self._d1_) / (self.underlyingPrice * self._a_)

    def _parity(self):
        '''Put-Call Parity'''
        return self.callPrice - self.putPrice - self.underlyingPrice + \
                (self.strikePrice / \
                ((1 + self.interestRate)**self.daysToExpiration))

class Me:
    '''Merton
    Used for pricing European options on stocks with dividends

    Me([underlyingPrice, strikePrice, interestRate, annualDividends, \
            daysToExpiration], volatility=x, callPrice=y, putPrice=z)

    eg:
        c = mibian.Me([52, 50, 1, 1, 30], volatility=20)
        c.callPrice                # Returns the call price
        c.putPrice                # Returns the put price
        c.callDelta                # Returns the call delta
        c.putDelta                # Returns the put delta
        c.callDelta2            # Returns the call dual delta
        c.putDelta2                # Returns the put dual delta
        c.callTheta                # Returns the call theta
        c.putTheta                # Returns the put theta
        c.callRho                # Returns the call rho
        c.putRho                # Returns the put rho
        c.vega                    # Returns the option vega
        c.gamma                    # Returns the option gamma

        c = mibian.Me([52, 50, 1, 1, 30], callPrice=0.0359)
        c.impliedVolatility        # Returns the implied volatility from the call price
        
        c = mibian.Me([52, 50, 1, 1, 30], putPrice=0.0306)
        c.impliedVolatility        # Returns the implied volatility from the put price
        
        c = mibian.Me([52, 50, 1, 1, 30], callPrice=0.0359, putPrice=0.0306)
        c.putCallParity            # Returns the put-call parity
    '''

    def __init__(self, args, volatility=None, callPrice=None, putPrice=None, \
            performance=None):
        self.underlyingPrice = float(args[0])
        self.strikePrice = float(args[1])
        self.interestRate = float(args[2]) / 100
        self.dividend = float(args[3])
        self.dividendYield = self.dividend / self.underlyingPrice
        self.daysToExpiration = float(args[4]) / 365

        for i in ['callPrice', 'putPrice', 'callDelta', 'putDelta', \
                'callDelta2', 'putDelta2', 'callTheta', 'putTheta', \
                'callRho', 'putRho', 'vega', 'gamma', 'impliedVolatility', \
                'putCallParity']:
            self.__dict__ = None
        
        if volatility:
            self.volatility = float(volatility) / 100

            self._a_ = self.volatility * self.daysToExpiration**0.5
            self._d1_ = (log(self.underlyingPrice / self.strikePrice) + \
                    (self.interestRate - self.dividendYield + \
                    (self.volatility**2) / 2) * self.daysToExpiration) / \
                    self._a_
            self._d2_ = self._d1_ - self._a_
            if performance:
                [self.callPrice, self.putPrice] = self._price()
            else:
                [self.callPrice, self.putPrice] = self._price()
                [self.callDelta, self.putDelta] = self._delta()
                [self.callDelta2, self.putDelta2] = self._delta2()
                [self.callTheta, self.putTheta] = self._theta()
                [self.callRho, self.putRho] = self._rho()
                self.vega = self._vega()
                self.gamma = self._gamma()
                self.exerciceProbability = norm.cdf(self._d2_)
        if callPrice:
            self.callPrice = round(float(callPrice), 6)
            self.impliedVolatility = impliedVolatility(\
                    self.__class__.__name__, args, self.callPrice)
        if putPrice and not callPrice:
            self.putPrice = round(float(putPrice), 6)
            self.impliedVolatility = impliedVolatility(\
                    self.__class__.__name__, args, putPrice=self.putPrice)
        if callPrice and putPrice:
            self.callPrice = float(callPrice)
            self.putPrice = float(putPrice)
            self.putCallParity = self._parity()

    def _price(self):
        '''Returns the option price: [Call price, Put price]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = max(0.0, self.underlyingPrice - self.strikePrice)
            put = max(0.0, self.strikePrice - self.underlyingPrice)
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            call = self.underlyingPrice * e**(-self.dividendYield * \
                    self.daysToExpiration) * norm.cdf(self._d1_) - \
                    self.strikePrice * e**(-self.interestRate * \
                    self.daysToExpiration) * norm.cdf(self._d2_)
            put = self.strikePrice * e**(-self.interestRate * \
                    self.daysToExpiration) * norm.cdf(-self._d2_) - \
                    self.underlyingPrice * e**(-self.dividendYield * \
                    self.daysToExpiration) * norm.cdf(-self._d1_)
        return [call, put]

    def _delta(self):
        '''Returns the option delta: [Call delta, Put delta]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = 1.0 if self.underlyingPrice > self.strikePrice else 0.0
            put = -1.0 if self.underlyingPrice < self.strikePrice else 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            _b_ = e**(-self.dividendYield * self.daysToExpiration)
            call = _b_ * norm.cdf(self._d1_)
            put = _b_ *    (norm.cdf(self._d1_) - 1)
        return [call, put]

    # Verify
    def _delta2(self):
        '''Returns the dual delta: [Call dual delta, Put dual delta]'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            call = -1.0 if self.underlyingPrice > self.strikePrice else 0.0
            put = 1.0 if self.underlyingPrice < self.strikePrice else 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            _b_ = e**-(self.interestRate * self.daysToExpiration)
            call = -norm.cdf(self._d2_) * _b_
            put = norm.cdf(-self._d2_) * _b_
        return [call, put]

    def _vega(self):
        '''Returns the option vega'''
        if self.volatility == 0 or self.daysToExpiration == 0:
            return 0.0
        if self.strikePrice == 0:
            raise ZeroDivisionError('The strike price cannot be zero')
        else:
            return self.underlyingPrice * e**(-self.dividendYield * \
                    self.daysToExpiration) * norm.pdf(self._d1_) * \
                    self.daysToExpiration**0.5 / 100

    def _theta(self):
        '''Returns the option theta: [Call theta, Put theta]'''
        _b_ = e**-(self.interestRate * self.daysToExpiration)
        _d_ = e**(-self.dividendYield * self.daysToExpiration)
        call = -self.underlyingPrice * _d_ * norm.pdf(self._d1_) * \
                self.volatility / (2 * self.daysToExpiration**0.5) + \
                self.dividendYield * self.underlyingPrice * _d_ * \
                norm.cdf(self._d1_) - self.interestRate * \
                self.strikePrice * _b_ * norm.cdf(self._d2_)
        put = -self.underlyingPrice * _d_ * norm.pdf(self._d1_) * \
                self.volatility / (2 * self.daysToExpiration**0.5) - \
                self.dividendYield * self.underlyingPrice * _d_ * \
                norm.cdf(-self._d1_) + self.interestRate * \
                self.strikePrice * _b_ * norm.cdf(-self._d2_)
        return [call / 365, put / 365]

    def _rho(self):
        '''Returns the option rho: [Call rho, Put rho]'''
        _b_ = e**-(self.interestRate * self.daysToExpiration)
        call = self.strikePrice * self.daysToExpiration * _b_ * \
                norm.cdf(self._d2_) / 100
        put = -self.strikePrice * self.daysToExpiration * _b_ * \
                norm.cdf(-self._d2_) / 100
        return [call, put]

    def _gamma(self):
        '''Returns the option gamma'''
        return e**(-self.dividendYield * self.daysToExpiration) * \
                norm.pdf(self._d1_) / (self.underlyingPrice * self._a_)

    # Verify
    def _parity(self):
        '''Put-Call Parity'''
        return self.callPrice - self.putPrice - self.underlyingPrice + \
                (self.strikePrice / \
                ((1 + self.interestRate)**self.daysToExpiration))
3#
期权匿名回答  16级独孤 | 2022-11-18 14:00:10 发帖IP地址来自 广东佛山
不想写大段代码,也不想囤积大量数据的,可以试试澎博云量化平台
隐含波动率一句python代码就计算出来了
ImpliedVola=CalOBJ.GetImpliedVolatility(OptDirection,AssetType,AssetPrice,StrikePrice,HisVola,InterestRate,ExpireinYear,OptionPrice)

代码范例可以参考qmhedging/poboquant

围绕隐含波动率和历史波动率可以有许多策略,这里也能找到
qmhedging/poboquant
这个工具是懒人和期权研究员的福音

真格量化 - 期货期权实盘量化平台
4#
期权匿名回答  16级独孤 | 2022-11-18 14:00:17 发帖IP地址来自 北京
提供一些在优矿计算隐含波动率的实例,点击到社区可以一键克隆源码。

波动率与期权     作者:bin2436
波动率曲面        作者:cheng.li
Greeks 和隐含波动率微笑     作者:李自龙

下面截取波动率与期权一贴当中的计算过程供参考:

与历史波动率反映的是标的资产过去一段时间的波动程度不同,隐含波动率是资产价格未来一段时间的波动率,是由相应的期权价格估计出来。
估算隐含波动率有很多模型,详见CAL文档: https://uqer.io/help/faqCAL/#Black-Scholes-Merton%E6%A8%A1%E5%9E%8B
这里我们使用CAL包中的BSMImpliedVolatity函数计算隐含波动率。
BSMImpliedVolatity(optionType, strike, spot, riskFree, dividend, maturity, quotePrice, rawOutput=False)

  • optionType – 期权类型,1 for CALL或者 -1 for PUT
  • strike – 行权价格
  • spot – 标的股票价格
  • riskFree – 无风险利率
  • dividend – 股票红利率
  • maturity – 到期时间(年)
  • uotePrice – 期权报价
关于无风险利率,在李自龙老师的帖子当中,使用的是计算“上海银行间同业拆借利率 SHIBOR”,本篇中,我们简化操作,将无风险利率设置为常数4%。



我们总共获得42378 x 9 的有效数据,代表自2015年2月9日以来,所有50ETF期权合约在每一个交易日的相关数据。
“隐含波动率的计算并没有统一的标准。
以同一只资产为标的的期权合约有很多,执行价格和到期时间不同的期权具有不同的隐含波动率。即使到期时间相同,不同执行价格的期权也具有不同的隐含波动率。
我们通过计算以同一资产为标的的不同期权的隐含波动率的平均值来作为这一资产的隐含波动率。”——《期权波动率模型及交易策略分析》
根据报告的内容,我们将在同一交易日期的,所有Call合约的隐含波动率hv取均值,得到该日该Call的隐含波动率,Put也同理。



目前有四个因子:“股票收盘价”“30天波动率”“call合约隐含波动率”“put合约隐含波动率”。
先来看看他们之间的相互关系:



根据报告中所描述的“利用历史波动率和隐含波动率的差异来做交易”思路。
“专业的期权交易者、做市商和机构投资者通过delta对冲来做波动率交易。意思是说,他们通过买卖期权来对冲标的资产敞口部分,这样会消除股市小幅波动带来的风险。这种对冲是一个持续的随市场变化而调整的过程。为了对冲标的资产的风险,交易者捕捉资产上的历史波动率和期权价格的隐含波动率。也就是说,如果卖出期权的隐含波动率高于对冲标的资产的历史波动率,他们就会赚钱。同样的,买入期权的隐含波动率低于对冲标的资产历史波动率,也会赚钱。”



我们将数据可视化再来看下:

5#
期权匿名回答  16级独孤 | 2022-11-18 14:00:53 发帖IP地址来自 中国
VBA for Excel Spreadsheet

The VBA includes two functions

  • BlackScholesCall() calculates the price of a call option
  • ImpliedVolatility() calculates implied volatility. It uses the bisection method to calculate volatility using BlackScholesCall()
Function BlackScholesCall( _
ByVal S As Double, _
ByVal X As Double, _
ByVal T As Double, _
ByVal r As Double, _
ByVal d As Double, _
ByVal v As Double) As Double Dim d1 As Double Dim d2 As Double
    d1 = (Log(S / X) + (r - d + v ^ 2 / 2) * T) / v / Sqr(T)
    d2 = d1 - v * Sqr(T)
    BlackScholesCall = Exp(-d * T) * S * Application.NormSDist(d1) - X * Exp(-r * T) * Application.NormSDist(d2)
End Function Function ImpliedVolatility( _
ByVal S As Double, _
ByVal X As Double, _
ByVal T As Double, _
ByVal r As Double, _
ByVal d As Double, _
ByVal Price As Double) As Double Dim epsilonABS As Double Dim epsilonSTEP As Double Dim volMid As Double Dim niter As Integer Dim volLower As Double Dim volUpper As Double

    epsilonABS = 0.0000001
    epsilonSTEP = 0.0000001
    niter = 0
    volLower = 0.001
    volUpper = 1

Do While volUpper - volLower >= epsilonSTEP Or Abs(BlackScholesCall(S, X, T, r, d, volLower) - Price) >= epsilonABS And epsilonABS <= Abs(BlackScholesCall(S, X, T, r, d, volUpper) - Price) >= epsilonABS
      volMid = (volLower + volUpper) / 2
If Abs(BlackScholesCall(S, X, T, r, d, volMid) - Price) <= epsilonABS Then Exit Do ElseIf ((BlackScholesCall(S, X, T, r, d, volLower) - Price) * (BlackScholesCall(S, X, T, r, d, volMid) - Price) < 0) Then
        volUpper = volMid
Else
        volLower = volMid
End If
      niter = niter + 1
Loop

    ImpliedVolatility = volLower

End Function
6#
期权匿名回答  16级独孤 | 2022-11-18 14:01:40 发帖IP地址来自 北京
import numpy as np
from scipy.stats import norm

# 设定参数
r=0.032 # risk-free interest rate
t=float(30)/365 # time to expire (30 days)
q=0 # dividend yield
S0=2.3 # underlying price
X=2.2 # strike price
mktprice=0.18 # market price

# 用二分法求implied volatility,暂只针对call option
sigma=0.3 # initial volatility
C=P=0
upper=1
lower=0
while abs(C-mktprice)>1e-6:
    d1=(np.log(S0/X)+(r-q+sigma**2/2)*t)/(sigma*np.sqrt(t))
    d2=d1-sigma*np.sqrt(t)
    C=S0*np.exp(-q*t)*norm.cdf(d1)-X*np.exp(-r*t)*norm.cdf(d2)
    P=X*np.exp(-r*t)*norm.cdf(-d2)-S0*np.exp(-q*t)*norm.cdf(-d1)
    if C-mktprice>0:
        upper=sigma
        sigma=(sigma+lower)/2
    else:
        lower=sigma
        sigma=(sigma+upper)/2
print sigma # implied volatility以上是用BS model计算欧式call option的implied volatility。稍微修改一下也可以算put option。计算结果可以和期权计算器上的结果对比。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:400157
帖子:80032
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP