Full Code of sfl666/option_tools for AI

master 092221184457 cached
15 files
74.8 KB
24.4k tokens
89 symbols
1 requests
Download .txt
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'</ul>')].split(b'</li>')
    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('<L', bytes(b[:4]))[0] / 1000.0
        price = struct.unpack('<L', bytes(b[4:8]))[0] / 1000.0
        amount = struct.unpack('<L', bytes(b[8:12]))[0]
        if price < 1000000.0:
            result.append((avg, price, amount, minutes[n]))
            n += 1
    return result


def my_test():
    # kline = get_stock_day_kline('sh510050')
    # for i in kline:
    #     print(i)
    for i in get_stock_time_line('sz000009'):
        print(i)


if __name__ == '__main__':
    my_test()
    # get_ex_data('sh510050')


================================================
FILE: time_line_iv.py
================================================
"""
Author: shifulin
Email: shifulin666@qq.com
"""
import math
from io import BytesIO
import matplotlib.pyplot as plt
from sina_etf_option_api import get_option_time_line as get_etf_option_time_line
from sina_stock_kline_api import get_stock_time_line, get_1minutes
import european_option as european_option


SPOT_CODE_MAP = {
    '510050': 'sh510050',
    '510300': 'sh510300',
}


def get_data(option_code, spot_code):
    option_line = get_etf_option_time_line(option_code)
    spot_line = get_stock_time_line(spot_code)
    return option_line, spot_line


def time_str_to_int(time_str):
    return int(''.join(time_str.split(':')[:2]))


def align_line(option_line, spot_line):
    times, option_price, spot_price = [], [], []
    if not option_line or not spot_line:
        return times, option_price, spot_price
    index = 0
    len_option_line = len(option_line)
    for i in spot_line:
        spot_time = time_str_to_int(i[3])
        option_time = time_str_to_int(option_line[index]['i'])
        while spot_time > 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()
Download .txt
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
Download .txt
SYMBOL INDEX (89 symbols across 13 files)

FILE: american_option.py
  function _call_price (line 9) | def _call_price(s, k, sigma, r, t, steps=100):
  function _put_price (line 33) | def _put_price(s, k, sigma, r, t, steps=100):
  function call_price (line 56) | def call_price(s, k, sigma, r, t, steps=100):
  function put_price (line 60) | def put_price(s, k, sigma, r, t, steps=100):
  function delta (line 64) | def delta(s, k, sigma, r, t, option_type, steps=100):
  function gamma (line 78) | def gamma(s, k, sigma, r, t, option_type, steps=100):
  function theta (line 87) | def theta(s, k, sigma, r, t, option_type, steps=100):
  function vega (line 98) | def vega(s, k, sigma, r, t, option_type, steps=100):
  function rho (line 107) | def rho(s, k, sigma, r, t, option_type, steps=100):
  function call_iv (line 113) | def call_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=3.0, e=0.00001...
  function put_iv (line 135) | def put_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=3.0, e=0.00001,...
  function my_test (line 156) | def my_test():
  function my_test2 (line 168) | def my_test2():

FILE: baw.py
  function bsm_call (line 10) | def bsm_call(s, k, sigma, t, r, q):
  function bsm_put (line 17) | def bsm_put(s, k, sigma, t, r, q):
  function find_sx (line 24) | def find_sx(sx, k, sigma, t, r, q, option_type):
  function baw_call (line 41) | def baw_call(s, k, sigma, t, r, q=0.0):
  function baw_put (line 52) | def baw_put(s, k, sigma, t, r, q=0.0):
  function call_iv (line 63) | def call_iv(c, s, k, t, r=0.03, sigma_min=0.0001, sigma_max=3.0, e=0.000...
  function put_iv (line 84) | def put_iv(c, s, k, t, r=0.03, sigma_min=0.0001, sigma_max=3.0, e=0.00001):
  function delta (line 105) | def delta(s, k, sigma, t, r, option_type):
  function gamma (line 119) | def gamma(s, k, sigma, t, r, option_type):
  function theta (line 128) | def theta(s, k, sigma, t, r, option_type):
  function vega (line 139) | def vega(s, k, sigma, t, r, option_type):
  function rho (line 148) | def rho(s, k, sigma, t, r, option_type):

FILE: european_option.py
  function greeks (line 21) | def greeks(s, k, sigma, r, t, option_type):
  function bs_call (line 78) | def bs_call(s, k, sigma, r, t):
  function bs_put (line 85) | def bs_put(s, k, sigma, r, t):
  function call_iv (line 92) | def call_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.00001):
  function put_iv (line 114) | def put_iv(c, s, k, t, r=0.03, sigma_min=0.01, sigma_max=1.0, e=0.00001):

FILE: historical_implied_volatility.py
  function days_interval (line 33) | def days_interval(date1, date2):
  function get_kline (line 40) | def get_kline(option_code, spot_code):
  function align_kline (line 52) | def align_kline(option_kline, spot_kline):
  function cal_historical_iv (line 86) | def cal_historical_iv(option_kline, spot_kline, strike_price, expiry_dat...
  function draw_picture (line 101) | def draw_picture(option_code, x, iv, option_cp, spot_cp, show=True):
  function main (line 131) | def main(option_code, spot_code, strike_price, expiry_date, option_type,...

FILE: historical_volatility.py
  function cal_stock_fluctuation (line 20) | def cal_stock_fluctuation(code, kline, ex):
  function get_stock_data (line 53) | def get_stock_data(code):
  function cal_future_fluctuation (line 63) | def cal_future_fluctuation(kline):
  function get_future_data (line 75) | def get_future_data(code):
  function cal_historical_volatility (line 79) | def cal_historical_volatility(y, window_size):
  function draw_picture (line 90) | def draw_picture(code, x, y, interval, window_size, show=True):
  function main (line 130) | def main(code, security_type='stock', window_size=(5, 15, 30, 50, 70, 90...

FILE: sina_commodity_option_api.py
  function get_t_quotation (line 40) | def get_t_quotation(code):
  function get_option_kline (line 58) | def get_option_kline(code):
  function get_option_price (line 63) | def get_option_price(code):
  function get_underlying_price (line 69) | def get_underlying_price(code):
  function get_underlying_price2 (line 74) | def get_underlying_price2(code):
  function get_000300_price (line 79) | def get_000300_price():
  function my_test (line 84) | def my_test():

FILE: sina_commodity_option_spider.py
  function get_instruments (line 11) | def get_instruments(product, exchange):

FILE: sina_etf_option_api.py
  function get_option_dates (line 15) | def get_option_dates(cate='50ETF', exchange='null'):
  function get_option_expire_day (line 22) | def get_option_expire_day(date, cate='50ETF', exchange='null'):
  function get_option_codes (line 33) | def get_option_codes(date, underlying='510050'):
  function get_option_price (line 43) | def get_option_price(code):
  function get_underlying_security_price (line 57) | def get_underlying_security_price(code='sh510050'):
  function get_option_greek_alphabet (line 69) | def get_option_greek_alphabet(code):
  function get_option_time_line (line 78) | def get_option_time_line(code):
  function get_option_day_kline (line 85) | def get_option_day_kline(code):
  function my_test (line 93) | def my_test():

FILE: sina_future_kline_api.py
  function get_future_day_kline (line 13) | def get_future_day_kline(code):

FILE: sina_stock_kline_api.py
  function get_stock_day_kline (line 314) | def get_stock_day_kline(code):
  function get_ex_data (line 323) | def get_ex_data(code):
  function get_1minutes (line 330) | def get_1minutes(t=(('9:30:00', '11:30:00'), ('13:00:00', '15:00:00'))):
  function get_stock_time_line (line 345) | def get_stock_time_line(code):
  function my_test (line 368) | def my_test():

FILE: time_line_iv.py
  function get_data (line 19) | def get_data(option_code, spot_code):
  function time_str_to_int (line 25) | def time_str_to_int(time_str):
  function align_line (line 29) | def align_line(option_line, spot_line):
  function cal_iv (line 50) | def cal_iv(option_price, spot_price, k, t, option_type):
  function draw_picture (line 55) | def draw_picture(times, option_price, spot_price, iv, option_code, show=...
  function main (line 83) | def main(option_code, spot_code, k, t, option_type, show=True):

FILE: volatility_surface.py
  function requests_get (line 33) | def requests_get(all_codes):
  function get_codes (line 46) | def get_codes(cate, exchange, underlying, dividend):
  function get_data (line 72) | def get_data(call, put, all_codes):
  function knockout_small_value (line 106) | def knockout_small_value(x, y):
  function fit (line 113) | def fit(call_x, call_y, put_x, put_y):
  function not_fit (line 151) | def not_fit(call_x, call_y, put_x, put_y):
  function update (line 183) | def update(call_codes, put_codes, all_codes, x, y, yy, surf_call, surf_p...
  function main (line 218) | def main(cate, exchange, underlying, dividend=True, is_fit=True):

FILE: volatility_surface2.py
  class VolSurface (line 20) | class VolSurface(object):
    method __init__ (line 21) | def __init__(self, expiry_date_map):
    method get_spot_price (line 41) | def get_spot_price():
    method get_option_price (line 44) | def get_option_price(self):
    method update_price (line 52) | def update_price(self, spot_price, option_price):
    method init (line 72) | def init(self):
    method start_update_picture (line 96) | def start_update_picture(self):
    method draw_picture (line 170) | def draw_picture(self):
  function main (line 174) | def main():
Condensed preview — 15 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (82K chars).
[
  {
    "path": ".gitignore",
    "chars": 1221,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": "american_option.py",
    "chars": 5850,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nfrom math import sqrt, exp, inf\nimport numpy as np\n\n\ndef _call_price("
  },
  {
    "path": "baw.py",
    "chars": 5616,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nfrom math import log, sqrt, exp, inf\nfrom scipy.stats import norm\nimp"
  },
  {
    "path": "european_option.py",
    "chars": 4788,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nfrom math import log, sqrt, exp\nimport numpy as np\nfrom scipy.stats i"
  },
  {
    "path": "historical_implied_volatility.py",
    "chars": 5439,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nimport math\nimport datetime\nfrom io import BytesIO\nfrom copy import d"
  },
  {
    "path": "historical_volatility.py",
    "chars": 5566,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nimport math\nfrom io import BytesIO\nimport numpy as np\nimport matplotl"
  },
  {
    "path": "readme.md",
    "chars": 464,
    "preview": "## ETF期权隐含波动率曲面和希腊字母\n用新浪财经的ETF期权数据(目前支持50ETF期权和上交所300ETF期权)\n画出的隐含波动率曲面和希腊字母,能随着行情变化更新。\n\n![图1](https://github.com/sfl666/"
  },
  {
    "path": "sina_commodity_option_api.py",
    "chars": 4654,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nimport json\nimport requests\n\n\nhttp_header = {\n    'User-Agent': \"Mozi"
  },
  {
    "path": "sina_commodity_option_spider.py",
    "chars": 596,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nimport requests\n\n\nURL = \"https://stock.finance.sina.com.cn/futures/vi"
  },
  {
    "path": "sina_etf_option_api.py",
    "chars": 5279,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nfrom json import loads\nfrom requests import get\n\nhttp_header = {\n    "
  },
  {
    "path": "sina_future_kline_api.py",
    "chars": 570,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nimport json\nimport requests\n\n\nURL_KLINE = \"https://stock2.finance.sin"
  },
  {
    "path": "sina_stock_kline_api.py",
    "chars": 13369,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\n# 股票K线接口代码copy自AkShare项目https://github.com/jindaxiang/akshare\nimport "
  },
  {
    "path": "time_line_iv.py",
    "chars": 3366,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nimport math\nfrom io import BytesIO\nimport matplotlib.pyplot as plt\nfr"
  },
  {
    "path": "volatility_surface.py",
    "chars": 12047,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\n# python3\n\nfrom time import sleep\nfrom threading import Thread, Lock\n"
  },
  {
    "path": "volatility_surface2.py",
    "chars": 7783,
    "preview": "\"\"\"\nAuthor: shifulin\nEmail: shifulin666@qq.com\n\"\"\"\nimport time\nfrom threading import Thread, Lock\nfrom collections impor"
  }
]

About this extraction

This page contains the full source code of the sfl666/option_tools GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 15 files (74.8 KB), approximately 24.4k tokens, and a symbol index with 89 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!