
When I coded the Fourier pricing technique for this blog, it was the first time I used such technique.
Speed wasn't that impressive but without any reference, it's difficult to judge.
Recalling from my digital signal processing course, we know that FFT stands for fast Fourier transform and it relies on symmetries to achieve its full speed for signals of length $2^N$.
I looked further and two things happened:
1/I mis translated matlab code
"x=x_min:dx:x_max"
into
"x=np.array([(dx*i+x_min) for i in range(N+1)]) "
and made a similar translation for the vector of frequency w. Price was converging it seemed but the length of the produced vector is odd.
2/numpy FFT behaved too nicely, instead of complaining about the vector size not being in the form $2^N$ it ran other optimized Fourier transforms. Such transforms however aren't nearly as fast as FFT.
To give an idea, with only 1024 steps, the previous version was 4 cents off, and PC parity off by less than 2 cents. When we keep in mind the slow convergence of the MC, we could accept such values. But the correct version is less than 0.05 cent from the theoretical price. Call put parity is perfect and my time measure says 0. Using 2^15 steps only takes 0.05 second and give an accuracy of 0.00005 cents. Find below the corrected code for the european option pricer and the one for the american pricer.
- def FourierST3(option,params):
- N=params.NAS
- [S0,K,sigma,T,r,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.0*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);
- v_call = np.maximum(s-K,0)
- v_put = np.maximum(K-s,0)
- # FST method
- char_exp_factor = np.exp((j*(r-0.5*sigma**2)*w - 0.5*sigma**2*(w**2)-r)*T)
- VC = np.real(np.fft.ifft(np.fft.fft(v_call)*char_exp_factor))
- VP = np.real(np.fft.ifft(np.fft.fft(v_put)*char_exp_factor))
- #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)
- def FourierST4(option,params):
- N=params.NAS
- NTS=params.NTS
- [S0,K,sigma,T,r,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)
- # FST method
- char_exp_factor = np.exp((j*(r-0.5*sigma**2)*w - 0.5*sigma**2*(w**2)-r)*T/NTS)
- for m in range(NTS):
- VC = np.real(np.fft.ifft(np.fft.fft(VC)*char_exp_factor))
- VC=earlyCall(VC,s,K)
- VP = np.real(np.fft.ifft(np.fft.fft(VP)*char_exp_factor))
- VP=earlyPut(VP,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)
I chose to keep the older code for reference but clearly the pricers FourierST3 and FourierST4 should ALWAYS be preferred to FourierST and FourierST2.
Aucun commentaire:
Enregistrer un commentaire