This is the documentation for fftoptionlib [https://github.com/arraystream/fftoptionlib]
Assume the price of the asset has the following representation: $$ S_t = S_0 \exp{[(r-q+w)t+X_t]} $$ where $X_t$ is a stochastic process and r,q,w are interest rate, dividend rate and martingale correction item
We can get the log price $$ \log(S_t)=\log(S_0)+(r-q+w)t+X_t $$
If the charateristic function of $X_t$ is known, we can derive the characteristic function of $\log(S_t)$
We provide the following characteristic functions for $\log(S_t)$:
If you want to add more, just write the characteristic function for $X_t$, use general_ln_st_chf function can produce the corresponding characteristic function for $\log(S_t)$
Each Process can be priced by the following three methods
In [1]:
import numpy as np
import simpleplotly as spt
from fftoptionlib import *
import plotly
import plotly.offline as py
%matplotlib inline
plotly.offline.init_notebook_mode() # run at the start of every notebook
In [2]:
from scipy.stats import norm
def phi(x):
return np.exp(-x*x/2.)/np.sqrt(2*pi)
def BlackScholesCore(CallPutFlag,DF,F,X,T,v):
vsqrt=v*np.sqrt(T)
d1 = (np.log(F/X)+(vsqrt*vsqrt/2.))/vsqrt
d2 = d1-vsqrt
if CallPutFlag:
return DF*(F*norm.cdf(d1)-X*norm.cdf(d2))
else:
return DF*(X*norm.cdf(-d2)-F*norm.cdf(-d1))
def BlackScholesAnalytical(CallPutFlag,S,X,T,r,d,v):
return BlackScholesCore(CallPutFlag,np.exp(-r*T),np.exp((r-d)*T)*S,X,T,v)
In [3]:
underlying_price = 100
maturity_date = '2016-03-17'
risk_free_rate = 0.05
dividend_rate = 0.01
evaluation_date = '2016-01-17'
vanilla_option = BasicOption()
(vanilla_option.set_underlying_close_price(underlying_price)
.set_dividend(dividend_rate)
.set_maturity_date(maturity_date)
.set_evaluation_date(evaluation_date)
.set_zero_rate(risk_free_rate))
ft_pricer = FourierPricer(vanilla_option)
In [4]:
import fftoptionlib
In [5]:
fftoptionlib.BasicOption
Out[5]:
In [6]:
import pandas as pd
In [7]:
N=2**15
d_u = 0.01
alpha = 1
sigma = 0.3
(ft_pricer.set_log_st_process(BlackScholes(sigma))
.set_pricing_engine(FFTEngine(N, d_u, alpha, spline_order=2)))
Out[7]:
In [8]:
strike_arr = np.arange(50,120,5)
put_call=np.array(['call']*len(strike_arr))
theory_value = BlackScholesAnalytical(True,underlying_price,strike_arr,60/365,risk_free_rate,dividend_rate,sigma)
In [9]:
%time
ffn_value = ft_pricer.calc_price(strike_arr,put_call)
In [10]:
fig=spt.FigureBuilder()
fig.add(spt.Scatter(x=strike_arr,y=theory_value,name='Analytical BS'))
fig.add(spt.Scatter(x=strike_arr,y=ffn_value,name='FFT BS'))
fig.add(spt.XAxis('strike'))
fig.add(spt.YAxis('call price'))
fig.update_layout(title='Call Option Prices').plot()
In [11]:
N=2**12
d_u = 0.01
d_k = 0.01
alpha = 1
ft_pricer.set_pricing_engine(FractionFFTEngine(N, d_u, d_k,alpha, spline_order=2))
Out[11]:
In [12]:
%time
ffn_value = ft_pricer.calc_price(strike_arr,put_call)
In [13]:
fig=spt.FigureBuilder()
fig.add(spt.Scatter(x=strike_arr,y=theory_value,name='Analytical BS'))
fig.add(spt.Scatter(x=strike_arr,y=ffn_value,name='FractionalFFT BS'))
fig.add(spt.XAxis('strike'))
fig.add(spt.YAxis('call price'))
fig.update_layout(title='Call Option Prices').plot()
In [14]:
N=1000
L=10
ft_pricer.set_pricing_engine(CosineEngine(N,L))
Out[14]:
In [15]:
%time
ffn_value = ft_pricer.calc_price(strike_arr,put_call)
In [16]:
fig=spt.FigureBuilder()
fig.add(spt.Scatter(x=strike_arr,y=theory_value,name='Analytical BS'))
fig.add(spt.Scatter(x=strike_arr,y=ffn_value,name='Cosine BS'))
fig.add(spt.XAxis('strike'))
fig.add(spt.YAxis('call price'))
fig.update_layout(title='Call Option Prices').plot()
In [17]:
ft_pricer.set_log_st_process(Heston(
V0=0.04,
theta=0.04,
k=2,
sigma=0.3,
rho=-0.7))
Out[17]:
In [18]:
ft_pricer.set_pricing_engine(FFTEngine(N=2**15, d_u=0.01, alpha=1, spline_order=2))
%time
fft_price = ft_pricer.calc_price(strike_arr,put_call)
In [19]:
ft_pricer.set_pricing_engine(FractionFFTEngine(N=2**15, d_u=0.01, d_k=0.01,alpha=1, spline_order=2))
%time
fraction_price = ft_pricer.calc_price(strike_arr,put_call)
In [20]:
ft_pricer.set_pricing_engine(CosineEngine(N=2000,L=10))
%time
cos_price = ft_pricer.calc_price(strike_arr,put_call)
In [21]:
fig=spt.FigureBuilder()
fig.add(spt.Scatter(x=strike_arr,y=theory_value,name='Analytical BS'))
fig.add(spt.Scatter(x=strike_arr,y=fft_price,name='FFT Heston'))
fig.add(spt.Scatter(x=strike_arr,y=fraction_price,name='FractionalFFT Hestion'))
fig.add(spt.Scatter(x=strike_arr,y=cos_price,name='Cosine Hestion'))
fig.add(spt.XAxis('strike'))
fig.add(spt.YAxis('call price'))
fig.update_layout(title='Call Option Prices').plot()
In [22]:
ft_pricer.set_log_st_process(VarianceGamma(
theta=-0.14,
v=0.2,
sigma=0.3))
Out[22]:
In [23]:
ft_pricer.set_pricing_engine(FFTEngine(N=2**15, d_u=0.01, alpha=1, spline_order=2))
%time
fft_price = ft_pricer.calc_price(strike_arr,put_call)
In [24]:
ft_pricer.set_pricing_engine(FractionFFTEngine(N=2**15, d_u=0.01, d_k=0.01,alpha=1, spline_order=2))
%time
fraction_price = ft_pricer.calc_price(strike_arr,put_call)
In [25]:
ft_pricer.set_pricing_engine(CosineEngine(N=2000,L=10))
%time
cos_price = ft_pricer.calc_price(strike_arr,put_call)
In [26]:
fig=spt.FigureBuilder()
fig.add(spt.Scatter(x=strike_arr,y=theory_value,name='Analytical BS'))
fig.add(spt.Scatter(x=strike_arr,y=fft_price,name='FFT Heston'))
fig.add(spt.Scatter(x=strike_arr,y=fraction_price,name='FractionalFFT Hestion'))
fig.add(spt.Scatter(x=strike_arr,y=cos_price,name='Cosine Hestion'))
fig.add(spt.XAxis('strike'))
fig.add(spt.YAxis('call price'))
fig.update_layout(title='Call Option Prices').plot()
The above example explore BlackShole, Heston, VaranceGamma with three Engines, FFT, Fractional FFT and Cosine. You can change to other processes easily by calling set_log_st_process.