
And the last class (to date) is the Fourier pricer. Like the MC class, I only have to computing methods, one for solo and one for duo. For european options without dividends, the pricer only compute in one step.
If we price european options with cash dividends, we need to provide time steps.
The value provided for the number of asset steps is a power of 2 so 10 gives 2^10=1024 asset steps.
- import scipy.interpolate as si
- from OptionPricer import *
- from scipy import *
- class FourierPricer(OptionPricer):
- _NumberAssetStep = 0
- _StepAdapt = False
- _Depth =1
- @staticmethod
- def FourierDuo(pricingParameters, optionContract, nts, N, stepAdapt,american):
- #parameters
- S0 = pricingParameters._S0
- r2 = pricingParameters._r2
- T = pricingParameters._T
- sigma = optionContract._Sigma
- divi = pricingParameters._div
- NTS = nts
- dt = float(T)/NTS
- dividends,dividendsStep = PricingParameters.preparedivs(divi,T,dt)
- r,rArray,rateChangeStep,DFArray = PricingParameters.preparerates(r2,T,dt,NTS)
- 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 = optionContract.expiC(s)
- VP = optionContract.expiP(s)
- # FST method
- if (american or pricingParameters._pvdividends>0):
- rr = rArray[-1][0]
- 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 adjust the path
- if (k in dividendsStep):
- if stepAdapt:
- #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:
- VC = optionContract.earlyC(VC,s)
- VP = optionContract.earlyP(VP,s)
- #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:
- VC = optionContract.earlyC(VC,s)
- VP = optionContract.earlyP(VP,s)
- 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:
- VC = optionContract.earlyC(VC,s)
- VP = optionContract.earlyP(VP,s)
- else:
- char_exp_factor = np.exp((j*(pricingParameters._r-0.5*sigma**2)*w - 0.5*sigma**2*(w**2)-pricingParameters._r)*T)
- 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))
- #Interpolate option prices
- tck = si.splrep(s,VC)
- optionContract.call.price = si.splev(S0,tck,der = 0)
- tck = si.splrep(s,VP)
- optionContract.put.price = si.splev(S0,tck,der = 0)
- @staticmethod
- def FourierSolo(pricingParameters, optionContract, nts, N, stepAdapt,american,option,payoff,early):
- #parameters
- S0 = pricingParameters._S0
- r2 = pricingParameters._r2
- T = pricingParameters._T
- sigma = optionContract._Sigma
- divi = pricingParameters._div
- NTS = nts
- dt = float(T)/NTS
- dividends,dividendsStep = PricingParameters.preparedivs(divi,T,dt)
- r,rArray,rateChangeStep,DFArray = PricingParameters.preparerates(r2,T,dt,NTS)
- 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 = payoff(s)
- # FST method
- if (american or pricingParameters._pvdividends>0):
- rr = rArray[-1][0]
- 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 adjust the path
- if (k in dividendsStep):
- if stepAdapt:
- #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))
- #handle dividends
- div = dividends[1][np.nonzero(dividendsStep == (k))[0]]
- if isinstance(optionContract,OptionContractCall):
- #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
- if isinstance(optionContract,OptionContractPut):
- #The put always decreasing
- mid = int(N/2)
- delta = VC[1:mid]-VC[:mid-1]
- BoS = np.where(delta<0)[0][0]
- VC[:BoS] = VC[BoS]
- tckC = si.splrep(s,VC)
- #option prices are interpolated on the grid
- VC = si.splev(np.maximum(s-div,np.exp(x_min)) ,tckC,der = 0)
- if american:
- VC = earlyC(VC,s)
- #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))
- else:
- VC = np.real(np.fft.ifft(np.fft.fft(VC)*char_exp_factor))
- div = dividends[1][np.nonzero(dividendsStep == (k))[0]]
- if isinstance(optionContract,OptionContractCall):
- #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
- if isinstance(optionContract,OptionContractPut):
- #The put always decreasing
- mid = int(N/2)
- delta = VC[1:mid]-VC[:mid-1]
- BoS = np.where(delta<0)[0][0]
- VC[:BoS] = VC[BoS]
- tckC = si.splrep(s,VC)
- #option prices are interpolated on the grid
- VC = si.splev(np.maximum(s-div,np.exp(x_min)) ,tckC,der = 0)
- if american:
- VC = early(VC,s)
- else:
- #no dividend at this step
- VC = np.real(np.fft.ifft(np.fft.fft(VC)*char_exp_factor))
- if american:
- VC = early(VC,s)
- else:
- char_exp_factor = np.exp((j*(pricingParameters._r-0.5*sigma**2)*w - 0.5*sigma**2*(w**2)-pricingParameters._r)*T)
- VC = np.real(np.fft.ifft(np.fft.fft(VC)*char_exp_factor))
- #Interpolate option prices
- tck = si.splrep(s,VC)
- option.price = si.splev(S0,tck,der = 0)
- def __init__(self):
- self.SetPricerId("FourierPricer")
- super(FourierPricer, self).__init__()
- def SetPricerParameters(self, NTS, depth = 1, StepAdapt = False):
- self._StepAdapt = StepAdapt
- self._Depth = depth
- self._NumberAssetSteps = 2**depth
- self._NumberSteps=NTS
- def PrintSelf(self):
- print self._pricerId
- print "Number of time steps: ", self._NumberSteps
- print "Number of asset steps: ", self._NumberAssetSteps
- print "StepAdapt on: ", self._StepAdapt
- self._PricingParameters.printSelf()
- for optionContract in self._contractList:
- optionContract.printSelf()
- def CalculateContract(self, optionContract):
- # Check input parameters
- if isinstance(optionContract, OptionContractUS):
- if isinstance(optionContract, OptionContractDuo):
- FourierPricer.FourierDuo(self._PricingParameters, optionContract, self._NumberSteps, self._NumberAssetSteps, self._StepAdapt, True)
- elif isinstance(optionContract, OptionContractCall):
- FourierPricer.FourierSolo(self._PricingParameters, optionContract, self._NumberSteps, self._NumberAssetSteps, self._StepAdapt, True, optionContract.call, optionContract.expiC, optionContract.earlyC)
- elif isinstance(optionContract, OptionContractPut):
- FourierPricer.FourierSolo(self._PricingParameters, optionContract, self._NumberSteps, self._NumberAssetSteps, self._StepAdapt, True, optionContract.put, optionContract.expiP, optionContract.earlyP)
- else:
- if isinstance(optionContract, OptionContractDuo):
- FourierPricer.FourierDuo(self._PricingParameters, optionContract, self._NumberSteps, self._NumberAssetSteps, self._StepAdapt, False)
- elif isinstance(optionContract, OptionContractCall):
- FourierPricer.FourierSolo(self._PricingParameters, optionContract, self._NumberSteps, self._NumberAssetSteps, self._StepAdapt, False, optionContract.call, optionContract.expiC,"")
- elif isinstance(optionContract, OptionContractPut):
- FourierPricer.FourierSolo(self._PricingParameters, optionContract, self._NumberSteps, self._NumberAssetSteps, self._StepAdapt, False, optionContract.put, optionContract.expiP,"")
- else:
- raise PricerError("FourierPricer", "Contract not yet supported by pricing algorithm")
Aucun commentaire:
Enregistrer un commentaire