[PYTHON] Effectuer des calculs de volatilité implicite à grande vitesse (traitement des données de marché)

Pour la volatilité implicite et l'équation des Black Shoals, Black–Scholes model - Wikipedia

À propos de la lecture des données que vous utilisez De la lecture de Pandas d'informations telles que le prix théorique moyen de l'option Nikkei à la mise en forme \ -vol de nuit

vérification

Que fais tu

--Calcul de la volatilité implicite à partir du prix de l'option --Comparaison de la vitesse de calcul (plus précisément, comparaison du temps d'exécution)

[Volatilité \ -Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9C%E3%83%A9%E3%83%86%E3%82%A3%E3%83%AA% E3% 83% 86% E3% 82% A3 # .E3.82.A4.E3.83.B3.E3.83.97.E3.83.A9.E3.82.A4.E3.83.89.E3.83.BB .E3.83.9C.E3.83.A9.E3.83.86.E3.82.A3.E3.83.AA.E3.83.86.E3.82.A3)

Equations pour $ \ sigma $ (notez que $ C (K, T) $ est une fonction de $ \ sigma $) Prix de marché $ = C (K, T) $ Le $ \ sigma $ obtenu en résolvant> est appelé volatilité implicite.

Comme vous pouvez le voir, C (K, T) est calculé pour trouver σ, qui est le prix du marché, mais comme C (K, T) ne peut pas être résolu simplement, [Algorithme d'enracinement](https: //ja.wikipedia) .org / wiki /% E6% B1% 82% E6% A0% B9% E3% 82% A2% E3% 83% AB% E3% 82% B4% E3% 83% AA% E3% 82% BA% E3% Utilisez 83% A0) etc. pour trouver la volatilité implicite.

L'une d'elles est la Méthode Bisection \ -Wikipedia.

Condition de calcul

Élément d'action

Résultat du temps d'exécution

Celui implémenté en C ++ était extrêmement plus rapide que les diverses ingéniosités de Python, et je pense que c'était l'effort de Cython. De plus, C ++ a encore une certaine capacité de réserve comme la parallélisation des threads, cette fois ...!

Dans le cas de Windwos, le multi-processus de Python va lire la bibliothèque Keras à chaque fois, ce qui est mauvais, mais même la version exécutée individuellement sans cela a pris environ 35 secondes.

La fonction inverse utilisant ANN est rapide mais imprécise. Je pense qu'il y a des limites à la conception de NN. (Peut-être que c'est impossible à moins de faire quelque chose comme WGAN / StackGAN)

article Data Handling Optimize BS_Vectorized Proc Time[sec]
Calcul d'optimisation avec Scipy(Solution numérique) / pandas.apply - Single Process Pd.apply minimize_scalar(Scipy) No Single 2.8061
Calcul d'optimisation avec Scipy(Solution numérique) /optimisation de la fonction scalaire ndarray boucle x:Vectorisation des fonctions- Single Process Pd->np minimize_scalar(Scipy) Yes Single 3.2068
Calcul d'optimisation avec Scipy(Solution numérique) /optimisation de la fonction vectorielle ndarray:Vectorisation des fonctions- Single Process Pd->np root(Scipy) Yes Single 0.6706
optimisation ndarray Cython/optimisation de la fonction vectorielle ndarray- Single Process Pd->np(CyPy) root(Scipy) Yes Single 0.6860
optimisation ndarray Cython/vecteur d'optimisation de la fonction scalaire ndarray- Single Process Pd->np(CyPy) VectBisection(Cython) Yes Single 0.4848
Calcul d'optimisation avec Scipy(Solution numérique) / pandas.apply + Multi Process Pd->Split(Pdx8)->np(CyPy) VectBisection(Cython) Yes MultiProc 128.3856
Apprentissage de la fonction inverse à l'aide de ANN ⇒ Utilisation comme moteur de conférence(Keras) Pd->np->ANN N/A Yes Single 0.1526
Utiliser Swig(C++la mise en oeuvre)/Boucle simple- Single Process Pd->np->C++(Swig) Bisection(C++) No Single 0.0010

La source

# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np

from keras.models import load_model


from datetime import datetime
import dateutil

import scipy.optimize
from scipy.stats import norm

#Version Cython
from BS_Cy import *
#Version Swig
from _BS import *

import multiprocessing as mp
import time
import matplotlib.pyplot as plt

maturity = 19.75/245.

def strip(text):
    try:
        return text.strip()
    except AttributeError:
        return text


def BS_norm(F225, Strike, maturity, vol, call):
    volSQM = vol * (maturity ** .5)
    d1 = np.log(F225/Strike) / volSQM + volSQM/2
    d2 = d1 - volSQM

    return (F225   * norm.cdf(d1) - Strike*norm.cdf(d2)) if call else \
            (Strike * norm.cdf(-d2) - F225*norm.cdf(-d1))

def BS_norm_vect(F225, Strike, maturity, vol, call):
    volSQM = vol * (maturity ** .5)
    d1 = np.log(F225/Strike) / volSQM + volSQM/2
    d2 = d1 - volSQM

    Call = F225   * norm.cdf(d1) - Strike*norm.cdf(d2)
    Put  = Strike * norm.cdf(-d2) - F225*norm.cdf(-d1)
    premium = Call * call + Put * np.logical_not(call)
    return premium
    
def myapply(x):
    res = scipy.optimize.minimize_scalar(
            lambda vol:
            (
                BS_norm(x['F225_PRICE'], x['STRIKE'], maturity, vol, x['CALL'])
                                - x['OP_PRICE']
            )**2, 
            method='Bounded', bounds =(0.01, 0.5),
            options={'maxiter': 50, 'xatol': 1e-04})
    x['vol1'] = res.x
    return x
   
def myapply_vect(F225, Strike, price, call):
    x = []
    for i in range(len(F225)):
        res = scipy.optimize.minimize_scalar(
            lambda vol:
            ( BS_norm_vect(F225[i], Strike[i], maturity, vol,call[i]) - price[i] )**2, 
            method='Bounded', bounds =(0.01, 0.5),
            options={'maxiter': 50, 'xatol': 1e-04})
        x.append(res.x)
    return x

def myapply_vect2(F225, Strike, price, call):
    res = scipy.optimize.root(
        lambda vol:( BS_norm_vect(F225, Strike, maturity, vol,call) - price), 
        np.ones_like(F225)*.3)
    return res.x

def pworker(df):
    df['vol6'] = cy_apply2(df['F225_PRICE'].values, df['STRIKE'].values, 
                              df['OP_PRICE'].values,   df['CALL'].values)
    return df

if __name__ == '__main__':
    # #Préparation des données
    # [Informations telles que le prix théorique de l'option\|Japan Exchange Group](http://www.jpx.co.jp/markets/derivatives/option-price/01.html)Données de clôture du 10 février 2017 obtenues à partir de.
    #Il existe différents éléments, mais l'actif sous-jacent pour le contrat de mars 2017: Seules les données de transaction d'options du Nikkei 225 sont utilisées.
    # 
    # ##Lire des données dans Pandas
    # 1.Élevez et nommez vous-même l'en-tête en fonction des informations d'en-tête distribuées séparément du corps de données
    # 2.Couper les blancs dans les données de texte lors du chargement

    #Nom de l'en-tête(Par nom de variable)
    #Code produit,Type de produit,Mois du contrat,Prix de l'exercice,réserve
    #Option de vente:Code de stock,le dernier prix,réserve,Prix théorique,Volatilité
    #Options d'appel:Code de stock,le dernier prix,réserve,Prix théorique,Volatilité
    #Cours de clôture de l'actif sous-jacent,Critères de volatilité
    colName = ("CODE","TYPE","MATURITY","STRIKE", "RSV", 
               "PUT_CODE", "PUT_PRICE", "PUT_RSV", "PUT_TPRICE", "PUT_VOLATILITY",
               "CALL_CODE","CALL_PRICE","CALL_RSV","CALL_TPRICE","CALL_VOLATILITY",
               "F225_PRICE", "Base_VOL")

    df = pd.read_csv('./ose20170210tp.csv',names=colName,
                     converters = {'CODE' : strip,
                                   'TYPE' : strip})
    #Seule l'option Nikkei 225 du contrat de mars 2017 est extraite. Au fait, j'ai supprimé les colonnes inutiles et c'était rafraîchissant.
    df = df.query("MATURITY == 201703 & CODE==\"NK225E\"")    .drop(['RSV','PUT_RSV','CALL_RSV','PUT_CODE','CALL_CODE','CODE','TYPE','MATURITY'], 1)

    # *Puisque PUT et CALL sont séparés, normalisez les données.
    # *Lorsque la colonne CALL est TRUE, il s'agit de données CALL et lorsqu'elle est FALSE, il s'agit de données PUT.
    # *Le prix d'exercice est également réduit à 14000 yens ou plus et moins de 22000 yens
    df_p = df[["STRIKE","PUT_PRICE","PUT_TPRICE", "PUT_VOLATILITY","F225_PRICE", "Base_VOL"]]    .rename(columns={'PUT_PRICE': 'OP_PRICE', 'PUT_TPRICE':'OP_TPRICE', 'PUT_VOLATILITY':'OP_VOL'})
    df_p['CALL'] = False
    df_c = df[["STRIKE","CALL_PRICE","CALL_TPRICE", "CALL_VOLATILITY","F225_PRICE", "Base_VOL"]]    .rename(columns={'CALL_PRICE': 'OP_PRICE', 'CALL_TPRICE':'OP_TPRICE', 'CALL_VOLATILITY':'OP_VOL'})
    df_c['CALL'] = True
    df = df_p.append(df_c).query("OP_PRICE > 1.0 & STRIKE < 22000 & STRIKE >= 14000")
    del (df_p,df_c)

    tmp_df = df
    loop_num = 10
    text = 'Time elapsed: %.2f seconds'

    result_time = []
    result_Col  = np.array([["Data Handling","Optimize","BS_Vectorized","Proc","Time[sec]"]])
    result_con  = np.array([["Pd.apply",
                   "Pd->np",
                   "Pd->np",
                   "Pd->np(CyPy)",
                   "Pd->np(CyPy)",
                   "Pd->Split(Pd x 8)->np(CyPy)",
                   "Pd->np->ANN",
                   "Pd->np->C++(Swig)"
                   ]])
    result_opt  = np.array([["minimize_scalar(Scipy)",
                   "minimize_scalar(Scipy)",
                   "root(Scipy)",
                   "root(Scipy)",
                   "Vect Bisection(Cython)",
                   "Vect Bisection(Cython)",
                   "N/A",
                   "Bisection(C++)"
                    ]])
    result_Vect = np.array([["No",
                   "Yes",
                   "Yes",
                   "Yes",
                   "Yes",
                   "Yes",
                   "Yes",
                   "No"
                    ]])
    result_Proc = np.array([["Single",
                   "Single",
                   "Single",
                   "Single",
                   "Single",
                   "Multi Proc",
                   "Single",
                   "Single"
                    ]])
    
    # 1.Calcul d'optimisation avec Scipy(Solution numérique) / pandas.apply - Single Process
    time_start = time.time()
    for i in range(loop_num):
        tmp_df = df.apply(myapply, axis=1)
    result_time.append((time.time() - time_start))


    # 2.Calcul d'optimisation avec Scipy(Solution numérique) /boucle ndarray:Vectorisation des fonctions- Single Process
    time_start = time.time()
    for i in range(loop_num):
        tmp_df['vol2'] = myapply_vect(df['F225_PRICE'].values, df['STRIKE'].values, 
                              df['OP_PRICE'].values,   df['CALL'].values)
    result_time.append((time.time() - time_start))

    # 3.Calcul d'optimisation avec Scipy(Solution numérique) /vecteur ndarray:Vectorisation des fonctions- Single Process
    time_start = time.time()
    for i in range(loop_num):
        tmp_df['vol3'] = myapply_vect2(df['F225_PRICE'].values, df['STRIKE'].values, 
                              df['OP_PRICE'].values,   df['CALL'].values)
    result_time.append((time.time() - time_start))
    
    # 4. Cython - Root
    time_start = time.time()
    for i in range(loop_num):
        tmp_df['vol4'] = cy_apply1(df['F225_PRICE'].values, df['STRIKE'].values, 
                              df['OP_PRICE'].values,   df['CALL'].values)
    result_time.append((time.time() - time_start))

    # 5. Cython - My Bisection
    time_start = time.time()
    for i in range(loop_num):
        tmp_df['vol5'] = cy_apply2(df['F225_PRICE'].values, df['STRIKE'].values, 
                              df['OP_PRICE'].values,   df['CALL'].values)
    result_time.append((time.time() - time_start))

    # 6. Multi Process
    time_start = time.time()
    for i in range(loop_num):
        p = mp.Pool(processes=8)
        split_dfs = np.array_split(df,8)
        pool_results = p.map(pworker, split_dfs)
        p.close()
        p.join()

        tmp_df = pd.concat(pool_results, axis=0)
    result_time.append((time.time() - time_start))

    # 7. ANN
    model = load_model('./model.h5')
    
    a = np.array([df['STRIKE'].values/df['F225_PRICE'].values])
    b = np.array([np.ones_like(df['F225_PRICE'].values)*maturity/(40./245.)])
    c = np.array([(df['OP_PRICE'].values/df['F225_PRICE'].values/0.25)**0.25])
    
    X = np.vstack((a,b,c)).transpose()

    time_start = time.time()
    for i in range(loop_num):
        tmp_df['vol7'] = 0.4*model.predict(X)+0.1
    result_time.append((time.time() - time_start))    
        
    # 8. Swig C++
    tmpmpmp=np.ones_like(df['F225_PRICE'].values).astype(np.float32)
    time_start = time.time()
    for i in range(loop_num):
        tmpmpmp = Swig_Apply_PY(df['F225_PRICE'].values, df['STRIKE'].values, 
                              df['OP_PRICE'].values,  df['CALL'].values.astype(dtype=np.int32),tmpmpmp.shape[0])
        tmp_df['vol8'] =  tmpmpmp
    result_time.append((time.time() - time_start))

    result_time = np.array([result_time])
    print(np.vstack((result_con, result_opt, result_Vect, result_Proc,result_time)).transpose())

Recommended Posts

Effectuer des calculs de volatilité implicite à grande vitesse (traitement des données de marché)
Effectuez une conversion demi-largeur / pleine largeur à grande vitesse avec Python
[Big Query] Chargez une partie des données BQ dans les pandas à grande vitesse
Combinez plusieurs fichiers python en un seul fichier python
[Python & Unix] Combinez plusieurs fichiers PDF en un seul.
Combinez plusieurs fichiers Excel chargés à l'aide de pandas en un seul
Effectuer des calculs de volatilité implicite à grande vitesse (traitement des données de marché)
[Big Query] Chargez une partie des données BQ dans les pandas à grande vitesse
Une petite histoire qui produit des données de table au format CSV à grande vitesse