Pages

vendredi 29 mars 2013

Implied Volatility


For option market makers, this is a very important subject.
As I pointed out earlier, one can wonder about the need of a pricer for options whose prices are liquid and tight in the market.

One use is for the greeks: knowing the price of an option doesn't tell me how such price will change when times passes or when the underlying moves. With the pricer and the appropriate volatility, I can calculate such sensitivity. One should keep in mind that such derivatives are model dependant.

But here I have a problem: in order to get the derivative, I need to know the volatility of the option (all other parameters are given: time, rate, stock value, strike). But all I know is the price, and maybe my own opinion about the volatility of the stock (in the future).

The implied volatility is the volatility I should use to have the same price than the one I see in the market. So I need to inverse my pricing function. If one checks the formulas, that's not possible. So we use an iterative procedure:

We guess a volatility, get a price, compare with the observed price and change the volatility until we get a price close enough.
One good aspect is that we know the price as f($\sigma$) but also the derivative of the price with respect to the volatility $df/d\sigma$. So schematically what we do is:

-Guess $\sigma$
-get a price $f(\sigma)$ and the derivative $Vega=df/d\sigma$
-update $\sigma=\sigma+(market price-f(\sigma))/Vega$
-until market price and $f(\sigma)$ are close enough.

This is an application of the Newton-Raphson method.
To have a smart guess for $\sigma$ I use the following approximation:
\[
Stra= S \sigma \frac{ \sqrt{days} }{24} \]
with Strad the price of the straddle (call +put with same strike) and $days$ the number of days until option expiration. Such approximation mostly works for ATM options with very low risk free rate. But that will be better than nothing to start.

Find here the code for the BlackAndScholesPricer class.
I also added an empty method GetImpled in the OptionPricer class

  1. def GetImplied(self,eps):  
  2.     def phi(z):  
  3.         return np.exp(-.5 * z * z) / (np.sqrt( 2 * np.pi ))  
  4.   
  5.   
  6.     S0  =  self._PricingParameters._S0  
  7.     r  =  self._PricingParameters._r  
  8.     T  =  self._PricingParameters._T         
  9.       
  10.     if self._DividendHandling == 'Hull':  
  11.         S0 = S0-self._PricingParameters._pvdividends  
  12.       
  13.     #we start with calls  
  14.     KVec = []  
  15.     SigVec = []  
  16.     TargetPriceVec = []  
  17.       
  18.     #get prices and strikes and a first guess for volatility  
  19.     for optionContract in self._contractList:  
  20.        if isinstance(optionContract, OptionContractCall):  
  21.             KVec.append(optionContract._K)  
  22.             #SigVec.append(optionContract._Sigma)  
  23.             put=-S0+optionContract._K  
  24.             SigVec.append((put+2*optionContract.call.price)*24/(S0*np.sqrt(T*365.0)))    
  25.             TargetPriceVec.append(optionContract.call.price)  
  26.        
  27.     if self._DividendHandling == 'Forward':  
  28.         KVec = KVec+self._PricingParameters._fvdividends  
  29.           
  30.        
  31.     Kv = np.array(KVec[:])  
  32.     Sv = np.array(SigVec[:])  
  33.     PriceTargetv=np.array(TargetPriceVec[:])  
  34.     Pricev=0*PriceTargetv  
  35.       
  36.     if len(Kv)>0:  
  37.         while True:  
  38.             #get vega  
  39.             Vegav= S0*phi(BlackAndScholesPricer.d1(S0,Kv, r, Sv, T))*np.sqrt(T)  
  40.             #get current price  
  41.             Pricev=BlackAndScholesPricer.price('C', S0, Kv, r, Sv, T)  
  42.             #update vola          
  43.             Sv=Sv+(PriceTargetv-Pricev)/Vegav       
  44.               
  45.             if max((PriceTargetv-Pricev)**2) <eps:  
  46.                 break  
  47.                
  48.         idC = 0  
  49.         for optionContract in self._contractList:  
  50.             if isinstance(optionContract, OptionContractCall):  
  51.                     optionContract.call.impliedVol = Sv[idC]  
  52.                     idC = idC+1  
  53.   
  54.     #Then Do it again for puts  
  55.     KVec = []  
  56.     SigVec = []  
  57.     TargetPriceVec = []  
  58.       
  59.     #get prices and strikes and a first guess for volatility  
  60.     for optionContract in self._contractList:  
  61.        if isinstance(optionContract, OptionContractPut):  
  62.             KVec.append(optionContract._K)  
  63.             #SigVec.append(optionContract._Sigma)  
  64.             call=S0-optionContract._K  
  65.             SigVec.append((call+2*optionContract.put.price)*24/(S0*np.sqrt(T*365.0)))   
  66.             TargetPriceVec.append(optionContract.put.price)  
  67.       
  68.     if self._DividendHandling == 'Forward':  
  69.         KVec = KVec+self._PricingParameters._fvdividends  
  70.        
  71.     Kv = np.array(KVec[:])  
  72.     Sv = np.array(SigVec[:])  
  73.     PriceTargetv=np.array(TargetPriceVec[:])  
  74.     Pricev=0*PriceTargetv  
  75.     if len(Kv)>0:  
  76.         while True:  
  77.             #get vega  
  78.             Vegav= S0*phi(BlackAndScholesPricer.d1(S0,Kv, r, Sv, T))*np.sqrt(T)  
  79.             #get current price  
  80.             Pricev=BlackAndScholesPricer.price('P', S0, Kv, r, Sv, T)  
  81.             #update vola          
  82.             Sv=Sv+(PriceTargetv-Pricev)/Vegav       
  83.               
  84.             if max((PriceTargetv-Pricev)**2) <eps:  
  85.                 break  
  86.                
  87.         idC = 0  
  88.         for optionContract in self._contractList:  
  89.             if isinstance(optionContract, OptionContractPut):  
  90.                     optionContract.put.impliedVol = Sv[idC]  
  91.                     idC = idC+1  

A couple of comments on the code:
-It is vectorized: a list of call is made, in one function call we get their vega, and in a second function call we get their price.
-Then we treat the puts. If the pricing model is right and the parameters as well (rate, dividends), we should get the same implied for the puts but it might not be the case.
-I added an attribute to store the implied volatility in the class OptionGreeks.
-EDIT: I added the support for the BS dividend model. Without it, the implied of call and put would not match, even for european options, when pricing with dividends.

eps is the square of the maximum deviation allowed between prices in the market and our prices. Since it is vectorized, most of the strikes will have a lower error.

Using the following code, one can see interesting things:

  1. from BlackAndScholesPricer import *  
  2. from BinomialTreePricer import *  
  3. from PDEPricer import *  
  4. from MCPricer import *  
  5. from OptionContracts import *  
  6. from FourierPricer import *  
  7. import matplotlib.pyplot as plt  
  8.   
  9. pricer = BlackAndScholesPricer ()  
  10. pricer2 = BinomialTreePricer ()  
  11. pricer3 = PDEPricer ()  
  12. pricer4 = MCPricer ()  
  13. pricer5 = FourierPricer ()  
  14.   
  15. pricerlist=[pricer,pricer2]  
  16.   
  17. try:  
  18.     pp = PricingParameters (100, 3.0, [[1.5,5], [0.04,0.04]], [[], []])  
  19.       
  20.     for pr in pricerlist:  
  21.         pr.SetPricingParameters(pp)  
  22.       
  23.     pricer.SetPricerParameters()  
  24.     pricer2.SetPricerParameters(250,True,True,True)  
  25.     pricer3.SetPricerParameters(850,100,True,True)  
  26.     pricer4.SetPricerParameters(132500,5,True,True,True)  
  27.     pricer5.SetPricerParameters(500,10,True)  
  28.       
  29.     contrat1 = OptionContractDuoUS(90, 0.4)  
  30.     contrat2 = OptionContractDuoUS(80, 0.42)  
  31.     contrat3 = OptionContractDuoUS(100, 0.43)  
  32.     contrat4 = OptionContractDuoUS(110, 0.45)  
  33.     contrat5 = OptionContractDuoUS(130, 0.50)  
  34.       
  35.     contractlist=[contrat1,contrat2,contrat3,contrat4,contrat5]  
  36.     for pr in pricerlist:  
  37.         for co in contractlist:  
  38.             pr.AddContract(co)  
  39.       
  40.     """ 
  41.     fig=plt.figure() 
  42.     fig2=plt.figure() 
  43.     ax=fig.add_subplot(111) 
  44.     ax2=fig2.add_subplot(111) 
  45.     legende=[] 
  46.     for pr in pricerlist: 
  47.         price,time=pr.TestConvergence(500, 3000) 
  48.         legende.append(pr._pricerId) 
  49.         ax.plot(price) 
  50.         ax2.plot(time) 
  51.          
  52.     leg=ax.legend(legende,'upper center')     
  53.     leg=ax2.legend(legende,'upper left')     
  54.     plt.show()     
  55.     """  
  56.     pricer2.Calculate()  
  57.     pricer.GetImplied(0.01)  
  58.     pricer.PrintSelf()  
  59.       
  60. except PricerError as e:  
  61.     print e._PricerStr, ":", e._Msg  

Note that only one object per contract is created so both pricer points toward the same contracts.

The binomial tree is used to price these american options. Then BS is used to get the implied volatility.
The results show, for example, that contract5 has an implied vol of  55.18% for the put and 50% for the call. The premium for american option is responsible for that and is large (+10% of the implied vol). It makes sense as the put is quite in the money so the early exercise is quite possible (for some stock paths in the future, not now).
For contract1, the put is initially out of the money (strike 90, spot 100) but its implied volatility is 42% instead of 40%. not a neglectable difference.

Aucun commentaire:

Enregistrer un commentaire