# -*- coding: utf-8 -*-
"""
py_vollib.ref_python.black.greeks.analytical
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A library for option pricing, implied volatility, and
greek calculation. py_vollib is based on lets_be_rational,
a Python wrapper for LetsBeRational by Peter Jaeckel as
described below.
:copyright: © 2023 Larry Richards
:license: MIT, see LICENSE for more details.
py_vollib.ref_python is a pure python version of py_vollib without any dependence on LetsBeRational. It is provided purely as a reference implementation for sanity checking. It is not recommended for industrial use.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
"""
# -----------------------------------------------------------------------------
# IMPORTS
# Standard library imports
from __future__ import division
# Related third party imports
import numpy
from scipy.stats import norm
# Local application/library specific imports
from py_vollib.helpers import pdf
from py_vollib.ref_python.black import d1, d2, black
N = norm.cdf
# -----------------------------------------------------------------------------
# FUNCTIONS - ANALYTICAL GREEKS
[docs]def delta(flag, F, K, t, r, sigma):
"""Returns the Black delta of an option.
:param flag: 'c' or 'p' for call or put.
:type flag: str
:param F: underlying futures price
:type F: float
:param K: strike price
:type K: float
:param t: time to expiration in years
:type t: float
:param r: annual risk-free interest rate
:type r: float
:param sigma: volatility
:type sigma: float
:returns: float
>>> F = 49
>>> K = 50
>>> r = .05
>>> t = 0.3846
>>> sigma = 0.2
>>> flag = 'c'
>>> v1 = delta(flag, F, K, t, r, sigma)
>>> v2 = 0.45107017482201828
>>> abs(v1-v2) < .000001
True
"""
D1 = d1(F, K, t, r, sigma)
if flag == 'p':
return - numpy.exp(-r * t) * N(-D1)
else:
return numpy.exp(-r * t) * N(D1)
[docs]def theta(flag, F, K, t, r, sigma):
"""Returns the Black theta of an option.
:param flag: 'c' or 'p' for call or put.
:type flag: str
:param F: underlying futures price
:type F: float
:param K: strike price
:type K: float
:param t: time to expiration in years
:type t: float
:param r: annual risk-free interest rate
:type r: float
:param sigma: volatility
:type sigma: float
:returns: float
>>> F = 49
>>> K = 50
>>> r = .05
>>> t = 0.3846
>>> sigma = 0.2
>>> flag = 'c'
>>> v1 = theta(flag, F, K, t, r, sigma)
>>> v2 = -0.00816236877462
>>> abs(v1-v2) < .000001
True
>>> flag = 'p'
>>> v1 = theta(flag, F, K, t, r, sigma)
>>> v2 = -0.00802799155312
>>> abs(v1-v2) < .000001
True
"""
e_to_the_minus_rt = numpy.exp(-r * t)
two_sqrt_t = 2 * numpy.sqrt(t)
D1 = d1(F, K, t, r, sigma)
D2 = d2(F, K, t, r, sigma)
pdf_d1 = pdf(D1)
N_d2 = N(D2)
first_term = F * e_to_the_minus_rt * pdf(D1) * sigma / two_sqrt_t
if flag == 'c':
second_term = -r * F * e_to_the_minus_rt * N(D1)
third_term = r * K * e_to_the_minus_rt * N(D2)
return -(first_term + second_term + third_term) / 365.
else:
second_term = -r * F * e_to_the_minus_rt * N(-D1)
third_term = r * K * e_to_the_minus_rt * N(-D2)
return (-first_term + second_term + third_term) / 365.0
return (first_term - second_term) / 365.0
[docs]def gamma(flag, F, K, t, r, sigma):
"""Returns the Black gamma of an option.
:param flag: 'c' or 'p' for call or put.
:type flag: str
:param F: underlying futures price
:type F: float
:param K: strike price
:type K: float
:param t: time to expiration in years
:type t: float
:param r: annual risk-free interest rate
:type r: float
:param sigma: volatility
:type sigma: float
:returns: float
>>> F = 49
>>> K = 50
>>> r = .05
>>> t = 0.3846
>>> sigma = 0.2
>>> flag = 'c'
>>> v1 = gamma(flag, F, K, t, r, sigma)
>>> # 0.0640646705882
>>> v2 = 0.0640646705882
>>> abs(v1-v2) < .000001
True
"""
D1 = d1(F, K, t, r, sigma)
return pdf(D1) * numpy.exp(-r * t) / (F * sigma * numpy.sqrt(t))
[docs]def vega(flag, F, K, t, r, sigma):
"""Returns the Black vega of an option.
:param flag: 'c' or 'p' for call or put.
:type flag: str
:param F: underlying futures price
:type F: float
:param K: strike price
:type K: float
:param t: time to expiration in years
:type t: float
:param r: annual risk-free interest rate
:type r: float
:param sigma: volatility
:type sigma: float
:returns: float
::
==========================================================
Note: The text book analytical formula does not multiply by .01,
but in practice vega is defined as the change in price
for each 1 percent change in IV, hence we multiply by 0.01.
==========================================================
>>> F = 49
>>> K = 50
>>> r = .05
>>> t = 0.3846
>>> sigma = 0.2
>>> flag = 'c'
>>> v1 = vega(flag, F, K, t, r, sigma)
>>> # 0.118317785624
>>> v2 = 0.118317785624
>>> abs(v1-v2) < .000001
True
"""
D1 = d1(F, K, t, r, sigma)
return F * numpy.exp(-r * t) * pdf(D1) * numpy.sqrt(t) * 0.01
[docs]def rho(flag, F, K, t, r, sigma):
"""Returns the Black rho of an option.
:param flag: 'c' or 'p' for call or put.
:type flag: str
:param F: underlying futures price
:type F: float
:param K: strike price
:type K: float
:param t: time to expiration in years
:type t: float
:param r: annual risk-free interest rate
:type r: float
:param sigma: volatility
:type sigma: float
:returns: float
::
==========================================================
The text book analytical formula does not multiply by .01,
but in practice rho is defined as the change in price
for each 1 percent change in r, hence we multiply by 0.01.
==========================================================
>>> F = 49
>>> K = 50
>>> r = .05
>>> t = 0.3846
>>> sigma = 0.2
>>> flag = 'c'
>>> v1 = rho(flag, F, K, t, r, sigma)
>>> v2 = -0.0074705380059582258
>>> abs(v1-v2) < .000001
True
>>> flag = 'p'
>>> v1 = rho(flag, F, K, t, r, sigma)
>>> v2 = -0.011243286001308292
>>> abs(v1-v2) < .000001
True
"""
return -t * black(flag, F, K, t, r, sigma) * .01
if __name__ == "__main__":
from py_vollib.helpers.doctest_helper import run_doctest
run_doctest()