Skip to content

Commit 1106341

Browse files
committed
增加美式期权
1 parent e8bf315 commit 1106341

5 files changed

+237
-50
lines changed

american_option.py

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
"""
2+
Author: shifulin
3+
4+
"""
5+
from math import sqrt, exp, inf
6+
import numpy as np
7+
8+
9+
def _call_price(s, k, sigma, r, t, steps=100):
10+
r_ = exp(r * (t / steps))
11+
r_reciprocal = 1.0 / r_
12+
u = exp(sigma * sqrt(t / steps))
13+
d = 1.0 / u
14+
u_square = u ** 2
15+
p_u = (r_ - d) / (u - d)
16+
p_d = 1.0 - p_u
17+
prices = np.zeros(steps + 1)
18+
prices[0] = s * d ** steps
19+
for i in range(1, steps + 1):
20+
prices[i] = prices[i - 1] * u_square
21+
values = np.zeros(steps + 1)
22+
for i in range(steps + 1):
23+
values[i] = max(0.0, prices[i] - k)
24+
for j in range(steps, 0, -1):
25+
for i in range(j):
26+
values[i] = (p_u * values[i + 1] + p_d * values[i]) * r_reciprocal
27+
prices[i] = d * prices[i + 1]
28+
values[i] = max(values[i], prices[i] - k)
29+
# print(values)
30+
return values[0]
31+
32+
33+
def _put_price(s, k, sigma, r, t, steps=100):
34+
r_ = exp(r * (t / steps))
35+
r_reciprocal = 1.0 / r_
36+
u = exp(sigma * sqrt(t / steps))
37+
d = 1.0 / u
38+
u_square = u ** 2
39+
p_u = (r_ - d) / (u - d)
40+
p_d = 1.0 - p_u
41+
prices = np.zeros(steps + 1)
42+
prices[0] = s * d ** steps
43+
for i in range(1, steps + 1):
44+
prices[i] = prices[i - 1] * u_square
45+
values = np.zeros(steps + 1)
46+
for i in range(steps + 1):
47+
values[i] = max(0, k - prices[i])
48+
for j in range(steps, 0, -1):
49+
for i in range(0, j):
50+
values[i] = (p_u * values[i + 1] + p_d * values[i]) * r_reciprocal
51+
prices[i] = d * prices[i + 1]
52+
values[i] = max(values[i], k - prices[i])
53+
return values[0]
54+
55+
56+
def call_price(s, k, sigma, r, t, steps=100):
57+
return (_call_price(s, k, sigma, r, t, steps) + _call_price(s, k, sigma, r, t, steps + 1)) / 2.0
58+
59+
60+
def put_price(s, k, sigma, r, t, steps=100):
61+
return (_put_price(s, k, sigma, r, t, steps) + _put_price(s, k, sigma, r, t, steps + 1)) / 2.0
62+
63+
64+
def delta(s, k, sigma, r, t, option_type, steps=100):
65+
if t == 0.0:
66+
if s == k:
67+
return {'Call': 0.5, 'Put': -0.5}[option_type]
68+
elif s > k:
69+
return {'Call': 1.0, 'Put': 0.0}[option_type]
70+
else:
71+
return {'Call': 0.0, 'Put': -1.0}[option_type]
72+
else:
73+
price_func = {'Call': call_price, 'Put': put_price}[option_type]
74+
return (price_func(s + 0.01, k, sigma, r, t, steps=steps) -
75+
price_func(s - 0.01, k, sigma, r, t, steps=steps)) * 50.0
76+
77+
78+
def gamma(s, k, sigma, r, t, option_type, steps=100):
79+
if t == 0.0:
80+
return inf if s == k else 0.0
81+
price_func = {'Call': call_price, 'Put': put_price}[option_type]
82+
return (price_func(s + 0.01, k, sigma, r, t, steps=steps) +
83+
price_func(s + 0.01, k, sigma, r, t, steps=steps) -
84+
price_func(s, k, sigma, r, t, steps=steps) * 2.0) * 10000.0
85+
86+
87+
def theta(s, k, sigma, r, t, option_type, steps=100):
88+
price_func = {'Call': call_price, 'Put': put_price}[option_type]
89+
t_unit = 1.0 / 365.0
90+
if t <= t_unit:
91+
return price_func(s, k, sigma, r, 0.0001, steps=steps) - \
92+
price_func(s, k, sigma, r, t, steps=steps)
93+
else:
94+
return price_func(s, k, sigma, r, t - t_unit, steps=steps) - \
95+
price_func(s, k, sigma, r, t, steps=steps)
96+
97+
98+
def vega(s, k, sigma, r, t, option_type, steps=100):
99+
price_func = {'Call': call_price, 'Put': put_price}[option_type]
100+
if sigma < 0.02:
101+
return 0.0
102+
else:
103+
return (price_func(s, k, sigma + 0.01, r, t, steps=steps) -
104+
price_func(s, k, sigma - 0.01, r, t, steps=steps)) * 50.0
105+
106+
107+
def rho(s, k, sigma, r, t, option_type, steps=100):
108+
price_func = {'Call': call_price, 'Put': put_price}[option_type]
109+
return (price_func(s, k, sigma, r + 0.001, t, steps=steps) -
110+
price_func(s, k, sigma, r - 0.001, t, steps=steps)) * 500.0
111+
112+
113+
def call_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.00001, steps=100):
114+
sigma_mid = (sigma_min + sigma_max) / 2.0
115+
call_min = call_price(s, k, sigma_min, r, t, steps)
116+
call_max = call_price(s, k, sigma_max, r, t, steps)
117+
call_mid = call_price(s, k, sigma_mid, r, t, steps)
118+
diff = c - call_mid
119+
if c <= call_min:
120+
return sigma_min
121+
elif c >= call_max:
122+
return sigma_max
123+
while abs(diff) > e:
124+
if c > call_mid:
125+
sigma_min = sigma_mid
126+
else:
127+
sigma_max = sigma_mid
128+
sigma_mid = (sigma_min + sigma_max) / 2.0
129+
call_mid = call_price(s, k, sigma_mid, r, t, steps)
130+
diff = c - call_mid
131+
# print(sigma_mid)
132+
return sigma_mid
133+
134+
135+
def put_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.00001, steps=100):
136+
sigma_mid = (sigma_min + sigma_max) / 2.0
137+
put_min = put_price(s, k, sigma_min, r, t, steps)
138+
put_max = put_price(s, k, sigma_max, r, t, steps)
139+
put_mid = put_price(s, k, sigma_mid, r, t, steps)
140+
diff = c - put_mid
141+
if c <= put_min:
142+
return sigma_min
143+
elif c >= put_max:
144+
return sigma_max
145+
while abs(diff) > e:
146+
if c > put_mid:
147+
sigma_min = sigma_mid
148+
else:
149+
sigma_max = sigma_mid
150+
sigma_mid = (sigma_min + sigma_max) / 2.0
151+
put_mid = put_price(s, k, sigma_mid, r, t, steps)
152+
diff = c - put_mid
153+
return sigma_mid
154+
155+
156+
def my_test():
157+
import matplotlib.pyplot as plt
158+
a = np.linspace(1.0 / 365.0, 2, 100)
159+
yc, yp = [], []
160+
for i in a:
161+
yc.append(vega(6.0, 5.0, 0.25, 0.03, i, option_type='Call', steps=100))
162+
yp.append(vega(6.0, 5.0, 0.25, 0.03, i, option_type='Put', steps=100))
163+
plt.plot(yc)
164+
plt.plot(yp)
165+
plt.show()
166+
167+
168+
def my_test2():
169+
# print(call_price(5.0, 5.0, 0.1, 0.03, 0.4))
170+
# call_price(5.0, 5.0, 0.25, 0.03, 0.4, 99)
171+
print(call_iv(0.138, 3.046, 3.1, 0.5, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.00001, steps=100))
172+
173+
174+
if __name__ == '__main__':
175+
my_test2()
176+

european_option.py

+52-46
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
"""
55
from math import log, sqrt, exp
6-
# import numpy as np
6+
import numpy as np
77
from scipy.stats import norm
88

99

@@ -17,50 +17,44 @@
1717
# d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
1818
# d2 = d1 - sigma * np.sqrt(t)
1919
# return k * np.exp(-r * t) * norm.cdf(-d2) - s * norm.cdf(-d1)
20-
#
21-
#
22-
# def delta(s, k, sigma, r, t, option_type, position_type):
23-
# d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
24-
# if option_type == 'Call':
25-
# if position_type == 'long':
26-
# return norm.cdf(d1)
27-
# else:
28-
# return -norm.cdf(d1)
29-
# else:
30-
# if position_type == 'long':
31-
# return norm.cdf(d1) - 1.0
32-
# else:
33-
# return 1.0 - norm.cdf(d1)
34-
#
35-
#
36-
# def gamma(s, k, sigma, r, t):
37-
# d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
38-
# return np.exp(-pow(d1, 2) / 2.0) / (s * sigma * np.sqrt(2.0 * np.pi * t))
39-
#
40-
#
41-
# def theta(s, k, sigma, r, t, option_type):
42-
# d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
43-
# d2 = d1 - sigma * np.sqrt(t)
44-
# theta_call = -(s * sigma * np.exp(-pow(d1, 2) / 2.0)) / (2.0 * np.sqrt(2.0 * np.pi * t)) - \
45-
# r * k * np.exp(-r * t) * norm.cdf(d2)
46-
# if option_type == 'Call':
47-
# return theta_call
48-
# else:
49-
# return theta_call + r * k * np.exp(-r * t)
50-
#
51-
#
52-
# def vega(s, k, sigma, r, t):
53-
# d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
54-
# return s * np.sqrt(t) * np.exp(-pow(d1, 2) / 2.0) / np.sqrt(2.0 * np.pi)
55-
#
56-
#
57-
# def rho(s, k, sigma, r, t, option_type):
58-
# d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
59-
# d2 = d1 - sigma * np.sqrt(t)
60-
# if option_type == 'Call':
61-
# return k * t * np.exp(-r * t) * norm.cdf(d2)
62-
# else:
63-
# return -k * t * np.exp(-r * t) * norm.cdf(-d2)
20+
21+
22+
def delta(s, k, sigma, r, t, option_type):
23+
d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
24+
if option_type == 'Call':
25+
return norm.cdf(d1)
26+
else:
27+
return norm.cdf(d1) - 1.0
28+
29+
30+
def gamma(s, k, sigma, r, t):
31+
d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
32+
return np.exp(-pow(d1, 2) / 2.0) / (s * sigma * np.sqrt(2.0 * np.pi * t))
33+
34+
35+
def theta(s, k, sigma, r, t, option_type):
36+
d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
37+
d2 = d1 - sigma * np.sqrt(t)
38+
theta_call = -(s * sigma * np.exp(-pow(d1, 2) / 2.0)) / (2.0 * np.sqrt(2.0 * np.pi * t)) - \
39+
r * k * np.exp(-r * t) * norm.cdf(d2)
40+
if option_type == 'Call':
41+
return theta_call
42+
else:
43+
return theta_call + r * k * np.exp(-r * t)
44+
45+
46+
def vega(s, k, sigma, r, t):
47+
d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
48+
return s * np.sqrt(t) * np.exp(-pow(d1, 2) / 2.0) / np.sqrt(2.0 * np.pi)
49+
50+
51+
def rho(s, k, sigma, r, t, option_type):
52+
d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t))
53+
d2 = d1 - sigma * np.sqrt(t)
54+
if option_type == 'Call':
55+
return k * t * np.exp(-r * t) * norm.cdf(d2)
56+
else:
57+
return -k * t * np.exp(-r * t) * norm.cdf(-d2)
6458

6559

6660
def bs_call(s, k, sigma, r, t):
@@ -124,6 +118,18 @@ def my_test():
124118
call_iv(0.138, 3.046, 3.1, 0.5, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.000001)
125119

126120

121+
def my_test2():
122+
import matplotlib.pyplot as plt
123+
a = np.linspace(0, 0.8, 100)
124+
yc, yp = [], []
125+
for i in a:
126+
yc.append(vega(6.0, 5.0, i, 0.03, 0.5))
127+
yp.append(vega(6.0, 5.0, i, 0.03, 0.5))
128+
plt.plot(yc)
129+
plt.plot(yp)
130+
plt.show()
131+
132+
127133
if __name__ == '__main__':
128-
my_test()
134+
my_test2()
129135

historical_implied_volatility.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
from sina_future_kline_api import get_future_day_kline
1515
from sina_commodity_option_api import get_option_kline as get_future_option_day_kline
1616
from sina_etf_option_api import get_option_day_kline as get_etf_option_day_kline
17-
from european_option import call_iv, put_iv
17+
import european_option
18+
import american_option
1819

1920

2021
ETF_SPOT_CODE = {
@@ -104,9 +105,9 @@ def align_kline(option_kline, spot_kline, window_size):
104105

105106
def cal_historical_iv(option_kline, spot_kline, strike_price, expiry_date, r, option_type, exercise_type):
106107
if exercise_type == 'european':
107-
iv_func = call_iv if option_type == 'call' else put_iv
108+
iv_func = european_option.call_iv if option_type == 'call' else european_option.put_iv
108109
else:
109-
pass
110+
iv_func = american_option.call_iv if option_type == 'call' else american_option.put_iv
110111
x, y = [], []
111112
for option, spot in zip(option_kline, spot_kline):
112113
x.append(str(option[0]))
@@ -156,6 +157,8 @@ def main(option_code, spot_code, strike_price, expiry_date, option_type, exercis
156157

157158

158159
if __name__ == '__main__':
159-
main('cu2003C51000', 'cu2003', 51000.0, '20200224', 'call', 'european', 5)
160+
# main('cu2003C51000', 'cu2003', 51000.0, '20200224', 'call', 'european', 5)
160161
# main('au2004P340', 'au2004', 340.0, '20200325', 'put', 'european', 10)
161162
# main('10002062', '510050', 3.0, '20200122', 'put', 'european', 20)
163+
# main('m2005C2800', 'm2005', 2800.0, '20200408', 'call', 'american', 5)
164+
main('m2005P2700', 'm2005', 2700.0, '20200408', 'put', 'american', 10)

sina_commodity_option_api.py

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
'ru': {'product': 'ru_o', 'exchange': 'shfe'},
1818
'cu': {'product': 'cu_o', 'exchange': 'shfe'},
1919
'au': {'product': 'au_o', 'exchange': 'shfe'},
20+
'rm': {'product': 'rm', 'exchange': 'czce'},
2021
}
2122
URL_T_QUOTATION = "http://stock.finance.sina.com.cn/futures/api/openapi.php/OptionService.getOptionData?" \
2223
"type=futures&product={product}&exchange={exchange}&pinzhong={code}"

volatility_surface.py

+1
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ def main(cate, exchange, underlying, dividend=True, is_fit=True):
246246
ax_iv_sf_call = fig.add_subplot(gs[:2, :2], projection='3d')
247247
ax_iv_sf_call.view_init(ELEV, AZIM)
248248
# surf_call = ax_iv_sf_call.plot_surface(x, y, array(call_y2), rstride=1, cstride=1, cmap='rainbow')
249+
# print(x.shape, y.shape, array(call_y2).shape)
249250
surf_call = ax_iv_sf_call.plot_wireframe(x, y, array(call_y2), rstride=1, cstride=1, cmap='rainbow')
250251
ax_iv_sf_call.set_yticklabels(dates_label)
251252
ax_iv_sf_call.set_xlabel('Strike Price')

0 commit comments

Comments
 (0)