-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathoptions_IV.py
More file actions
156 lines (121 loc) · 5.47 KB
/
Copy pathoptions_IV.py
File metadata and controls
156 lines (121 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""
Created by: Gabriele Pompa (gabriele.pompa@gmail.com)
File: options_IV.py
Created on Tue Jul 14 2020 - Version: 1.0
Description:
This script shows usage of PlainVanillaOption and DigitalOption classes to compute of Black-Scholes implied volatility
surfaces for plain-vanilla and digital option contracts.
"""
import numpy as np
import pandas as pd
import warnings
from pyblackscholesanalytics.market.market import MarketEnvironment
from pyblackscholesanalytics.options.options import PlainVanillaOption, DigitalOption
warnings.filterwarnings("ignore")
def option_factory(mkt_env, plain_or_digital, option_type):
option_dispatcher = {
"plain_vanilla": {"call": PlainVanillaOption(mkt_env),
"put": PlainVanillaOption(mkt_env, option_type="put")
},
"digital": {"call": DigitalOption(mkt_env),
"put": DigitalOption(mkt_env, option_type="put")
}
}
return option_dispatcher[plain_or_digital][option_type]
def main():
#
# Black-Scholes implied volatility calculation with user-defined 'sigma'
# parameter surface, used to evaluate the quality of the implied volatility
# calculation.
#
# output format: pd.DataFrame
np_output = False
# default market environment
market_env = MarketEnvironment()
print(market_env)
# define option style and type
opt_style = "plain_vanilla" # "digital"
opt_type = "call" # "call" # "put"
option = option_factory(market_env, opt_style, opt_type)
print(option)
# K
K_vector = [50, 75, 100, 125, 150]
# tau: a date-range of 5 valuation dates between t and T-10d
n = 6
valuation_date = option.get_t()
expiration_date = option.get_T()
t_vector = pd.date_range(start=valuation_date,
end=expiration_date - pd.Timedelta(days=25),
periods=n)
# sigma (qualitatively reproducing the smile)
k, tau = np.meshgrid(K_vector, option.time_to_maturity(t=t_vector))
sigma_grid_K = 0.01 + ((k - 100) ** 2) / (100 * k) / tau
# pricing parameters
param_dict = {"S": 100,
"K": K_vector,
"t": t_vector,
"sigma": sigma_grid_K,
"r": 0.01,
"np_output": np_output}
print("Parameters:")
print("S: {}".format(param_dict["S"]))
print("K: {}".format(param_dict["K"]))
print("t: {}".format(param_dict["t"]))
print("sigma: \n{}".format(param_dict["sigma"]))
print("r: {}\n".format(param_dict["r"]))
# expected implied volatility: is the 'sigma' parameter with which the
# target price has been generated
expected_IV = pd.DataFrame(data=param_dict["sigma"],
columns=K_vector,
index=t_vector)
expected_IV.rename_axis('K', axis='columns', inplace=True)
expected_IV.rename_axis('t', axis='rows', inplace=True)
print("\nExpected Kxt Implied volatility Surface: \n", expected_IV)
#
# Without target_price in input: param_dict['sigma'] parameter is
# used to construct target price, used in minimization
#
print("\n--- WITHOUT target_price in input ---\n")
# newton method
param_dict["minimization_method"] = "Newton"
newton_IV = option.implied_volatility(**param_dict)
RMSE_newton = np.sqrt(np.nanmean((newton_IV - expected_IV) ** 2))
RMSRE_newton = np.sqrt(np.nanmean(((newton_IV - expected_IV) / expected_IV) ** 2))
print("\nImplied Volatility - Newton method - Metrics (NaN excluded): RMSE={:.1E}, RMSRE={:.1E}:\n"
.format(RMSE_newton, RMSRE_newton), newton_IV)
# Least-Squares method
param_dict["minimization_method"] = "Least-Squares"
ls_IV = option.implied_volatility(**param_dict)
RMSE_ls = np.sqrt(np.nanmean((ls_IV - expected_IV) ** 2))
RMSRE_ls = np.sqrt(np.nanmean(((ls_IV - expected_IV) / expected_IV) ** 2))
print(
"\nImplied Volatility - Least-Squares constrained method - Metrics (NaN excluded): RMSE={:.1E}, RMSRE={:.1E}:\n"
.format(RMSE_ls, RMSRE_ls), ls_IV)
#
# With target_price in input: target_price, but no param_dict['sigma'],
# is used in minimization.
#
print("\n--- WITH target_price in input ---\n")
# compute target price
target_price = option.price(**param_dict)
print("\nTarget Price in input: \n", target_price)
# Add target_price to parameters dictionary:
param_dict['target_price'] = target_price
# newton method
param_dict["minimization_method"] = "Newton"
newton_IV = option.implied_volatility(**param_dict)
RMSE_newton = np.sqrt(np.nanmean((newton_IV - expected_IV) ** 2))
RMSRE_newton = np.sqrt(np.nanmean(((newton_IV - expected_IV) / expected_IV) ** 2))
print("\nImplied Volatility - Newton method - Metrics (NaN excluded): RMSE={:.1E}, RMSRE={:.1E}:\n"
.format(RMSE_newton, RMSRE_newton), newton_IV)
# Least-Squares method
param_dict["minimization_method"] = "Least-Squares"
ls_IV = option.implied_volatility(**param_dict)
RMSE_ls = np.sqrt(np.nanmean((ls_IV - expected_IV) ** 2))
RMSRE_ls = np.sqrt(np.nanmean(((ls_IV - expected_IV) / expected_IV) ** 2))
print(
"\nImplied Volatility - Least-Squares constrained method - Metrics (NaN excluded): RMSE={:.1E}, RMSRE={:.1E}:\n"
.format(RMSE_ls, RMSRE_ls), ls_IV)
# ----------------------------- usage example ---------------------------------#
if __name__ == "__main__":
main()