-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathoption.py
151 lines (133 loc) · 5.39 KB
/
option.py
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
import datetime
import math
import pytz
from scipy.stats import norm
day = 86400
class Option:
def __init__(self,
underlying_pair,
option_type,
strike,
expiry,
interest_rate=0,
volatility=None,
underlying_price=None,
time=datetime.datetime.now(),
exchange_symbol=None):
self.underlying_pair = underlying_pair
if not option_type == 'call' and not option_type == 'put':
raise ValueError('Expected "call" or "put", got ' + option_type)
self.option_type = option_type
self.strike = strike
self.expiry = expiry
self.interest_rate = interest_rate
if volatility is not None:
self.vol = volatility # decimal
self.underlying_price = underlying_price
self.time = time
self.exchange_symbol = exchange_symbol
self.time_left = self.get_time_left(self.time)
self.d1 = None
self.d2 = None
self.theo = None
self.delta = None
self.gamma = None
self.theta = None
self.vega = None
self.wvega = None
self.present_value = None
self.mid_market = None
def __str__(self):
return self.underlying_pair + " " + str(self.strike) + " " + self.option_type + " with expiry " + str(self.expiry)
def get_time_left(self, current_datetime=datetime.datetime.now()):
return (self.expiry - current_datetime).total_seconds() / (day * 365)
def set_underlying_price(self, underlying_price):
self.underlying_price = underlying_price
def set_time(self, time):
self.time = time
def set_vol(self, vol):
self.vol = vol
def set_mid_market(self, mid_market):
self.mid_market = mid_market
def calc_greeks(self, verbose=False):
self.calc_theo()
self.calc_delta()
self.calc_gamma()
self.calc_theta()
self.calc_vega()
if verbose:
print("Calculated greeks for " + str(self) + ": delta=" + str(self.delta) + ", gamma=" + str(self.gamma)
+ ", theta=" + str(self.theta))
def calc_theo(self, time_left=None, store=True):
if not time_left:
time_left = self.time_left
d1 = (math.log(self.underlying_price / self.strike) + (self.interest_rate + ((self.vol ** 2) / 2.0))
* time_left) / (self.vol * math.sqrt(time_left))
d2 = d1 - (self.vol * math.sqrt(time_left))
present_value = self.strike * math.exp(-self.interest_rate * time_left)
theo = None
if self.option_type == "call":
theo = max((self.underlying_price * norm.cdf(d1)) - (present_value * norm.cdf(d2)), 0)
elif self.option_type == "put":
theo = max((norm.cdf(-d2) * present_value) - (norm.cdf(-d1) * self.underlying_price), 0)
if store:
self.d1 = d1
self.d2 = d2
self.theo = theo
self.present_value = present_value
return theo
def calc_delta(self, underlying_price=None, store=True):
if underlying_price:
d1 = (math.log(underlying_price / self.strike) + (self.interest_rate + ((self.vol ** 2) / 2.0))
* self.time_left) / (self.vol * math.sqrt(self.time_left))
else:
d1 = self.d1
delta = None
if self.option_type == 'call':
delta = norm.cdf(d1)
elif self.option_type == 'put':
delta = -norm.cdf(-d1)
if store:
self.delta = delta
return delta
def calc_gamma(self, underlying_pct_change=.1):
original_delta = self.calc_delta()
underlying_change = self.underlying_price * (underlying_pct_change / 100)
incremented_price = self.underlying_price + underlying_change
incremented_delta = self.calc_delta(underlying_price=incremented_price, store=False)
self.gamma = (incremented_delta - original_delta) / underlying_change
return self.gamma
def calc_theta(self, time_change=1/365):
if time_change >= self.time_left:
time_change = self.time_left / 2
original_theo = self.calc_theo()
advanced_time = self.time_left - time_change
advanced_theo = self.calc_theo(time_left=advanced_time)
self.theta = -1 * (advanced_theo - original_theo)
return self.theta
def calc_vega(self, vol_change=.01):
original_vol = self.vol
original_theo = self.theo
self.vol = original_vol * (1 + vol_change)
new_theo = self.calc_theo(store=False)
self.vol = original_vol
self.vega = new_theo - original_theo
return self.vega
# Weighted vega = vega / atm_vega
def calc_wvega(self, atm_vega):
self.wvega = self.calc_vega() / atm_vega
return self.wvega
# Price in BTC
def calc_implied_vol(self, btc_price, num_iterations=100, accuracy=.05, low_vol=0, high_vol=10):
usd_price = btc_price * self.underlying_price
self.calc_theo()
for i in range(num_iterations):
if self.theo > usd_price + accuracy:
high_vol = self.vol
elif self.theo < usd_price - accuracy:
low_vol = self.vol
else:
break
self.vol = low_vol + (high_vol - low_vol) / 2.0
self.calc_theo()
return self.vol