Repository: sfl666/option_tools Branch: master Commit: 092221184457 Files: 15 Total size: 74.8 KB Directory structure: gitextract_7kzs8_1g/ ├── .gitignore ├── american_option.py ├── baw.py ├── european_option.py ├── historical_implied_volatility.py ├── historical_volatility.py ├── readme.md ├── sina_commodity_option_api.py ├── sina_commodity_option_spider.py ├── sina_etf_option_api.py ├── sina_future_kline_api.py ├── sina_stock_kline_api.py ├── time_line_iv.py ├── volatility_surface.py └── volatility_surface2.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # pyenv .python-version # celery beat schedule file celerybeat-schedule # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ # pycharm .idea/ ================================================ FILE: american_option.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ from math import sqrt, exp, inf import numpy as np def _call_price(s, k, sigma, r, t, steps=100): r_ = exp(r * (t / steps)) r_reciprocal = 1.0 / r_ u = exp(sigma * sqrt(t / steps)) d = 1.0 / u u_square = u ** 2 p_u = (r_ - d) / (u - d) p_d = 1.0 - p_u prices = np.zeros(steps + 1) prices[0] = s * d ** steps for i in range(1, steps + 1): prices[i] = prices[i - 1] * u_square values = np.zeros(steps + 1) for i in range(steps + 1): values[i] = max(0.0, prices[i] - k) for j in range(steps, 0, -1): for i in range(j): values[i] = (p_u * values[i + 1] + p_d * values[i]) * r_reciprocal prices[i] = d * prices[i + 1] values[i] = max(values[i], prices[i] - k) # print(values) return values[0] def _put_price(s, k, sigma, r, t, steps=100): r_ = exp(r * (t / steps)) r_reciprocal = 1.0 / r_ u = exp(sigma * sqrt(t / steps)) d = 1.0 / u u_square = u ** 2 p_u = (r_ - d) / (u - d) p_d = 1.0 - p_u prices = np.zeros(steps + 1) prices[0] = s * d ** steps for i in range(1, steps + 1): prices[i] = prices[i - 1] * u_square values = np.zeros(steps + 1) for i in range(steps + 1): values[i] = max(0, k - prices[i]) for j in range(steps, 0, -1): for i in range(0, j): values[i] = (p_u * values[i + 1] + p_d * values[i]) * r_reciprocal prices[i] = d * prices[i + 1] values[i] = max(values[i], k - prices[i]) return values[0] def call_price(s, k, sigma, r, t, steps=100): return (_call_price(s, k, sigma, r, t, steps) + _call_price(s, k, sigma, r, t, steps + 1)) / 2.0 def put_price(s, k, sigma, r, t, steps=100): return (_put_price(s, k, sigma, r, t, steps) + _put_price(s, k, sigma, r, t, steps + 1)) / 2.0 def delta(s, k, sigma, r, t, option_type, steps=100): if t == 0.0: if s == k: return {'Call': 0.5, 'Put': -0.5}[option_type] elif s > k: return {'Call': 1.0, 'Put': 0.0}[option_type] else: return {'Call': 0.0, 'Put': -1.0}[option_type] else: price_func = {'Call': call_price, 'Put': put_price}[option_type] return (price_func(s + 0.01, k, sigma, r, t, steps=steps) - price_func(s - 0.01, k, sigma, r, t, steps=steps)) * 50.0 def gamma(s, k, sigma, r, t, option_type, steps=100): if t == 0.0: return inf if s == k else 0.0 price_func = {'Call': call_price, 'Put': put_price}[option_type] return (price_func(s + 0.01, k, sigma, r, t, steps=steps) + price_func(s - 0.01, k, sigma, r, t, steps=steps) - price_func(s, k, sigma, r, t, steps=steps) * 2.0) * 10000.0 def theta(s, k, sigma, r, t, option_type, steps=100): price_func = {'Call': call_price, 'Put': put_price}[option_type] t_unit = 1.0 / 365.0 if t <= t_unit: return price_func(s, k, sigma, r, 0.0001, steps=steps) - \ price_func(s, k, sigma, r, t, steps=steps) else: return price_func(s, k, sigma, r, t - t_unit, steps=steps) - \ price_func(s, k, sigma, r, t, steps=steps) def vega(s, k, sigma, r, t, option_type, steps=100): price_func = {'Call': call_price, 'Put': put_price}[option_type] if sigma < 0.02: return 0.0 else: return (price_func(s, k, sigma + 0.01, r, t, steps=steps) - price_func(s, k, sigma - 0.01, r, t, steps=steps)) * 50.0 def rho(s, k, sigma, r, t, option_type, steps=100): price_func = {'Call': call_price, 'Put': put_price}[option_type] return (price_func(s, k, sigma, r + 0.001, t, steps=steps) - price_func(s, k, sigma, r - 0.001, t, steps=steps)) * 500.0 def call_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=3.0, e=0.00001, steps=100): sigma_mid = (sigma_min + sigma_max) / 2.0 call_min = call_price(s, k, sigma_min, r, t, steps) call_max = call_price(s, k, sigma_max, r, t, steps) call_mid = call_price(s, k, sigma_mid, r, t, steps) diff = c - call_mid if c <= call_min: return sigma_min elif c >= call_max: return sigma_max while abs(diff) > e: if c > call_mid: sigma_min = sigma_mid else: sigma_max = sigma_mid sigma_mid = (sigma_min + sigma_max) / 2.0 call_mid = call_price(s, k, sigma_mid, r, t, steps) diff = c - call_mid # print(sigma_mid) return sigma_mid def put_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=3.0, e=0.00001, steps=100): sigma_mid = (sigma_min + sigma_max) / 2.0 put_min = put_price(s, k, sigma_min, r, t, steps) put_max = put_price(s, k, sigma_max, r, t, steps) put_mid = put_price(s, k, sigma_mid, r, t, steps) diff = c - put_mid if c <= put_min: return sigma_min elif c >= put_max: return sigma_max while abs(diff) > e: if c > put_mid: sigma_min = sigma_mid else: sigma_max = sigma_mid sigma_mid = (sigma_min + sigma_max) / 2.0 put_mid = put_price(s, k, sigma_mid, r, t, steps) diff = c - put_mid return sigma_mid def my_test(): import matplotlib.pyplot as plt a = np.linspace(1.0 / 365.0, 2, 100) yc, yp = [], [] for i in a: yc.append(vega(6.0, 5.0, 0.25, 0.03, i, option_type='Call', steps=100)) yp.append(vega(6.0, 5.0, 0.25, 0.03, i, option_type='Put', steps=100)) plt.plot(yc) plt.plot(yp) plt.show() def my_test2(): # print(call_price(5.0, 5.0, 0.1, 0.03, 0.4)) # call_price(5.0, 5.0, 0.25, 0.03, 0.4, 99) 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)) if __name__ == '__main__': my_test2() ================================================ FILE: baw.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ from math import log, sqrt, exp, inf from scipy.stats import norm import scipy.optimize as opt def bsm_call(s, k, sigma, t, r, q): sqrt_t = sqrt(t) d1 = (log(s / k) + (r - q + sigma ** 2 / 2.0) * t) / (sigma * sqrt_t) d2 = d1 - sigma * sqrt_t return s * exp(-q * t) * norm.cdf(d1) - k * exp(-r * t) * norm.cdf(d2) def bsm_put(s, k, sigma, t, r, q): sqrt_t = sqrt(t) d1 = (log(s / k) + (r - q + sigma ** 2 / 2.0) * t) / (sigma * sqrt_t) d2 = d1 - sigma * sqrt_t return k * exp(-r * t) * norm.cdf(-d2) - s * exp(-q * t) * norm.cdf(-d1) def find_sx(sx, k, sigma, t, r, q, option_type): n = 2.0 * (r - q) / sigma ** 2 k_ = 2.0 * r / sigma ** 2 / (1.0 - exp(-r * t)) if sx < 0.0: return inf if option_type == 'Call': q2 = (1.0 - n + sqrt((n - 1.0) ** 2 + 4.0 * k_)) / 2.0 return (bsm_call(sx, k, sigma, t, r, q) + (1.0 - exp(-q * t) * norm.cdf((log(sx / k) + (r - q + sigma ** 2 / 2.0)) / (sigma * sqrt(t)))) * sx / q2 - sx + k) ** 2 else: q1 = (1.0 - n - sqrt((n - 1.0) ** 2 + 4.0 * k_)) / 2.0 return (bsm_put(sx, k, sigma, t, r, q) - (1.0 - exp(-q * t) * norm.cdf(-(log(sx / k) + (r - q + sigma ** 2 / 2.0)) / (sigma * sqrt(t)))) * sx / q1 + sx - k) ** 2 def baw_call(s, k, sigma, t, r, q=0.0): c = bsm_call(s, k, sigma, t, r, q) sx = opt.fmin(lambda i: find_sx(i, k, sigma, t, r, q, 'Call'), s)[0] d1 = (log(sx / k) + (r - q + sigma ** 2 / 2.0)) / (sigma * sqrt(t)) n = 2.0 * (r - q) / sigma ** 2.0 k_ = 2.0 * r / (sigma ** 2 * (1.0 - exp(-r * t))) q2 = (1.0 - n + sqrt((n - 1.0) ** 2 + 4.0 * k_)) / 2.0 a2 = sx * (1.0 - exp(-q * t) * norm.cdf(d1)) / q2 return c + a2 * (s / sx) ** q2 if s < sx else s - k def baw_put(s, k, sigma, t, r, q=0.0): p = bsm_put(s, k, sigma, t, r, q) sx = opt.fmin(lambda i: find_sx(i, k, sigma, t, r, q, 'Put'), s)[0] d1 = (log(sx / k) + (r - q + sigma ** 2 / 2.0)) / (sigma * sqrt(t)) n = 2.0 * (r - q) / sigma ** 2 k_ = 2.0 * r / (sigma ** 2 * (1.0 - exp(-r * t))) q1 = (1.0 - n - sqrt((n - 1.0) ** 2 + 4.0 * k_)) / 2.0 a1 = -sx * (1.0 - exp(-q * t) * norm.cdf(-d1)) / q1 return p + a1 * (s / sx) ** q1 if s > sx else k - s def call_iv(c, s, k, t, r=0.03, sigma_min=0.0001, sigma_max=3.0, e=0.00001): sigma_mid = (sigma_min + sigma_max) / 2.0 call_min = bsm_call(s, k, sigma_min, t, r, 0.0) call_max = bsm_call(s, k, sigma_max, t, r, 0.0) call_mid = bsm_call(s, k, sigma_mid, t, r, 0.0) diff = c - call_mid if c <= call_min: return sigma_min elif c >= call_max: return sigma_max while abs(diff) > e: if c > call_mid: sigma_min = sigma_mid else: sigma_max = sigma_mid sigma_mid = (sigma_min + sigma_max) / 2.0 call_mid = bsm_call(s, k, sigma_mid, t, r, 0.0) diff = c - call_mid return sigma_mid def put_iv(c, s, k, t, r=0.03, sigma_min=0.0001, sigma_max=3.0, e=0.00001): sigma_mid = (sigma_min + sigma_max) / 2.0 put_min = bsm_put(s, k, sigma_min, t, r, 0.0) put_max = bsm_put(s, k, sigma_max, t, r, 0.0) put_mid = bsm_put(s, k, sigma_mid, t, r, 0.0) diff = c - put_mid if c <= put_min: return sigma_min elif c >= put_max: return sigma_max while abs(diff) > e: if c > put_mid: sigma_min = sigma_mid else: sigma_max = sigma_mid sigma_mid = (sigma_min + sigma_max) / 2.0 put_mid = bsm_put(s, k, sigma_mid, t, r, 0.0) diff = c - put_mid return sigma_mid def delta(s, k, sigma, t, r, option_type): if t == 0.0: if s == k: return 0.5 if option_type == 'Call' else -0.5 elif s > k: return 1.0 if option_type == 'Call' else 0.0 else: return 0.0 if option_type == 'Call' else -1.0 else: price_func = baw_call if option_type == 'Call' else baw_put return (price_func(s + 0.01, k, sigma, t, r) - price_func(s - 0.01, k, sigma, t, r)) * 50.0 def gamma(s, k, sigma, t, r, option_type): if t == 0.0: return inf if s == k else 0.0 price_func = baw_call if option_type == 'Call' else baw_put return (price_func(s + 0.01, k, sigma, t, r) + price_func(s - 0.01, k, sigma, t, r) - price_func(s, k, sigma, t, r) * 2.0) * 10000.0 def theta(s, k, sigma, t, r, option_type): price_func = baw_call if option_type == 'Call' else baw_put t_unit = 1.0 / 365.0 if t <= t_unit: return price_func(s, k, sigma, 0.0001, r) - \ price_func(s, k, sigma, t, r) else: return price_func(s, k, sigma, t - t_unit, r) - \ price_func(s, k, sigma, t, r) def vega(s, k, sigma, t, r, option_type): price_func = baw_call if option_type == 'Call' else baw_put if sigma < 0.02: return 0.0 else: return (price_func(s, k, sigma + 0.01, t, r) - price_func(s, k, sigma - 0.01, t, r)) * 50.0 def rho(s, k, sigma, t, r, option_type): price_func = baw_call if option_type == 'Call' else baw_put return (price_func(s, k, sigma, t, r + 0.001,) - price_func(s, k, sigma, t, r - 0.001,)) * 500.0 if __name__ == '__main__': # print(baw_call(2707, 2900, 0.165, 78.0 / 365, 0.03, 0.0)) # print(baw_put(2710, 2750, 0.15, 78.0 / 365, 0.03, 0.0)) print(call_iv(24.0, 2710, 2900, 78.0 / 365)) print(put_iv(92.5, 2710, 2750, 78.0 / 365)) ================================================ FILE: european_option.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ from math import log, sqrt, exp import numpy as np from scipy.stats import norm # def bs_call(s, k, sigma, r, t): # d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t)) # d2 = d1 - sigma * np.sqrt(t) # return s * norm.cdf(d1) - k * np.exp(-r * t) * norm.cdf(d2) # # # def bs_put(s, k, sigma, r, t): # d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t)) # d2 = d1 - sigma * np.sqrt(t) # return k * np.exp(-r * t) * norm.cdf(-d2) - s * norm.cdf(-d1) def greeks(s, k, sigma, r, t, option_type): sqrt_t = sqrt(t) d1 = (log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * sqrt_t) d2 = d1 - sigma * sqrt_t tmp = exp(-pow(d1, 2) / 2.0) tmp2 = sqrt(2.0 * np.pi * t) tmp3 = r * k * exp(-r * t) gamma = tmp / (s * sigma * tmp2) theta_call = -(s * sigma * tmp) / (2.0 * tmp2) - tmp3 * norm.cdf(d2) vega = s * sqrt_t * tmp / sqrt(2.0 * np.pi) if option_type == 'Call': delta = norm.cdf(d1) theta = theta_call else: delta = norm.cdf(d1) - 1.0 theta = theta_call + tmp3 return delta, gamma, theta, vega # # def delta(s, k, sigma, r, t, option_type): # d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t)) # if option_type == 'Call': # return norm.cdf(d1) # else: # return norm.cdf(d1) - 1.0 # # # def gamma(s, k, sigma, r, t): # d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t)) # return np.exp(-pow(d1, 2) / 2.0) / (s * sigma * np.sqrt(2.0 * np.pi * t)) # # # def theta(s, k, sigma, r, t, option_type): # d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t)) # d2 = d1 - sigma * np.sqrt(t) # theta_call = -(s * sigma * np.exp(-pow(d1, 2) / 2.0)) / (2.0 * np.sqrt(2.0 * np.pi * t)) - \ # r * k * np.exp(-r * t) * norm.cdf(d2) # if option_type == 'Call': # return theta_call # else: # return theta_call + r * k * np.exp(-r * t) # # # def vega(s, k, sigma, r, t): # d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t)) # return s * np.sqrt(t) * np.exp(-pow(d1, 2) / 2.0) / np.sqrt(2.0 * np.pi) # # # def rho(s, k, sigma, r, t, option_type): # d1 = (np.log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * np.sqrt(t)) # d2 = d1 - sigma * np.sqrt(t) # if option_type == 'Call': # return k * t * np.exp(-r * t) * norm.cdf(d2) # else: # return -k * t * np.exp(-r * t) * norm.cdf(-d2) def bs_call(s, k, sigma, r, t): tmp = sqrt(t) d1 = (log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * tmp) d2 = d1 - sigma * tmp return s * norm.cdf(d1) - k * exp(-r * t) * norm.cdf(d2) def bs_put(s, k, sigma, r, t): tmp = sqrt(t) d1 = (log(s / k) + (r + pow(sigma, 2) / 2.0) * t) / (sigma * tmp) d2 = d1 - sigma * tmp return k * exp(-r * t) * norm.cdf(-d2) - s * norm.cdf(-d1) def call_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.00001): sigma_mid = (sigma_min + sigma_max) / 2.0 call_min = bs_call(s, k, sigma_min, r, t) call_max = bs_call(s, k, sigma_max, r, t) call_mid = bs_call(s, k, sigma_mid, r, t) diff = c - call_mid if c <= call_min: return sigma_min elif c >= call_max: return sigma_max while abs(diff) > e: if c > call_mid: sigma_min = sigma_mid else: sigma_max = sigma_mid sigma_mid = (sigma_min + sigma_max) / 2.0 call_mid = bs_call(s, k, sigma_mid, r, t) diff = c - call_mid # print(sigma_mid) return sigma_mid def put_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.00001): sigma_mid = (sigma_min + sigma_max) / 2.0 put_min = bs_put(s, k, sigma_min, r, t) put_max = bs_put(s, k, sigma_max, r, t) put_mid = bs_put(s, k, sigma_mid, r, t) diff = c - put_mid if c <= put_min: return sigma_min elif c >= put_max: return sigma_max while abs(diff) > e: if c > put_mid: sigma_min = sigma_mid else: sigma_max = sigma_mid sigma_mid = (sigma_min + sigma_max) / 2.0 put_mid = bs_put(s, k, sigma_mid, r, t) diff = c - put_mid return sigma_mid # def my_test(): # call_iv(0.138, 3.046, 3.1, 0.5, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.000001) # # # def my_test2(): # import matplotlib.pyplot as plt # a = np.linspace(0, 0.8, 100) # yc, yp = [], [] # for i in a: # yc.append(vega(6.0, 5.0, i, 0.03, 0.5)) # yp.append(vega(6.0, 5.0, i, 0.03, 0.5)) # plt.plot(yc) # plt.plot(yp) # plt.show() # if __name__ == '__main__': # my_test2() ================================================ FILE: historical_implied_volatility.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ import math import datetime from io import BytesIO from copy import deepcopy import matplotlib.pyplot as plt from sina_stock_kline_api import get_stock_day_kline from sina_future_kline_api import get_future_day_kline from sina_commodity_option_api import get_option_kline as get_future_option_day_kline from sina_etf_option_api import get_option_day_kline as get_etf_option_day_kline import european_option # import american_option import baw ETF_SPOT_CODE = { '510050': 'sh510050', '510300': 'sh510300', '159919': 'sz159919', } STOCK_SPOT_CODE = deepcopy(ETF_SPOT_CODE) STOCK_SPOT_CODE.update({ '000300': 'sh000300', }) def days_interval(date1, date2): d1 = datetime.datetime.strptime(str(date1), "%Y%m%d") d2 = datetime.datetime.strptime(str(date2), "%Y%m%d") days = abs((d1 - d2).days) return days, float(days) / 365.0 def get_kline(option_code, spot_code): if spot_code in ETF_SPOT_CODE: option_kline = get_etf_option_day_kline(option_code) else: option_kline = get_future_option_day_kline(option_code) if spot_code in STOCK_SPOT_CODE: spot_kline = get_stock_day_kline(STOCK_SPOT_CODE[spot_code]) else: spot_kline = get_future_day_kline(spot_code) return option_kline, spot_kline def align_kline(option_kline, spot_kline): if not option_kline or not spot_kline: return [], [] else: if 'c' in option_kline[0]: date_key, close_key, date_func = 'd', 'c', lambda x: int(''.join(x.split('-'))) else: date_key, close_key, date_func = 'date', 'close', lambda x: int(''.join(x[:10].split('-'))) option_data = [(date_func(i[date_key]), float(i[close_key])) for i in option_kline] if 'c' in spot_kline[0]: date_key, close_key, date_func = 'd', 'c', lambda x: int(''.join(x.split('-'))) else: date_key, close_key, date_func = 'date', 'close', lambda x: int(''.join(x[:10].split('-'))) spot_data, option_data2 = [], [] len_option_data = len(option_data) index = 0 for k in spot_kline: k_date = date_func(k[date_key]) op_date = option_data[index][0] while k_date > op_date: # print(f'Warning, miss option kline date: {k_date}', option_data[index]) index += 1 op_date = option_data[index][0] if k_date == op_date: spot_data.append((k_date, float(k[close_key]))) option_data2.append(option_data[index]) index += 1 if index >= len_option_data: break # print(option_data) # print(spot_data) return option_data2, spot_data def cal_historical_iv(option_kline, spot_kline, strike_price, expiry_date, r, option_type, exercise_type): if exercise_type == 'european': iv_func = european_option.call_iv if option_type == 'Call' else european_option.put_iv else: iv_func = baw.call_iv if option_type == 'Call' else baw.put_iv x, y, option_cp, spot_cp = [], [], [], [] for option, spot in zip(option_kline, spot_kline): x.append(str(option[0])) t = days_interval(option[0], expiry_date)[1] y.append(iv_func(option[1], spot[1], strike_price, t, r=r)) option_cp.append(option[1]) spot_cp.append(spot[1]) return x, y, option_cp, spot_cp def draw_picture(option_code, x, iv, option_cp, spot_cp, show=True): interval = math.ceil(len(x) / 20) real_x = list(range(len(x))) x_index = real_x[::-interval] x_label = x[::-interval] fig, axs = plt.subplots(2, sharex=True, gridspec_kw={'hspace': 0}, figsize=(12.0, 5.7)) axs[0].plot(iv, color='r') axs[0].set_xlim((real_x[0], real_x[-1])) axs[0].set_ylabel('Implied Volatility') axs[0].set_title(option_code) axs[0].grid() line1 = axs[1].plot(option_cp, 'blue', label='option') ax2 = axs[1].twinx() line2 = ax2.plot(spot_cp, 'orange', label='spot') axs[1].set_xticks(x_index[::-1]) axs[1].set_xticklabels(x_label[::-1], rotation=60) axs[1].set_ylabel('Price') axs[1].grid() lines = line1 + line2 line_labels = [i.get_label() for i in lines] axs[1].legend(lines, line_labels, loc=0) plt.tight_layout() if show: plt.show() else: buffer = BytesIO() plt.savefig(buffer, format='png') return buffer.getvalue() def main(option_code, spot_code, strike_price, expiry_date, option_type, exercise_type): option_kline, spot_kline = get_kline(option_code, spot_code) op_k, sp_k = align_kline(option_kline, spot_kline) x, iv, option_cp, spot_cp = cal_historical_iv(op_k, sp_k, strike_price, expiry_date, 0.03, option_type, exercise_type) draw_picture(option_code, x, iv, option_cp, spot_cp) if __name__ == '__main__': # main('cu2003C51000', 'cu2003', 51000.0, '20200224', 'Call', 'european') # main('au2004P340', 'au2004', 340.0, '20200325', 'Put', 'european') # main('io2002C4050', '000300', 4050.0, '20200221', 'Call', 'european') # main('10002194', '510050', 3.1, '20200226', 'Call', 'european') # main('m2005C2800', 'm2005', 2800.0, '20200408', 'Call', 'american') main('m2005P2600', 'm2005', 2600.0, '20200408', 'Put', 'american') # main('ta2005P4800', 'ta2005', 4800.0, '20200403', 'Put', 'american') ================================================ FILE: historical_volatility.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ import math from io import BytesIO import numpy as np import matplotlib.pyplot as plt from sina_stock_kline_api import get_stock_day_kline, get_ex_data from sina_future_kline_api import get_future_day_kline ETF_SPOT_MAP = { 'sh510050': 'sh000016', 'sh510300': 'sh000300', 'sz159919': 'sh000300', } def cal_stock_fluctuation(code, kline, ex): x, y = [], [] kline_data = kline[code] for index, i in enumerate(kline_data): if index > 0: y.append(math.log(i['close'] / kline_data[index - 1]['close'])) x.append(int(''.join(i['date'][:10].split('-')))) if code in ETF_SPOT_MAP: listed_date = int(''.join(kline_data[0]['date'][:10].split('-'))) ex_date = [int(''.join(i['djr'][:10].split('-'))) for i in ex[code] if i['djr']] ex_date = [i for i in ex_date if i > listed_date][::-1] ex_result = {} if ex_date: spot_kline = kline[ETF_SPOT_MAP[code]] last_date, last_close = 0, 0.0 for index, i in enumerate(spot_kline): this_date = int(''.join(i['date'][:10].split('-'))) if index > 0: if last_date <= ex_date[0] < this_date: ex_result[this_date] = math.log(i['close'] / last_close) ex_date = ex_date[1:] if not ex_date: break last_date = this_date last_close = i['close'] # print(ex_result) for index, i in enumerate(x): if i in ex_result: # print(i, y[index], ex_result[i]) y[index] = ex_result[i] return x, y def get_stock_data(code): kline = {code: get_stock_day_kline(code)} if code in ETF_SPOT_MAP: kline[ETF_SPOT_MAP[code]] = get_stock_day_kline(ETF_SPOT_MAP[code]) ex = {code: get_ex_data(code)} else: ex = {code: []} return cal_stock_fluctuation(code, kline, ex) def cal_future_fluctuation(kline): x, y = [], [] if kline: last_close = float(kline[0]['c']) for k in kline[1:]: x.append(int(''.join(k['d'].split('-')))) close = float(k['c']) y.append(math.log(close / last_close)) last_close = close return x, y def get_future_data(code): return cal_future_fluctuation(get_future_day_kline(code)) def cal_historical_volatility(y, window_size): y2 = y[::-1] hv_lines, hv_cone = [], [] factor = np.sqrt(252) * 100.0 for w in window_size: hv = [np.std(y2[i: i + w]) * factor for i in range(len(y2) - w + 1)] hv_lines.append(hv) # hv_cone.append((max(hv), np.percentile(hv, 75), np.median(hv), np.percentile(hv, 25), min(hv), hv[0])) return hv_lines, hv_cone def draw_picture(code, x, y, interval, window_size, show=True): hv_lines, hv_cone = cal_historical_volatility(y, window_size) x_int = list(range(len(x))) len_window = len(window_size) fig, axs = plt.subplots(2, len_window, sharey=True, gridspec_kw={'hspace': 0, 'wspace': 0}, figsize=(13, 6.4)) ylim = None for i in range(len_window): axs[0, i].hist(hv_lines[i], orientation='horizontal', bins=30, alpha=0.6, color='Orange') axs[0, i].axhline(hv_lines[i][0], color='r') axs[0, i].set_title(str(window_size[i])) if ylim is None: ylim = axs[0, i].get_ylim() else: axs[0, i].set_ylim(ylim) axs[0, i].get_xaxis().set_visible(False) axs[1, i].axis('off') axs[0, 0].set_ylabel('historical volatility(%)') axs2 = fig.subplots(2, 1, gridspec_kw={'hspace': 0, 'wspace': 0}) axs2[0].axis('off') for hv in hv_lines: x_hv = x_int[-len(hv):] axs2[1].plot(x_hv, hv[::-1]) x_hv = x_int[-len(hv_lines[0]):] axs2[1].set_xlim((min(x_hv), max(x_hv))) axs2[1].legend([str(i) for i in window_size]) xticks = x[-len(hv_lines[0]):][::-interval][::-1] xticks_index = x_hv[::-interval][::-1] axs2[1].set_xticks(xticks_index) axs2[1].set_xticklabels([str(i) for i in xticks], rotation=60) axs2[1].set_ylabel('historical volatility(%)') axs2[1].set_xlabel(f'historical volatility of {code} in different window size') plt.tight_layout() if show: plt.show() else: buffer = BytesIO() plt.savefig(buffer, format='png') return buffer.getvalue() def main(code, security_type='stock', window_size=(5, 15, 30, 50, 70, 90, 120, 150)): # import pickle, os # if os.path.isfile('cache'): # with open('cache', 'rb') as fp: # x = pickle.load(fp) # y = pickle.load(fp) # hv_lines = pickle.load(fp) # hv_cone = pickle.load(fp) # else: # x, y = get_stock_data(code) # hv_lines, hv_cone = cal_historical_volatility(y, window_size) # with open('cache', 'wb') as fp: # pickle.dump(x, fp) # pickle.dump(y, fp) # pickle.dump(hv_lines, fp) # pickle.dump(hv_cone, fp) if security_type == 'stock': x, y = get_stock_data(code) elif security_type == 'future': x, y = get_future_data(code) else: return interval = math.ceil(len(x) / 20) draw_picture(code, x, y, interval, window_size, show=True) if __name__ == '__main__': # main('sz159919') # main('sh000300') main('m2005', security_type='future', window_size=(5, 15, 30, 50, 90, 120)) ================================================ FILE: readme.md ================================================ ## ETF期权隐含波动率曲面和希腊字母 用新浪财经的ETF期权数据(目前支持50ETF期权和上交所300ETF期权) 画出的隐含波动率曲面和希腊字母,能随着行情变化更新。 ![图1](https://github.com/sfl666/50ETF_option/blob/master/iv.png) ## 标的历史波动率 支持ETF期权、指数期权和商品期权。 ![图2](https://github.com/sfl666/50ETF_option/blob/master/hv.png) ## 期权历史的隐含波动率 不支持深交所300ETF期权和部分不活跃的商品期权。 ![图3](https://github.com/sfl666/50ETF_option/blob/master/hiv.png) ## 期权最近交易日1分钟线级别的隐含波动率 只支持上交所期权。 ![图4](https://github.com/sfl666/50ETF_option/blob/master/min1_iv.png) ================================================ FILE: sina_commodity_option_api.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ import json import requests http_header = { 'User-Agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/97.0.4692.71 Safari/537.36", 'Referer': "https://stock.finance.sina.com.cn/", } PIN_ZHONG_PARAMS = { 'io': {'product': 'io', 'exchange': 'cffex'}, 'm': {'product': 'm_o', 'exchange': 'dce'}, 'c': {'product': 'c_o', 'exchange': 'dce'}, 'i': {'product': 'i_o', 'exchange': 'dce'}, 'cf': {'product': 'cf', 'exchange': 'czce'}, 'sr': {'product': 'sr', 'exchange': 'czce'}, 'ta': {'product': 'ta', 'exchange': 'czce'}, 'ma': {'product': 'ma', 'exchange': 'czce'}, 'ru': {'product': 'ru_o', 'exchange': 'shfe'}, 'cu': {'product': 'cu_o', 'exchange': 'shfe'}, 'au': {'product': 'au_o', 'exchange': 'shfe'}, 'rm': {'product': 'rm', 'exchange': 'czce'}, } URL_T_QUOTATION = "http://stock.finance.sina.com.cn/futures/api/openapi.php/OptionService.getOptionData?" \ "type=futures&product={product}&exchange={exchange}&pinzhong={code}" URL_KLINE = "https://stock.finance.sina.com.cn/futures/api/jsonp.php//" \ "FutureOptionAllService.getOptionDayline?symbol={code}" URL_PRICE = "https://hq.sinajs.cn/etag.php?list=P_OP_{code}" URL_UNDERLYING_PRICE = "http://hq.sinajs.cn/list={code}" URL_UNDERLYING_PRICE2 = "http://hq.sinajs.cn/list=nf_{code}" URL_000300 = "http://hq.sinajs.cn/list=sh000300" def get_t_quotation(code): """获取T型报价数据""" p = ''.join(filter(str.isalpha, code)) data = requests.get(URL_T_QUOTATION.format(code=code, **PIN_ZHONG_PARAMS[p])).json()['result']['data'] up = data['up'] if 'up' in data else [] down = data['down'] if 'down' in data else [] for i in down: s = [] for j in i[-1][::-1]: if j.isdigit(): s.append(j) else: break strike_price = ''.join(s[::-1]) i.insert(-1, strike_price) return up, down def get_option_kline(code): """获取日K线数据""" return json.loads(requests.get(URL_KLINE.format(code=code)).content.split(b'(')[1].split(b')')[0]) def get_option_price(code): """获取实时行情数据""" data = requests.get(URL_PRICE.format(code=code), headers=http_header).content.split(b'"')[1].decode().split(',') return data def get_underlying_price(code): """获取标的(期货)实时行情""" return requests.get(URL_UNDERLYING_PRICE.format(code=code), headers=http_header).content.split(b'"')[1].decode('gbk').split(',') def get_underlying_price2(code): """获取标的(期货)实时行情""" return requests.get(URL_UNDERLYING_PRICE2.format(code=code), headers=http_header).content.split(b'"')[1].decode('gbk').split(',') def get_000300_price(): """获取指数000300实时行情""" return requests.get(URL_000300, headers=http_header).content.split(b'"')[1].decode('gbk').split(',') def my_test(): header = ['买量', '买价', '最新价', '卖价', '卖量', '持仓量', '涨跌(%)', '行权价', '代码'] up, down = get_t_quotation('io2002') for i in up + down: print(list(zip(header, i))) day_kline = get_option_kline('m2005C2400') print() for i in day_kline: print('日期:{d}, 开:{o}, 高:{h}, 低:{l}, 收:{c}, 成交:{v}'.format(**i)) header2 = ['买量', '买价', '最新价', '卖价', '卖量', '持仓量', '涨幅', '行权价', '昨收价', '开盘价', '涨停价', '跌停价', '申卖价五', '申卖量五', '申卖价四', '申卖量四', '申卖价三', '申卖量三', '申卖价二', '申卖量二', '申卖价一', '申卖量一', '申买价一', '申买量一', '申买价二', '申买量二', '申买价三', '申买量三', '申买价四', '申买量四', '申买价五', '申买量五', '行情时间', '主力合约标识', '状态码', '标的证券类型', '标的股票', '期权合约简称', '振幅', '最高价', '最低价', '成交量', '成交额'] price_data = get_option_price('io2002C4000') print() for i in zip(header2, price_data): print(i) header3 = ['期货名称', '现在交易时间', '开盘价', '最高价', '最低价', '(昨?)收盘价', '竞买价', '竞卖价', '最新价', '动态结算价', '昨日结算价', '买量', '卖量', '持仓量', '成交量', '交易所', '品种', '日期', '是否热门', '5天最高', '5天最低', '10天最高', '10天最低', '20天最高', '20天最低', '55天最高', '55天最低', '加权平均'] future_data = get_underlying_price('M2005') print() for i in zip(header3, future_data): print(i) header4 = ['股票名字', '今日开盘价', '昨日收盘价', '当前价格', '今日最高价', '今日最低价', '竞买价', '竞卖价', '成交的股票数', '成交金额', '买一量', '买一价', '买二量', '买二价', '买三量', '买三价', '买四量', '买四价', '买五量', '买五价', '卖一量', '卖一价', '卖二量', '卖二价', '卖三量', '卖三价', '卖四量', '卖四价', '卖五量', '卖五价', '日期', '时间'] price_000300 = get_000300_price() print() for i in zip(header4, price_000300): print(i) if __name__ == '__main__': my_test() # print(get_option_price('m2005C2400')) ================================================ FILE: sina_commodity_option_spider.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ import requests URL = "https://stock.finance.sina.com.cn/futures/view/optionsDP.php/{product}/{exchange}" def get_instruments(product, exchange): data = requests.get(URL.format(product=product, exchange=exchange)).content data1 = data[data.find(b'option_suffix'):] data2 = data1[:data1.find(b'')].split(b'') instruments = sorted(set(i[i.rfind(b'>') + 1:].decode().lower() for i in data2[:-1]), key=lambda x: int(x[-4:])) return instruments if __name__ == '__main__': print(get_instruments('ma', 'czce')) ================================================ FILE: sina_etf_option_api.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ from json import loads from requests import get http_header = { 'User-Agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/97.0.4692.71 Safari/537.36", 'Referer': "https://stock.finance.sina.com.cn/", } def get_option_dates(cate='50ETF', exchange='null'): url = f"http://stock.finance.sina.com.cn/futures/api/openapi.php/StockOptionService.getStockName?" \ f"exchange={exchange}&cate={cate}" dates = get(url).json()['result']['data']['contractMonth'] return [''.join(i.split('-')) for i in dates][1:] def get_option_expire_day(date, cate='50ETF', exchange='null'): url = "http://stock.finance.sina.com.cn/futures/api/openapi.php/StockOptionService.getRemainderDay?" \ "exchange={exchange}&cate={cate}&date={year}-{month}" url2 = url.format(year=date[:4], month=date[4:], cate=cate, exchange=exchange) data = get(url2).json()['result']['data'] if int(data['remainderDays']) < 0: url2 = url.format(year=date[:4], month=date[4:], cate='XD' + cate, exchange=exchange) data = get(url2).json()['result']['data'] return data['expireDay'], int(data['remainderDays']) def get_option_codes(date, underlying='510050'): url_up = ''.join(["http://hq.sinajs.cn/list=OP_UP_", underlying, str(date)[-4:]]) url_down = ''.join(["http://hq.sinajs.cn/list=OP_DOWN_", underlying, str(date)[-4:]]) data_up = str(get(url_up, headers=http_header).content).replace('"', ',').split(',') codes_up = [i[7:] for i in data_up if i.startswith('CON_OP_')] data_down = str(get(url_down, headers=http_header).content).replace('"', ',').split(',') codes_down = [i[7:] for i in data_down if i.startswith('CON_OP_')] return codes_up, codes_down def get_option_price(code): url = "http://hq.sinajs.cn/list=CON_OP_{code}".format(code=code) data = get(url, headers=http_header).content.decode('gbk') data = data[data.find('"') + 1: data.rfind('"')].split(',') fields = ['买量', '买价', '最新价', '卖价', '卖量', '持仓量', '涨幅', '行权价', '昨收价', '开盘价', '涨停价', '跌停价', '申卖价五', '申卖量五', '申卖价四', '申卖量四', '申卖价三', '申卖量三', '申卖价二', '申卖量二', '申卖价一', '申卖量一', '申买价一', '申买量一 ', '申买价二', '申买量二', '申买价三', '申买量三', '申买价四', '申买量四', '申买价五', '申买量五', '行情时间', '主力合约标识', '状态码', '标的证券类型', '标的股票', '期权合约简称', '振幅', '最高价', '最低价', '成交量', '成交额', '分红调整标志', '昨结算价', '认购认沽标志', '到期日', '剩余天数', '虚实值标志', '内在价值', '时间价值'] result = list(zip(fields, data)) return result def get_underlying_security_price(code='sh510050'): url = "http://hq.sinajs.cn/list=" + code data = get(url, headers=http_header).content.decode('gbk') data = data[data.find('"') + 1: data.rfind('"')].split(',') fields = ['证券简称', '今日开盘价', '昨日收盘价', '最近成交价', '最高成交价', '最低成交价', '买入价', '卖出价', '成交数量', '成交金额', '买数量一', '买价位一', '买数量二', '买价位二', '买数量三', '买价位三', '买数量四', '买价位四', '买数量五', '买价位五', '卖数量一', '卖价位一', '卖数量二', '卖价位二', '卖数量三', '卖价位三', '卖数量四', '卖价位四', '卖数量五', '卖价位五', '行情日期', '行情时间', '停牌状态'] return list(zip(fields, data)) def get_option_greek_alphabet(code): url = "http://hq.sinajs.cn/list=CON_SO_{code}".format(code=code) data = get(url, headers=http_header).content.decode('gbk') data = data[data.find('"') + 1: data.rfind('"')].split(',') fields = ['期权合约简称', '成交量', 'Delta', 'Gamma', 'Theta', 'Vega', '隐含波动率', '最高价', '最低价', '交易代码', '行权价', '最新价', '理论价值'] return list(zip(fields, [data[0]] + data[4:])) def get_option_time_line(code): url = f"https://stock.finance.sina.com.cn/futures/api/openapi.php/StockOptionDaylineService.getOptionMinline?" \ f"symbol=CON_OP_{code}" data = get(url).json()['result']['data'] return data def get_option_day_kline(code): url = f"http://stock.finance.sina.com.cn/futures/api/jsonp_v2.php//StockOptionDaylineService.getSymbolInfo?" \ f"symbol=CON_OP_{code}" data = get(url).content data = loads(data[data.find(b'(') + 1: data.rfind(b')')]) return data def my_test(): dates = get_option_dates(cate='300ETF') print('期权合约月份:{}'.format(dates)) for date in dates: print('期权月份{}:到期日{} 剩余天数{}'.format(date, *get_option_expire_day(date, cate='300ETF'))) demo_code = '10002180' for date in dates: call_codes, put_codes = get_option_codes(date, underlying='510300') print('期权月份{} 看涨期权代码:{}\n期权月份{} 看跌期权代码:{}'.format(date, call_codes, date, put_codes)) demo_code = call_codes[0] for index, i in enumerate(get_option_price(demo_code)): print('期权' + demo_code, index, *i) for index, i in enumerate(get_underlying_security_price(code='sh510300')): print(index, *i) for index, i in enumerate(get_option_greek_alphabet(demo_code)): print('期权' + demo_code, index, *i) time_line = get_option_time_line(demo_code) for i in time_line[-10:]: print('时间:{i}, 价格:{p}, 成交:{v}, 持仓:{t}, 均价:{a}'.format(**i)) day_kline = get_option_day_kline(demo_code) for i in day_kline: print('日期:{d}, 开盘:{o}, 最高:{h}, 最低:{l}, 收盘:{c}, 成交:{v}'.format(**i)) if __name__ == '__main__': my_test() ================================================ FILE: sina_future_kline_api.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ import json import requests URL_KLINE = "https://stock2.finance.sina.com.cn/futures/api/jsonp.php//" \ "InnerFuturesNewService.getDailyKLine?symbol={code}" def get_future_day_kline(code): # 日期, 开, 高, 低, 收, 成交量, 持仓量 data = requests.get(URL_KLINE.format(code=code)).content kline = json.loads(data[data.find(b'(') + 1: data.rfind(b')')]) # for i in kline: # print(i) # print(type(kline[0]['o'])) return kline if __name__ == '__main__': get_future_day_kline('M2005') ================================================ FILE: sina_stock_kline_api.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ # 股票K线接口代码copy自AkShare项目https://github.com/jindaxiang/akshare import re import base64 import struct import requests import execjs http_header = { 'User-Agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/97.0.4692.71 Safari/537.36", 'Referer': "https://stock.finance.sina.com.cn/", } hk_js_decode = """ function d(t) { var e, i, n, r, a, o, s, l = (arguments, 864e5), u = 7657, c = [], h = [], d = ~(3 << 30), f = 1 << 30, p = [0, 3, 5, 6, 9, 10, 12, 15, 17, 18, 20, 23, 24, 27, 29, 30], m = Math, g = function () { var l, u; for (l = 0; 64 > l; l++) h[l] = m.pow(2, l), 26 > l && (c[l] = v(l + 65), c[l + 26] = v(l + 97), 10 > l && (c[l + 52] = v(l + 48))); for (c.push("+", "/"), c = c.join(""), i = t.split(""), n = i.length, l = 0; n > l; l++) i[l] = c.indexOf(i[l]); return r = {}, e = o = 0, a = {}, u = w([12, 6]), s = 63 ^ u[1], { _1479: T, _136: _, _200: S, _139: k, _197: _mi_run }["_" + u[0]] || function () { return [] } }, v = String.fromCharCode, b = function (t) { return t === {}._ }, N = function () { var t, e; for (t = y(), e = 1; ;) { if (!y()) return e * (2 * t - 1); e++ } }, y = function () { var t; return e >= n ? 0 : (t = i[e] & 1 << o, o++, o >= 6 && (o -= 6, e++), !!t) }, w = function (t, r, a) { var s, l, u, c, d; for (l = [], u = 0, r || (r = []), a || (a = []), s = 0; s < t.length; s++) if (c = t[s], u = 0, c) { if (e >= n) return l; if (t[s] <= 0) u = 0; else if (t[s] <= 30) { for (; d = 6 - o, d = c > d ? d : c, u |= (i[e] >> o & (1 << d) - 1) << t[s] - c, o += d, o >= 6 && (o -= 6, e++), c -= d, !(0 >= c);) ; r[s] && u >= h[t[s] - 1] && (u -= h[t[s]]) } else u = w([30, t[s] - 30], [0, r[s]]), a[s] || (u = u[0] + u[1] * h[30]); l[s] = u } else l[s] = 0; return l }, x = function (t) { var e, i, n; for (t > 1 && (e = 0), e = 0; t > e; e++) r.d++, n = r.d % 7, (3 == n || 4 == n) && (r.d += 5 - n); return i = new Date, i.setTime((u + r.d) * l), i }, S = function () { var t, i, a, o, l; if (s >= 1) return []; for (r.d = w([18], [1])[0] - 1, a = w([3, 3, 30, 6]), r.p = a[0], r.ld = a[1], r.cd = a[2], r.c = a[3], r.m = m.pow(10, r.p), r.pc = r.cd / r.m, i = [], t = 0; o = { d: 1 }, y() && (a = w([3])[0], 0 == a ? o.d = w([6])[0] : 1 == a ? (r.d = w([18])[0], o.d = 0) : o.d = a), l = { day: x(o.d) }, y() && (r.ld += N()), a = w([3 * r.ld], [1]), r.cd += a[0], l.close = r.cd / r.m, i.push(l), !(e >= n) && (e != n - 1 || 63 & (r.c ^ t + 1)); t++) ; return i[0].prevclose = r.pc, i }, _ = function () { var t, i, a, o, l, u, c, h, d, f, p; if (s > 2) return []; for (c = [], d = { v: "volume", p: "price", a: "avg_price" }, r.d = w([18], [1])[0] - 1, h = { day: x(1) }, a = w(1 > s ? [3, 3, 4, 1, 1, 1, 5] : [4, 4, 4, 1, 1, 1, 3]), t = 0; 7 > t; t++) r[["la", "lp", "lv", "tv", "rv", "zv", "pp"][t]] = a[t]; for (r.m = m.pow(10, r.pp), s >= 1 ? (a = w([3, 3]), r.c = a[0], a = a[1]) : (a = 5, r.c = 2), r.pc = w([6 * a])[0], h.pc = r.pc / r.m, r.cp = r.pc, r.da = 0, r.sa = r.sv = 0, t = 0; !(e >= n) && (e != n - 1 || 7 & (r.c ^ t)); t++) { for (l = {}, o = {}, f = r.tv ? y() : 1, i = 0; 3 > i; i++) if (p = ["v", "p", "a"][i], (f ? y() : 0) && (a = N(), r["l" + p] += a), u = "v" == p && r.rv ? y() : 1, a = w([3 * r["l" + p] + ("v" == p ? 7 * u : 0)], [!!i])[0] * (u ? 1 : 100), o[p] = a, "v" == p) { if (!(l[d[p]] = a) && (s > 1 || 241 > t) && (r.zv ? !y() : 1)) { o.p = 0; break } } else "a" == p && (r.da = (1 > s ? 0 : r.da) + o.a); r.sv += o.v, l[d.p] = (r.cp += o.p) / r.m, r.sa += o.v * r.cp, l[d.a] = b(o.a) ? t ? c[t - 1][d.a] : l[d.p] : r.sv ? ((m.floor((r.sa * (2e3 / r.m) + r.sv) / r.sv) >> 1) + r.da) / 1e3 : l[d.p] + r.da / 1e3, c.push(l) } return c[0].date = h.day, c[0].prevclose = h.pc, c }, T = function () { var t, e, i, n, a, o, l; if (s >= 1) return []; for (r.lv = 0, r.ld = 0, r.cd = 0, r.cv = [0, 0], r.p = w([6])[0], r.d = w([18], [1])[0] - 1, r.m = m.pow(10, r.p), a = w([3, 3]), r.md = a[0], r.mv = a[1], t = []; a = w([6]), a.length;) { if (i = { c: a[0] }, n = {}, i.d = 1, 32 & i.c) for (; ;) { if (a = w([6])[0], 63 == (16 | a)) { l = 16 & a ? "x" : "u", a = w([3, 3]), i[l + "_d"] = a[0] + r.md, i[l + "_v"] = a[1] + r.mv; break } if (32 & a) { o = 8 & a ? "d" : "v", l = 16 & a ? "x" : "u", i[l + "_" + o] = (7 & a) + r["m" + o]; break } if (o = 15 & a, 0 == o ? i.d = w([6])[0] : 1 == o ? (r.d = o = w([18])[0], i.d = 0) : i.d = o, !(16 & a)) break } n.date = x(i.d); for (o in { v: 0, d: 0 }) b(i["x_" + o]) || (r["l" + o] = i["x_" + o]), b(i["u_" + o]) && (i["u_" + o] = r["l" + o]); for (i.l_l = [i.u_d, i.u_d, i.u_d, i.u_d, i.u_v], l = p[15 & i.c], 1 & i.u_v && (l = 31 - l), 16 & i.c && (i.l_l[4] += 2), e = 0; 5 > e; e++) l & 1 << 4 - e && i.l_l[e]++, i.l_l[e] *= 3; i.d_v = w(i.l_l, [1, 0, 0, 1, 1], [0, 0, 0, 0, 1]), o = r.cd + i.d_v[0], n.open = o / r.m, n.high = (o + i.d_v[1]) / r.m, n.low = (o - i.d_v[2]) / r.m, n.close = (o + i.d_v[3]) / r.m, a = i.d_v[4], "number" == typeof a && (a = [a, a >= 0 ? 0 : -1]), r.cd = o + i.d_v[3], l = r.cv[0] + a[0], r.cv = [l & d, r.cv[1] + a[1] + !!((r.cv[0] & d) + (a[0] & d) & f)], n.volume = (r.cv[0] & f - 1) + r.cv[1] * f, t.push(n) } return t }, k = function () { var t, e, i, n; if (s > 1) return []; for (r.l = 0, n = -1, r.d = w([18])[0] - 1, i = w([18])[0]; r.d < i;) e = x(1), 0 >= n ? (y() && (r.l += N()), n = w([3 * r.l], [0])[0] + 1, t || (t = [e], n--)) : t.push(e), n--; return t }; return _mi_run = function () { var t, i, a, o; if (s >= 1) return []; for (r.f = w([6])[0], r.c = w([6])[0], a = [], r.dv = [], r.dl = [], t = 0; t < r.f; t++) r.dv[t] = 0, r.dl[t] = 0; for (t = 0; !(e >= n) && (e != n - 1 || 7 & (r.c ^ t)); t++) { for (o = [], i = 0; i < r.f; i++) y() && (r.dl[i] += N()), r.dv[i] += w([3 * r.dl[i]], [1])[0], o[i] = r.dv[i]; a.push(o) } return a } , g()() } """ zh_sina_a_stock_hist_url = "https://finance.sina.com.cn/realstock/company/{}/hisdata/klc_kl.js" js_code = execjs.compile(hk_js_decode) def get_stock_day_kline(code): res = requests.get(zh_sina_a_stock_hist_url.format(code)) dict_list = js_code.call('d', res.text.split("=")[1].split(";")[0].replace('"', "")) return dict_list URL_EX = "http://stock.finance.sina.com.cn/fundInfo/api/openapi.php/FundPageInfoService.tabfh?symbol={code}&format=json" def get_ex_data(code): tmp = code[2:] if code[:2] in ('sh', 'sz') else code data = requests.get(URL_EX.format(code=tmp)).json()['result']['data']['fhdata'] # print(data) return data def get_1minutes(t=(('9:30:00', '11:30:00'), ('13:00:00', '15:00:00'))): # t = [('9:30:00', '11:30:00'), ('13:00:00', '15:00:00')] res, result = [], [] for begin, end in t: b = begin.split(':') b = int(b[0]) * 60 + int(b[1]) e = end.split(':') e = int(e[0]) * 60 + int(e[1]) res.extend(list(range(b, e + 1))) for index, i in enumerate(res): hour, minute = divmod(i, 60) result.append(':'.join([str(hour).rjust(2, '0'), str(minute).rjust(2, '0'), '00'])) return result def get_stock_time_line(code): """股票分时线(1分钟线)""" minutes = get_1minutes() content = requests.get(f'http://hq.sinajs.cn/list=ml_{code}', headers=http_header).content.decode() patt = re.compile(r'\"(.*)\"') m = patt.search(content) start = m.start() + 1 end = m.end() - 1 result = [] n = 0 while start < end: tmp = (content[start:start+16]) start += 16 b = base64.b64decode(tmp) avg = struct.unpack(' option_time and index < len_option_line - 1: index += 1 # print('#########', len(option_line), len(spot_line), index) option_time = time_str_to_int(option_line[index]['i']) if spot_time == option_time: times.append(i[3]) spot_price.append(i[1] if i[1] > 0.00001 else math.nan) tmp_price = float(option_line[index]['p']) option_price.append(tmp_price if tmp_price > 0.00001 else math.nan) return times, option_price, spot_price def cal_iv(option_price, spot_price, k, t, option_type): iv_func = european_option.call_iv if option_type == 'Call' else european_option.put_iv return [iv_func(i, j, k, t) if (i > 0.00001 and j > 0.00001) else math.nan for i, j in zip(option_price, spot_price)] def draw_picture(times, option_price, spot_price, iv, option_code, show=True): interval = math.ceil(len(times) / 20.0) x = list(range(len(times))) fig, axs = plt.subplots(2, sharex=True, gridspec_kw={'hspace': 0}, figsize=(12.0, 5.7)) axs[0].plot(iv, color='r') axs[0].set_xlim((x[0], x[-1])) axs[0].set_ylabel('Implied Volatility') axs[0].set_title(option_code) axs[0].grid() line1 = axs[1].plot(option_price, 'blue', label='option') ax2 = axs[1].twinx() line2 = ax2.plot(spot_price, 'orange', label='spot') axs[1].set_xticks(x[::-interval][::-1]) axs[1].set_xticklabels(times[::-interval][::-1], rotation=60) axs[1].set_ylabel('Price') axs[1].grid() lines = line1 + line2 line_labels = [i.get_label() for i in lines] axs[1].legend(lines, line_labels, loc=0) plt.tight_layout() if show: plt.show() else: buffer = BytesIO() plt.savefig(buffer, format='png') return buffer.getvalue() def main(option_code, spot_code, k, t, option_type, show=True): if spot_code in SPOT_CODE_MAP: spot_code = SPOT_CODE_MAP[spot_code] else: return None times, option_price, spot_price = align_line(*get_data(option_code, spot_code)) if times: iv = cal_iv(option_price, spot_price, k, t, option_type) return draw_picture(times, option_price, spot_price, iv, option_code, show) else: return None if __name__ == '__main__': main('10002235', '510050', 3.0, 238.0 / 365.0, 'Call') ================================================ FILE: volatility_surface.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ # python3 from time import sleep from threading import Thread, Lock from requests import get, exceptions from numpy import polyfit, polyval, meshgrid, array, nan import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec from mpl_toolkits.mplot3d import Axes3D import sina_etf_option_api http_header = { 'User-Agent': "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/97.0.4692.71 Safari/537.36", 'Referer': "https://stock.finance.sina.com.cn/", } COLORS = ['blue', 'yellow', 'lime', 'red', 'purple', 'slategray', 'tomato', 'orange', 'darkred', 'aqua'] global_ax_lines_call = [{'ax': None, 'lines': []} for _ in range(5)] global_ax_lines_put = [{'ax': None, 'lines': []} for _ in range(5)] update_picture_lock = Lock() ELEV = 30 AZIM = 120 def requests_get(all_codes): url = "http://hq.sinajs.cn/list={codes}".format(codes=all_codes) while True: try: data = get(url, headers=http_header).content.decode('gbk').strip().split('\n') break except (exceptions.ConnectionError, exceptions.ConnectTimeout) as e: print('连接出错,10秒后重试') print(e) sleep(10) return [i.split(',') for i in data] def get_codes(cate, exchange, underlying, dividend): while True: try: dates = sorted(sina_etf_option_api.get_option_dates(cate=cate, exchange=exchange)) call, put = [], [] for date in dates: call_codes, put_codes = sina_etf_option_api.get_option_codes(date, underlying=underlying) call.append(['CON_SO_' + i for i in call_codes]) put.append(['CON_SO_' + i for i in put_codes]) all_codes = ','.join([','.join(i) for i in call] + [','.join(i) for i in put]) data = requests_get(all_codes) if dividend: codes_tmp = [i[0][11:26] for i in data] # 考虑分红 else: codes_tmp = [i[0][11:26] for i in data if not i[0].endswith('A')] # 不考虑分红 for i in range(len(call)): call[i] = [j for j in call[i] if j in codes_tmp] put[i] = [j for j in put[i] if j in codes_tmp] break except (exceptions.ConnectionError, exceptions.ConnectTimeout) as e: print('连接出错,10秒后重试') print(e) sleep(10) return call, put, ','.join(codes_tmp), dates def get_data(call, put, all_codes): implied_volatility, strike_price, vega, theta, gamma, delta = [], [], [], [], [], [] for line in requests_get(all_codes): implied_volatility.append(float(line[9])) vega.append(float(line[8])) strike_price.append(float(line[13])) theta.append(float(line[7])) gamma.append(float(line[6])) delta.append(float(line[5])) call_implied_volatility, call_strike_price, call_vega, call_theta, call_gamma, call_delta = [], [], [], [], [], [] put_implied_volatility, put_strike_price, put_vega, put_theta, put_gamma, put_delta = [], [], [], [], [], [] b = 0 for i in call: len_i = len(i) call_implied_volatility.append(implied_volatility[b:b + len_i]) call_strike_price.append(strike_price[b:b + len_i]) call_vega.append(vega[b:b + len_i]) call_theta.append(theta[b:b + len_i]) call_gamma.append(gamma[b:b + len_i]) call_delta.append(delta[b:b + len_i]) b += len_i for i in put: len_i = len(i) put_implied_volatility.append(implied_volatility[b:b + len_i]) put_strike_price.append(strike_price[b:b + len_i]) put_vega.append(vega[b:b + len_i]) put_theta.append(theta[b:b + len_i]) put_gamma.append(gamma[b:b + len_i]) put_delta.append(delta[b:b + len_i]) b += len_i return call_strike_price, [call_delta, call_gamma, call_theta, call_vega, call_implied_volatility], \ put_strike_price, [put_delta, put_gamma, put_theta, put_vega, put_implied_volatility] def knockout_small_value(x, y): length = len(x) new_x = [x[i] for i in range(length) if y[i] > 0.01] new_y = [i for i in y if i > 0.01] return new_x, new_y def fit(call_x, call_y, put_x, put_y): xx = set() for i in call_x: xx |= set(i) xx = sorted(xx) call_y2, put_y2 = [], [] for i in range(len(call_x)): if xx == call_x[i]: call_y2.append(call_y[i]) else: new_x, new_y = knockout_small_value(call_x[i], call_y[i]) tmp = polyval(polyfit(new_x, new_y, 2), xx) tmp[tmp < 0.0] = 0.0 tmp_y, index_y = [], 0 for index, j in enumerate(xx): if j in call_x[i]: tmp_y.append(call_y[i][index_y]) index_y += 1 else: tmp_y.append(tmp[index]) call_y2.append(tmp_y) if xx == put_x[i]: put_y2.append(put_y[i]) else: new_x, new_y = knockout_small_value(put_x[i], put_y[i]) tmp = polyval(polyfit(new_x, new_y, 2), xx) tmp[tmp < 0.0] = 0.0 tmp_y, index_y = [], 0 for index, j in enumerate(xx): if j in put_x[i]: tmp_y.append(put_y[i][index_y]) index_y += 1 else: tmp_y.append(tmp[index]) put_y2.append(tmp_y) return xx, call_y2, put_y2 def not_fit(call_x, call_y, put_x, put_y): xx = set() for i in call_x: xx |= set(i) xx = sorted(xx) call_y2, put_y2 = [], [] for i in range(len(call_x)): if xx == call_x[i]: call_y2.append(call_y[i]) else: tmp_y, index_y = [], 0 for index, j in enumerate(xx): if j in call_x[i]: tmp_y.append(call_y[i][index_y]) index_y += 1 else: tmp_y.append(nan) call_y2.append(tmp_y) if xx == put_x[i]: put_y2.append(put_y[i]) else: tmp_y, index_y = [], 0 for index, j in enumerate(xx): if j in put_x[i]: tmp_y.append(put_y[i][index_y]) index_y += 1 else: tmp_y.append(nan) put_y2.append(tmp_y) return xx, call_y2, put_y2 def update(call_codes, put_codes, all_codes, x, y, yy, surf_call, surf_put, ax_iv_sf_call, ax_iv_sf_put, is_fit): azim = AZIM while True: # sleep(5) # 每隔5秒刷新一次 sleep(10) with update_picture_lock: call_x, call_ys, put_x, put_ys = get_data(call_codes, put_codes, all_codes) if is_fit: xx, call_y2, put_y2 = fit(call_x, call_ys[-1], put_x, put_ys[-1]) else: xx, call_y2, put_y2 = not_fit(call_x, call_ys[-1], put_x, put_ys[-1]) surf_call.remove() azim += 15 if azim > 360: azim -= 360 ax_iv_sf_call.view_init(ELEV, azim) # surf_call = ax_iv_sf_call.plot_surface(x, y, array(call_y2), rstride=1, cstride=1, cmap='rainbow') surf_call = ax_iv_sf_call.plot_wireframe(x, y, array(call_y2), rstride=1, cstride=1) surf_put.remove() ax_iv_sf_put.view_init(ELEV, azim) # surf_put = ax_iv_sf_put.plot_surface(x, y, array(put_y2), rstride=1, cstride=1, cmap='rainbow') surf_put = ax_iv_sf_put.plot_wireframe(x, y, array(put_y2), rstride=1, cstride=1) for index in range(5): for i in yy: global_ax_lines_call[index]['ax'].lines.remove(global_ax_lines_call[index]['lines'][i]) global_ax_lines_put[index]['ax'].lines.remove(global_ax_lines_put[index]['lines'][i]) global_ax_lines_call[index]['lines'] = [] global_ax_lines_put[index]['lines'] = [] for index in range(5): for i in yy: global_ax_lines_call[index]['lines'].append(global_ax_lines_call[index]['ax'].plot(call_x[i], array(call_ys[index][i]), COLORS[i])[0]) global_ax_lines_put[index]['lines'].append(global_ax_lines_put[index]['ax'].plot(put_x[i], array(put_ys[index][i]), COLORS[i])[0]) plt.draw() def main(cate, exchange, underlying, dividend=True, is_fit=True): call_codes, put_codes, all_codes, dates = get_codes(cate, exchange, underlying, dividend) dates_label = ',,'.join(dates).split(',') call_x, call_ys, put_x, put_ys = get_data(call_codes, put_codes, all_codes) if is_fit: xx, call_y2, put_y2 = fit(call_x, call_ys[-1], put_x, put_ys[-1]) else: xx, call_y2, put_y2 = not_fit(call_x, call_ys[-1], put_x, put_ys[-1]) yy = list(range(len(call_y2))) x, y = meshgrid(xx, yy) fig = plt.figure(figsize=(12, 5.7)) fig.canvas.mpl_connect('button_press_event', lambda event: update_picture_lock.acquire()) fig.canvas.mpl_connect('button_release_event', lambda event: update_picture_lock.release()) gs = gridspec.GridSpec(3, 6, figure=fig) ylabels = ['Delta', 'Gamma', 'Theta', 'Vega', 'Implied Volatility'] call_gs = [gs[2:3, :1], gs[2:3, 1:2], gs[2:3, 2:3], gs[1:2, 2:3], gs[:1, 2:3]] put_gs = [gs[2:3, 3:4], gs[2:3, 4:5], gs[2:3, 5:6], gs[1:2, 5:6], gs[:1, 5:6]] # --------------------------------------------------------------------------------------------------- for index in range(5): call_ax = fig.add_subplot(call_gs[index]) for i in yy: line, = call_ax.plot(call_x[i], call_ys[index][i], COLORS[i]) global_ax_lines_call[index]['lines'].append(line) call_ax.set_xlabel('Strike Price') call_ax.set_ylabel(ylabels[index]) call_ax.legend(dates, fontsize='xx-small') global_ax_lines_call[index]['ax'] = call_ax put_ax = fig.add_subplot(put_gs[index]) for i in yy: line, = put_ax.plot(put_x[i], put_ys[index][i], COLORS[i]) global_ax_lines_put[index]['lines'].append(line) put_ax.set_xlabel('Strike Price') put_ax.set_ylabel(ylabels[index]) put_ax.legend(dates, fontsize='xx-small') global_ax_lines_put[index]['ax'] = put_ax ax_iv_sf_call = fig.add_subplot(gs[:2, :2], projection='3d') ax_iv_sf_call.view_init(ELEV, AZIM) # surf_call = ax_iv_sf_call.plot_surface(x, y, array(call_y2), rstride=1, cstride=1, cmap='rainbow') # print(x.shape, y.shape, array(call_y2).shape) surf_call = ax_iv_sf_call.plot_wireframe(x, y, array(call_y2), rstride=1, cstride=1, cmap='rainbow') ax_iv_sf_call.set_yticklabels(dates_label) ax_iv_sf_call.set_xlabel('Strike Price') ax_iv_sf_call.set_ylabel('Expiration Date') ax_iv_sf_call.set_zlabel('Implied Volatility') ax_iv_sf_call.set_title('Call Option') ax_iv_sf_put = fig.add_subplot(gs[:2, 3:5], projection='3d') ax_iv_sf_put.view_init(ELEV, AZIM) # surf_put = ax_iv_sf_put.plot_surface(x, y, array(put_y2), rstride=1, cstride=1, cmap='rainbow') surf_put = ax_iv_sf_put.plot_wireframe(x, y, array(put_y2), rstride=1, cstride=1, cmap='rainbow') ax_iv_sf_put.set_yticklabels(dates_label) ax_iv_sf_put.set_xlabel('Strike Price') ax_iv_sf_put.set_ylabel('Expiration Date') ax_iv_sf_put.set_zlabel('Implied Volatility') ax_iv_sf_put.set_title('Put Option') plt.tight_layout() thread = Thread(target=update, args=(call_codes, put_codes, all_codes, x, y, yy, surf_call, surf_put, ax_iv_sf_call, ax_iv_sf_put, is_fit)) thread.setDaemon(True) thread.start() plt.show() if __name__ == '__main__': category = '50ETF' underlying_security = '510050' # category = '300ETF' # underlying_security = '510300' main(cate=category, exchange='null', underlying=underlying_security, dividend=False, is_fit=True) ================================================ FILE: volatility_surface2.py ================================================ """ Author: shifulin Email: shifulin666@qq.com """ import time from threading import Thread, Lock from collections import namedtuple import numpy as np import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec from mpl_toolkits.mplot3d import Axes3D from sina_commodity_option_api import get_000300_price, get_t_quotation import european_option # EUROPEAN_OPTION = {'io', 'cu', 'au'} OptionInfo = namedtuple('OptionInfo', ['x', 'y', 'k', 't', 'type']) class VolSurface(object): def __init__(self, expiry_date_map): self.spot = 'io' self.expiry_date_map = expiry_date_map self.x = np.array([[[]]]) self.y = np.array([[[]]]) self.data = np.array([[[]]]) self.code_to_info = {} self.strike_prices = [] self.expiry_dates = [] self.x_index = [] self.spot_price = 0.0 self.update_picture_lock = Lock() self.elev = 30 self.azim = 120 self.colors = ['blue', 'yellow', 'lime', 'red', 'purple', 'slategray', 'tomato', 'orange', 'darkred', 'aqua'] self.init() self.lines = {} self.update_price(self.get_spot_price(), self.get_option_price()) @staticmethod def get_spot_price(): return float(get_000300_price()[3]) def get_option_price(self): result = [] for i in self.expiry_date_map: up, down = get_t_quotation(self.spot + i) result.extend(up) result.extend(down) return result def update_price(self, spot_price, option_price): self.spot_price = spot_price for i in option_price: price = (float(i[1]) + float(i[3])) / 2.0 x, y, k, t, option_type = self.code_to_info[i[-1]] if option_type == 'Call': index = 0 iv_func = european_option.call_iv else: index = 5 iv_func = european_option.put_iv self.data[x, y, index] = price iv = iv_func(price, spot_price, k, t) delta, gamma, theta, vega = european_option.greeks(spot_price, k, iv, 0.03, t, option_type) self.data[x, y, index] = delta self.data[x, y, index + 1] = gamma self.data[x, y, index + 2] = theta self.data[x, y, index + 3] = vega self.data[x, y, index + 4] = iv def init(self): strike_prices, expiry_dates = set(), set() data = self.get_option_price() for i in data: strike_prices.add(i[7]) expiry_dates.add(i[-1][2:6]) self.strike_prices = sorted(strike_prices, key=lambda i: float(i)) self.expiry_dates = sorted(expiry_dates, key=lambda i: int(i)) self.x_index = [float(i) for i in self.strike_prices] strike_prices = {j: i for i, j in enumerate(self.strike_prices)} expiry_dates = {j: i for i, j in enumerate(self.expiry_dates)} for i in data: x = strike_prices[i[7]] y = expiry_dates[i[-1][2:6]] k = float(i[7]) t = float(self.expiry_date_map[i[-1][2:6]]) / 365.0 option_type = 'Call' if 'C' in i[-1] else 'Put' self.code_to_info[i[-1]] = OptionInfo(x, y, k, t, option_type) self.x, self.y = np.meshgrid(list(range(len(expiry_dates))), [float(i) for i in strike_prices]) self.data = np.ones(self.x.shape + (10, )) for i in self.code_to_info.values(): self.data[i.x, i.y, :] = 0.0 self.data[self.data > 0.5] = np.nan def start_update_picture(self): fig = plt.figure(figsize=(12, 5.7)) fig.canvas.mpl_connect('button_press_event', lambda event: self.update_picture_lock.acquire()) fig.canvas.mpl_connect('button_release_event', lambda event: self.update_picture_lock.release()) gs = gridspec.GridSpec(3, 6, figure=fig) call_gs = [gs[2:3, :1], gs[2:3, 1:2], gs[2:3, 2:3], gs[1:2, 2:3], gs[:1, 2:3]] put_gs = [gs[2:3, 3:4], gs[2:3, 4:5], gs[2:3, 5:6], gs[1:2, 5:6], gs[:1, 5:6]] ylabels = ['Delta', 'Gamma', 'Theta', 'Vega', 'Implied Volatility'] m, n, _ = self.data.shape for index in range(5): call_ax = fig.add_subplot(call_gs[index]) tmp = [] for i in range(n): line, = call_ax.plot(self.x_index, self.data[:, i, index], self.colors[i]) tmp.append(line) self.lines[call_ax] = (index, tmp) call_ax.set_xlabel('Strike Price') call_ax.set_ylabel(ylabels[index]) call_ax.legend(self.expiry_dates, fontsize='xx-small') put_ax = fig.add_subplot(put_gs[index]) tmp = [] for i in range(n): line, = put_ax.plot(self.x_index, self.data[:, i, index + 5], self.colors[i]) tmp.append(line) self.lines[put_ax] = (index + 5, tmp) put_ax.set_xlabel('Strike Price') put_ax.set_ylabel(ylabels[index]) put_ax.legend(self.expiry_dates, fontsize='xx-small') ax_iv_sf_call = fig.add_subplot(gs[:2, :2], projection='3d') self.surf_call = ax_iv_sf_call.plot_wireframe(self.x, self.y, self.data[:, :, 4], rstride=1, cstride=1) ax_iv_sf_call.set_yticklabels(self.expiry_dates) ax_iv_sf_call.set_xlabel('Strike Price') ax_iv_sf_call.set_ylabel('Expiration Date') ax_iv_sf_call.set_zlabel('Implied Volatility') ax_iv_sf_call.set_title('Call Option') ax_iv_sf_call.view_init(self.elev, self.azim) ax_iv_sf_put = fig.add_subplot(gs[:2, 3:5], projection='3d') self.surf_put = ax_iv_sf_put.plot_wireframe(self.x, self.y, self.data[:, :, 9], rstride=1, cstride=1) ax_iv_sf_put.set_yticklabels(self.expiry_dates) ax_iv_sf_put.set_xlabel('Strike Price') ax_iv_sf_put.set_ylabel('Expiration Date') ax_iv_sf_put.set_zlabel('Implied Volatility') ax_iv_sf_put.set_title('Put Option') ax_iv_sf_put.view_init(self.elev, self.azim) def update_picture(): while True: with self.update_picture_lock: self.azim += 15 if self.azim > 360: self.azim -= 360 for k, v in self.lines.items(): index, lines = v [k.lines.remove(lines[i]) for i in range(n)] tmp = [] for i in range(n): line, = k.plot(self.x_index, self.data[:, i, index], self.colors[i]) tmp.append(line) self.lines[k] = (index, tmp) self.surf_call.remove() self.surf_call = ax_iv_sf_call.plot_wireframe(self.x, self.y, self.data[:, :, 4], rstride=1, cstride=1) ax_iv_sf_call.view_init(self.elev, self.azim) self.surf_put.remove() self.surf_put = ax_iv_sf_put.plot_wireframe(self.x, self.y, self.data[:, :, 9], rstride=1, cstride=1) ax_iv_sf_put.view_init(self.elev, self.azim) plt.draw() plt.tight_layout() time.sleep(5) self.update_price(self.get_spot_price(), self.get_option_price()) t = Thread(target=update_picture) t.setDaemon(True) t.start() plt.show() def draw_picture(self): pass def main(): expiry_date_map = { '2002': 33, '2003': 61, '2004': 89, '2006': 152, '2009': 243, '2012': 334, } vs = VolSurface(expiry_date_map) vs.start_update_picture() if __name__ == '__main__': main()