main

Options Greeks calculation with Python

Aplying the BlackScholes formula we can relatively easily calculate the different greeks of the options.

Options greeks are the parameters that are going to tell us how the option prices is going to performance in relation to the changes in the underlying price and others like time to the expiry date or volatility.

One of the most important parameters to get is the implied volatility. Given the market price of the option and the rest of parameters (time to expiry date, strike, interest) we can calculate the volatility with which this market option price was calculated. And see if it makes sense.

Although IB gives all of this information, I have decided to calculate them in my own just for checking and for doing further analysis.

The tricky one is the implied volatility, as we have to iterate in order to estimate its value.

 

As I intend to do massive calculations, I also have created a class for processing multiple options.

 

Here it is the code:

 
"""
This module contains backscholes calculations of prices and greeks for options
"""
 
 
 
#===============================================================================
# LIBRARIES
#===============================================================================
import math
from scipy.stats import norm
import pandas as pd
from datetime import date
import numpy as np
 
 
#===============================================================================
# CLASS OPTION  
#===============================================================================
class Option:
    """
    This class will group the different black-shcoles calculations for an opion
    """
    def __init__(self, right, s, k, eval_date, exp_date, price = None, rf = 0.01, vol = 0.3,
                 div = 0):
        self.k = float(k)
        self.s = float(s)
        self.rf = float(rf)
        self.vol = float(vol)
        self.eval_date = eval_date
        self.exp_date = exp_date
        self.t = self.calculate_t()
        if self.t == 0: self.t = 0.000001 ## Case valuation in expiration date
        self.price = price
        self.right = right   ## 'C' or 'P'
        self.div = div
 
    def calculate_t(self):
        if isinstance(self.eval_date, basestring):
            if '/' in self.eval_date:
                (day, month, year) = self.eval_date.split('/')
            else:
                (day, month, year) = self.eval_date[6:8], self.eval_date[4:6], self.eval_date[0:4]
            d0 = date(int(year), int(month), int(day))
        elif type(self.eval_date)==float or type(self.eval_date)==long or type(self.eval_date)==np.float64:
            (day, month, year) = (str(self.eval_date)[6:8], str(self.eval_date)[4:6], str(self.eval_date)[0:4])
            d0 = date(int(year), int(month), int(day))
        else:
            d0 = self.eval_date 
 
        if isinstance(self.exp_date, basestring):
            if '/' in self.exp_date:
                (day, month, year) = self.exp_date.split('/')
            else:
                (day, month, year) = self.exp_date[6:8], self.exp_date[4:6], self.exp_date[0:4]
            d1 = date(int(year), int(month), int(day))
        elif type(self.exp_date)==float or type(self.exp_date)==long or type(self.exp_date)==np.float64:
            (day, month, year) = (str(self.exp_date)[6:8], str(self.exp_date)[4:6], str(self.exp_date)[0:4])
            d1 = date(int(year), int(month), int(day))
        else:
            d1 = self.exp_date
 
        return (d1 - d0).days / 365.0
 
    def get_price_delta(self):
        d1 = ( math.log(self.s/self.k) + ( self.rf + self.div + math.pow( self.vol, 2)/2 ) * self.t ) / ( self.vol * math.sqrt(self.t) )
        d2 = d1 - self.vol * math.sqrt(self.t)
        if self.right == 'C':
            self.calc_price = ( norm.cdf(d1) * self.s * math.exp(-self.div*self.t) - norm.cdf(d2) * self.k * math.exp( -self.rf * self.t ) )
            self.delta = norm.cdf(d1)
        elif self.right == 'P':
            self.calc_price =  ( -norm.cdf(-d1) * self.s * math.exp(-self.div*self.t) + norm.cdf(-d2) * self.k * math.exp( -self.rf * self.t ) )
            self.delta = -norm.cdf(-d1) 
 
    def get_call(self):
        d1 = ( math.log(self.s/self.k) + ( self.rf + math.pow( self.vol, 2)/2 ) * self.t ) / ( self.vol * math.sqrt(self.t) )
        d2 = d1 - self.vol * math.sqrt(self.t)
        self.call = ( norm.cdf(d1) * self.s - norm.cdf(d2) * self.k * math.exp( -self.rf * self.t ) )
        #put =  ( -norm.cdf(-d1) * self.s + norm.cdf(-d2) * self.k * math.exp( -self.rf * self.t ) ) 
        self.call_delta = norm.cdf(d1)
 
 
    def get_put(self):
        d1 = ( math.log(self.s/self.k) + ( self.rf + math.pow( self.vol, 2)/2 ) * self.t ) / ( self.vol * math.sqrt(self.t) )
        d2 = d1 - self.vol * math.sqrt(self.t)
        #call = ( norm.cdf(d1) * self.s - norm.cdf(d2) * self.k * math.exp( -self.rf * self.t ) )
        self.put =  ( -norm.cdf(-d1) * self.s + norm.cdf(-d2) * self.k * math.exp( -self.rf * self.t ) )
        self.put_delta = -norm.cdf(-d1) 
 
 
    def get_theta(self, dt = 0.0027777):
        self.t += dt
        self.get_price_delta()
        after_price = self.calc_price
        self.t -= dt
        self.get_price_delta()
        orig_price = self.calc_price
        self.theta = (after_price - orig_price) * (-1)
 
    def get_gamma(self, ds = 0.01):
        self.s += ds
        self.get_price_delta()
        after_delta = self.delta
        self.s -= ds
        self.get_price_delta()
        orig_delta = self.delta
        self.gamma = (after_delta - orig_delta) / ds
 
    def get_all(self):
        self.get_price_delta()
        self.get_theta()
        self.get_gamma()
        return self.calc_price, self.delta, self.theta, self.gamma
 
 
    def get_impl_vol(self):
        """
        This function will iterate until finding the implied volatility
        """
        ITERATIONS = 100
        ACCURACY = 0.05
        low_vol = 0
        high_vol = 1
        self.vol = 0.5  ## It will try mid point and then choose new interval
        self.get_price_delta()
        for i in range(ITERATIONS):
            if self.calc_price > self.price + ACCURACY:
                high_vol = self.vol
            elif self.calc_price < self.price - ACCURACY:
                low_vol = self.vol
            else:
                break
            self.vol = low_vol + (high_vol - low_vol)/2.0
            self.get_price_delta()
 
        return self.vol
 
    def get_price_by_binomial_tree(self):
        """
        This function will make the same calculation but by Binomial Tree
        """
        n=30
        deltaT=self.t/n
        u = math.exp(self.vol*math.sqrt(deltaT))
        d=1.0/u
        # Initialize our f_{i,j} tree with zeros
        fs = [[0.0 for j in xrange(i+1)] for i in xrange(n+1)]
        a = math.exp(self.rf*deltaT)
        p = (a-d)/(u-d)
        oneMinusP = 1.0-p 
        # Compute the leaves, f_{N,j}
        for j in xrange(i+1):
            fs[n][j]=max(self.s * u**j * d**(n-j) - self.k, 0.0)
        print fs
 
        for i in xrange(n-1, -1, -1):
            for j in xrange(i+1):
                fs[i][j]=math.exp(-self.rf * deltaT) * (p * fs[i+1][j+1] +
                                                        oneMinusP * fs[i+1][j])
        print fs
 
        return fs[0][0]
 
#===============================================================================
# CLASS OPTIONS STRATEGY
#=============================================================================== 
class Options_strategy:
    """
    This class will calculate greeks for a group of options (called Options Strategy)
    """
    def __init__(self, df_options): 
        self.df_options = df_options       #It will store the different options in a pandas dataframe
 
    def get_greeks(self):
        """
        For analysis underlying (option chain format)
        """
        self.delta = 0
        self.gamma = 0
        self.theta = 0
        for k,v in self.df_options.iterrows():
 
            ## Case stock or future
            if v['m_secType']=='STK':
                self.delta += float(v['position']) * 1
 
            ## Case option
            elif v['m_secType']=='OPT':    
                opt = Option(s=v['underlying_price'], k=v['m_strike'], eval_date=date.today(), # We want greeks for today
                             exp_date=v['m_expiry'], rf = v['interest'], vol = v['volatility'],
                             right = v['m_right'])
 
                price, delta, theta, gamma = opt.get_all()
 
                self.delta += float(v['position']) * delta
                self.gamma += float(v['position']) * gamma
                self.theta += float(v['position']) * theta
 
            else:
                print "ERROR: Not known type"
 
        return self.delta, self.gamma, self.theta 
 
    def get_greeks2(self):
        """
        For analysis_options_strategy
        """
        self.delta = 0
        self.gamma = 0
        self.theta = 0
        for k,v in self.df_options.iterrows():
 
            ## Case stock or future
            if v['m_secType']=='STK':
                self.delta += float(v['position']) * 1
 
            ## Case option
            elif v['m_secType']=='OPT':    
                opt = Option(s=v['underlying_price'], k=v['m_strike'], eval_date=date.today(), # We want greeks for today
                             exp_date=v['m_expiry'], rf = v['interest'], vol = v['volatility'],
                             right = v['m_right'])
 
                price, delta, theta, gamma = opt.get_all()
 
                if v['m_side']=='BOT':
                    position = float(v['position'])
                else:
                    position = - float(v['position']) 
                self.delta += position * delta
                self.gamma += position * gamma
                self.theta += position * theta
 
            else:
                print "ERROR: Not known type"
 
        return self.delta, self.gamma, self.theta 
 
 
if __name__ == '__main__':
 
 
    #===========================================================================
    # TO CHECK OPTION CALCULATIONS
    #===========================================================================
    s = 56.37
    k = 60
    exp_date = '20171215'
    eval_date = '20140528'
    rf = 0.01
    vol = 0.2074
    div = 0.014
    right = 'C'
    opt = Option(s=s, k=k, eval_date=eval_date, exp_date=exp_date, rf=rf, vol=vol, right=right,
                 div = div)
    price, delta, theta, gamma = opt.get_all()
    print "-------------- FIRST OPTION -------------------"
    print "Price CALL: " + str(price)  # 2.97869320042
    print "Delta CALL: " + str(delta)  # 0.664877358932
    print "Theta CALL: " + str(theta)  # 0.000645545628288
    print "Gamma CALL:" + str(gamma)   # 0.021127937082
 
    price = opt.get_price_by_binomial_tree()
    print "Price by BT:" + str(price)
 
    s = 110.41
    k = 112
    exp_date = '20160115'
    eval_date = '20140429'
    rf = 0.01
    vol = 0.11925
    right = 'C'
    opt = Option(s=s, k=k, eval_date=eval_date, exp_date=exp_date, rf=rf, vol=vol, right=right)
    price, delta, theta, gamma = opt.get_all()
    print "-------------- SECOND OPTION -------------------"
    print "Price CALL: " + str(price)   # 7.02049813137
    print "Delta CALL: " + str(delta)   # 0.53837898036
    print "Theta CALL: " + str(theta)   # -0.00699852931575
    print "Gamma CALL:" + str(gamma)    # 0.0230279263655
 
    #===========================================================================
    # TO CHECK OPTIONS STRATEGIES CALCULATIONS
    #===========================================================================
    d_option1 = {'m_secType': 'OPT', 'm_expiry': '20150116', 'm_right': 'C', 'm_symbol': 'TLT', 'm_strike': '115', 
                 'm_multiplier': '100', 'position': '-2', 'trade_price': '3.69', 'comission': '0',
                 'eval_date': '20140422', 'interest': '0.01', 'volatility': '0.12353', 'underlying_price': '109.96'}
    d_option2 = {'m_secType': 'OPT', 'm_expiry': '20150116', 'm_right': 'C', 'm_symbol': 'TLT', 'm_strike': '135', 
                 'm_multiplier': '100', 'position': '2', 'trade_price': '0.86', 'comission': '0',
                 'eval_date': '20140422', 'interest': '0.01', 'volatility': '0.12353', 'underlying_price': '109.96'}
 
    df_options = pd.DataFrame([d_option1, d_option2])
    opt_strat = Options_strategy(df_options)
    delta, gamma, theta = opt_strat.get_greeks()
    print "-------- OPTIONS STRATEGY --------------"
    print "Delta: " + str(delta)
    print "Gamma: " + str(gamma)
    print "Theta: " + str(theta)
 
    #===========================================================================
    # TO CHECK OPTION IMPLIED VOLATILITY CALCULATION 
    #===========================================================================
    s = 110.63
    k = 115
    exp_date = '20150116'
    eval_date = '20140424'
    rf = 0.01
    price = 3.18  ## Calculated for a vol = 0.12353
    right = 'C'
    opt = Option(s=s, k=k, eval_date=eval_date, exp_date=exp_date, rf=rf, price=price, right=right)
    ivol = opt.get_impl_vol()
    print "-------------- FIRST OPTION -------------------"
    print "Implied Volatility: " + str(ivol)

2 Responses to Options Greeks calculation with Python

  1. Nicholas Stein July 8, 2016 at 4:02 am #

    I ran into a couple of problems with your python code to calculate the greeks.

    I am a beginning python programmer, so I probably made some ignorant mistakes.

    1. I tried to run the code in Python 3.4 and I got some errors. I was able to resolve most of them by changing the code to be 3.4 compliant, but in the binomial computation the following line gave me the error that “i” was not defined.

    fs = [[0.0 for j in xrange(i+1)] for i in xrange(n+1)]

    I had to change xrange to range, so that may have been a problem. I could not figure out from the code what the initial value of i should be, probably 0, so I could not declare it earlier and set it to 0.

    So I spun up a 2.7 environment and tried again.

    2. The first option test did not come out with the expected values. I get.
    ————– FIRST OPTION ——————-
    Price CALL: 6.39548850488
    Delta CALL: 0.600248022046
    Theta CALL: -0.00247525485112
    Gamma CALL:0.0175249946303
    Price by BT:8.17598850134 although the test did not have a comment for this value.

    The second option was right on the money.
    ————– SECOND OPTION ——————-
    Price CALL: 7.02049813137
    Delta CALL: 0.53837898036
    Theta CALL: -0.00699852931575
    Gamma CALL:0.0230279263655

    3. The option strategy gave me a problem because i am running this on 7/7/2016 and you wrote it over 2 years ago. In the option strategies calculation you set m_expiry to ‘20150116’ but get_greeks(2) sets eval_date=date.today() in the Option(…) constructor. I had to change the ‘m_expiry': ‘20170113’ in order to get it to give me a positive days to expiration.

    I realize that it has been a while, but I thought I would share my experience so other people reading this excellent blog post

    • alfil March 10, 2017 at 7:52 pm #

      Many thanks Nicholas, I have not been working lately in this subject (sorry for late reply)

Leave a Reply

Current day month ye@r *

Powered by WordPress. Designed by Woo Themes