
We now look at the convergence of the Fourier based pricer for american and european options. As I expected, one can see on the following graph the sawtooth pattern that existed with the binomial tree. It is present for both american and european options. First the american option:
and the the european one:
As you can guess, I fixed that with an adaptive step (curves with AS employ this technique). The very small oscillations we see for the american options with Fourier AS can be removed if we test early exercise only after the dividend is paid instead of at each step. This would be a slight speed up as well. However if we introduce stochastic volatility or jumps, it might not be the case that a call should only be exercised early before a dividend is paid so I prefer to remain general.
- def FourierST6(option,params):
- N=params.NAS
- NTS=params.NTS
- [S0,K,sigma,T,r2,divi,american]=[option.S0,option.K,option.sigma,option.T,option.r,option.divi,option.american]
- j=complex(0,1)
- #create vector in the real space
- x_min = -7.5
- x_max = 7.5
- dx=(x_max-x_min)/(N-1)
- x=linspace(x_min,x_max,N)
- #create vector in the fourier space
- w_max=np.pi/dx;
- dw=2*w_max/(N);
- w=np.concatenate((linspace(0,w_max,N/2+1),linspace(-w_max+dw,-dw,N/2-1)))
- # Option payoff
- s = S0*np.exp(x);
- VC = np.maximum(s-K,0)
- VP = np.maximum(K-s,0)
- dt=float(T/NTS)
- dividends,dividendsStep=preparedivs(divi,float(T),dt)
- r,rArray,rateChangeStep,DFArray=preparerates(r2,T,dt,NTS)
- rr=rArray[-1][0]
- # FST method
- char_exp_factor = np.exp((j*(rr-0.5*sigma**2)*w - 0.5*sigma**2*(w**2)-rr)*dt)
- for k in range(NTS,0,-1):
- if (k in rateChangeStep[:]):
- rr= (r[1][np.nonzero(rateChangeStep==(k))[0]])
- char_exp_factor = np.exp((j*(rr-0.5*sigma**2)*w - 0.5*sigma**2*(w**2)-rr)*dt)
- #if there is a dividend, we interpolate the grid
- if (k in dividendsStep):
- if params.stepAdapt==True:
- #first get the fraction of dt
- fraction=dividends[0][np.nonzero(dividendsStep==(k))[0]]-k*dt
- dtf=dt-fraction
- #perform fraction of step
- char_exp_factorf = np.exp((j*(rr-0.5*sigma**2)*w - 0.5*sigma**2*(w**2)-rr)*dtf)
- VC = np.real(np.fft.ifft(np.fft.fft(VC)*char_exp_factorf))
- VP = np.real(np.fft.ifft(np.fft.fft(VP)*char_exp_factorf))
- #handle dividends
- div=dividends[1][np.nonzero(dividendsStep==(k))[0]]
- #a call is an always increasing function of the strike
- mid=int(N/2)
- delta=VC[1:mid]-VC[:mid-1]
- BoS=np.where(delta<0)[0][-1]
- VC[:BoS]=0
- #The put always decreasing
- delta=VP[1:mid]-VP[:mid-1]
- BoS=np.where(delta<0)[0][0]
- VP[:BoS]=VP[BoS]
- tckC=si.splrep(s,VC)
- tckP=si.splrep(s,VP)
- #option prices are interpolated on the grid
- VC=si.splev(np.maximum(s-div,np.exp(x_min)) ,tckC,der=0)
- VP=si.splev(np.maximum(s-div,np.exp(x_min)) ,tckP,der=0)
- if american==True:
- VC=earlyCall(VC,s,K)
- VP=earlyPut(VP,s,K)
- #finish the step
- dtf=fraction
- char_exp_factorf = np.exp((j*(rr-0.5*sigma**2)*w - 0.5*sigma**2*(w**2)-rr)*dtf)
- VC = np.real(np.fft.ifft(np.fft.fft(VC)*char_exp_factorf))
- VP = np.real(np.fft.ifft(np.fft.fft(VP)*char_exp_factorf))
- else:
- VC = np.real(np.fft.ifft(np.fft.fft(VC)*char_exp_factor))
- VP = np.real(np.fft.ifft(np.fft.fft(VP)*char_exp_factor))
- div=dividends[1][np.nonzero(dividendsStep==(k))[0]]
- #a call is an always increasing function of the strike
- mid=int(N/2)
- delta=VC[1:mid]-VC[:mid-1]
- BoS=np.where(delta<0)[0][-1]
- VC[:BoS]=0
- #The put always decreasing
- delta=VP[1:mid]-VP[:mid-1]
- BoS=np.where(delta<0)[0][0]
- VP[:BoS]=VP[BoS]
- tckC=si.splrep(s,VC)
- tckP=si.splrep(s,VP)
- #option prices are interpolated on the grid
- VC=si.splev(np.maximum(s-div,np.exp(x_min)) ,tckC,der=0)
- VP=si.splev(np.maximum(s-div,np.exp(x_min)) ,tckP,der=0)
- if american==True:
- VC=earlyCall(VC,s,K)
- VP=earlyPut(VP,s,K)
- else:
- #no dividend at this step
- VC = np.real(np.fft.ifft(np.fft.fft(VC)*char_exp_factor))
- VP = np.real(np.fft.ifft(np.fft.fft(VP)*char_exp_factor))
- if american==True:
- VP=earlyPut(VP,s,K)
- VC=earlyCall(VC,s,K)
- #Interpolate option prices
- tck=si.splrep(s,VC)
- option.call.price=si.splev(S0,tck,der=0)
- tck=si.splrep(s,VP)
- option.put.price=si.splev(S0,tck,der=0)
Compared to the adaptive stepping for the tree where we try to make the step match a dividend, here we cut the step where the dividend falls into two sub steps of appropriate size to make sure that the dividend payment falls right where it should. The good thing is that we can do that for an arbitrary number of dividends.
Assume now we have two late (2.8 and 2.9 years for a 3 year option) and large (5€ each) dividends. Compare the AS Fourier and AS tree on the following picture.
We still have the little waves but there is no comparison with the tree. I don't reproduce figures but similar sawtooth pattern can be seen with the puts (much smaller though) and AS improves the situation here as well.
One should note that I also included the time structure of rates as well. We could do some adaptive stepping for the rates as well but I suppose the error coming from here is very small in our case.
Aucun commentaire:
Enregistrer un commentaire