In previous posts, I have described how to construct USD LIBOR Swap Yield curve then we have looked at how to price an interest rate swap. In this note I will discuss what is European Swaption and how to value such a product using Quantlib.
Product Description:
European Swaptions are instruments that give holder of the option right to Pay or receive fixed rate. This option can be classified into two different types. Payer Swaption in which option buyer gets right to receive fixed rate and pay floating rate at the exercise date if exercised. Receiver Swaption works exactly opposite to the payer swaption where the option holder has the right to receive fixed rate.
Variety of players,including, Hedge funds, banks and pension funds are actively participating in this market to monetize their views on the shapes of yield curve or conduct risk management of their existing portfolios. Consequently it becomes imperative to know if the price they are paying or getting on the swaption is right. To understand this one has to have a tool set to price a swaption.
Pricing and Modeling of the Swaption:
I have covered some details with regards to how to price a 5y into 5y ATM (at the money) swaption. To be able to value this swaption, I have constructed an yield curve ( with the details of the instruments and curve construction provided below) and then priced a 5y forward 5y swap. Then applying a 15.3% implied volatility I have priced a payer swaption yielding price of 23,162. In general these long dated swaptions are vega sesitive. In other words they appreciate or depreciate when Implied volatility changes significantly. To understand this effect,I have changed the price of the option to 60,000 and implied volatility changed to 40%. Calculations for how to perform these computations are provided step by step using python and Quantlib.
Feel free to use these in your calculation or learning process. Also available other articles on this blog that discuss nuances of swaption market, pricing model and risk management.
# Copyright (C) 2014, Khandrika Capital Markets # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the license for more details. from QuantLib import * import datetime import numpy as np import matplotlib.pyplot as plt # global data # Here enter the Yield Curve reference Data calendar = TARGET() todaysDate = Date(28,February,2014); Settings.instance().evaluationDate = todaysDate settlementDate = Date(4,March,2014); # market quotes # Update deposit Rates ( usual source will be LIBOR Fixings on the Curve Date deposits = { (1,Weeks): 0.0023, (1,Months): 0.0023, (3,Months): 0.0023, (6,Months): 0.0023} # Obtain Futures prices from CME traded Euro Dollar Futures futures = { Date(19,3,2014): 99.765, Date(18,6,2014): 99.75, Date(17,9,2014): 99.73, Date(17,12,2014): 99.69, Date(18,3,2015): 99.605, Date(17,6,2015): 99.47, Date(16,9,2015): 99.3, Date(16,12,2015): 99.085 } # Obtain Swap rates from Traded Swaps on the Curve data swaps = { (3,Years): 0.0079, (4,Years): 0.012, (5,Years): 0.0157, (6,Years): 0.01865, (7,Years): 0.0216, (8,Years): 0.0235, (9,Years): 0.0254, (10,Years): 0.0273, (15,Years): 0.0297, (20,Years): 0.0316, (25,Years): 0.0335, (30,Years): 0.0354} # convert them to Quote objects for n,unit in deposits.keys(): deposits[(n,unit)] = SimpleQuote(deposits[(n,unit)]) for d in futures.keys(): futures[d] = SimpleQuote(futures[d]) for n,unit in swaps.keys(): swaps[(n,unit)] = SimpleQuote(swaps[(n,unit)]) # build rate helpers dayCounter = Actual360() settlementDays = 2 depositHelpers = [ DepositRateHelper(QuoteHandle(deposits[(n,unit)]), Period(n,unit), settlementDays, calendar, ModifiedFollowing, False, dayCounter) for n, unit in [(1,Weeks),(1,Months),(3,Months), (6,Months)] ] dayCounter = Actual360() months = 3 futuresHelpers = [ FuturesRateHelper(QuoteHandle(futures[d]), d, months, calendar, ModifiedFollowing, True, dayCounter, QuoteHandle(SimpleQuote(0.0))) for d in futures.keys() ] settlementDays = 2 fixedLegFrequency = Semiannual fixedLegTenor = Period(6,Months) fixedLegAdjustment = Unadjusted fixedLegDayCounter = Thirty360() floatingLegFrequency = Quarterly floatingLegTenor = Period(3,Months) floatingLegAdjustment = ModifiedFollowing swapHelpers = [ SwapRateHelper(QuoteHandle(swaps[(n,unit)]), Period(n,unit), calendar, fixedLegFrequency, fixedLegAdjustment, fixedLegDayCounter, Euribor3M()) for n, unit in swaps.keys() ] # term structure handles discountTermStructure = RelinkableYieldTermStructureHandle() forecastTermStructure = RelinkableYieldTermStructureHandle() # term-structure construction helpers = depositHelpers[:2] + futuresHelpers + swapHelpers[1:] depoFuturesSwapCurve = PiecewiseFlatForward(settlementDate, helpers, Actual360()) print depoFuturesSwapCurve.dates() df=[] dates1=[] for c in depoFuturesSwapCurve.dates(): df.append(depoFuturesSwapCurve.discount(c)) dates1.append(c) print depoFuturesSwapCurve.discount(c) termStructure = YieldTermStructureHandle(depoFuturesSwapCurve) #End of Yield Curve Construction #Begin building forward Curve # Forward swap underlying the Swaption to be priced # In this case I am pricing a 5y into 5Y swap swapEngine = DiscountingSwapEngine(discountTermStructure) nominal = 1000000 length = 5 maturity = calendar.advance(settlementDate,length,Years) payFixed = True fixedLegFrequency = Semiannual fixedLegAdjustment = Unadjusted fixedLegDayCounter = Thirty360() floatingLegFrequency = Quarterly spread = 0.0 fixingDays = 2 index = Euribor3M(forecastTermStructure) floatingLegAdjustment = ModifiedFollowing floatingLegDayCounter = index.dayCounter() #ATM forward Rate fixedRate = 0.040852 forwardStart = calendar.advance(settlementDate,5,Years) forwardEnd = calendar.advance(forwardStart,length,Years) fixedSchedule = Schedule(forwardStart, forwardEnd, fixedLegTenor, calendar, fixedLegAdjustment, fixedLegAdjustment, DateGeneration.Forward, False) floatingSchedule = Schedule(forwardStart, forwardEnd, floatingLegTenor, calendar, floatingLegAdjustment, floatingLegAdjustment, DateGeneration.Forward, False) forward = VanillaSwap(VanillaSwap.Payer, nominal, fixedSchedule, fixedRate, fixedLegDayCounter, floatingSchedule, index, spread, floatingLegDayCounter) forward.setPricingEngine(swapEngine) def formatPrice(p,digits=2): format = '%%.%df' % digits return format % p def formatRate(r,digits=2): format = '%%.%df %%%%' % digits return format % (r*100) headers = ("term structure", "net present value", "fair spread", "fair fixed rate" ) separator = " | " format = '' width = 0 for h in headers[:-1]: format += '%%%ds' % len(h) format += separator width += len(h) + len(separator) format += '%%%ds' % len(headers[-1]) width += len(headers[-1]) rule = "-" * width dblrule = "=" * width tab = " " * 8 def report(swap, name): print format % (name, formatPrice(swap.NPV(),2), formatRate(swap.fairSpread(),4), formatRate(swap.fairRate(),4)) print dblrule print "5-year market Spot swap-rate = %s" % formatRate(swaps[(5,Years)].value()) print dblrule discountTermStructure.linkTo(depoFuturesSwapCurve) forecastTermStructure.linkTo(depoFuturesSwapCurve) report(forward,'depo-fut-swap') ############################################## # Bulding the European Swaption pricer part exercise = maturity exercised = EuropeanExercise(exercise) settlementtype="physical" atmswaption = Swaption(forward,exercised) #Applying a 15.3% implied volatility to 5y into 5y ATM swaption vol1 = QuoteHandle(SimpleQuote(0.1533)) atmswaption.setPricingEngine(BlackSwaptionEngine(termStructure,vol1)) print atmswaption.NPV() #***************************** #Now given Market Premium implying the underlying volatility index = Euribor3M(termStructure) #Place holder for the iterator to hold the implied volatility in the #swaption Helper swaptionVols = [ # maturity, length, volatility (Period(5, Years), Period(5, Years), 0.1533)] helpers = [ SwaptionHelper(maturity, length, QuoteHandle(SimpleQuote(vol)), index, index.tenor(), index.dayCounter(), index.dayCounter(), termStructure) for maturity, length, vol in swaptionVols ] for swaption, helper in zip(swaptionVols, helpers): maturity, length, vol = swaption print swaption helper.setPricingEngine(BlackSwaptionEngine(termStructure,vol1)) NPV = helper.modelValue() print NPV NPV=0.06 # here we are adding a premium of 60,000 per 1 MM Notional implied = helper.impliedVolatility(NPV, 1.0e-4, 1000, 0.05, 0.50) print implied
your examples are really SUPER helpful thank you so much. I have one question, how do you obtain the swap rates from traded swaps? (Obtain Swap rates from Traded Swaps on the Curve data)?
ReplyDeleteNevermind, I figured it out. It feels silly not seeing that earlier :-) THank you very much for your work on this website.
ReplyDeleteThank you.
ReplyDeleteHi. I have a question in regards to the ATM forward rate. the ATM forward rate is the rate at time of the Swaption expiry, derived from the currency of the underlying? Please correct me if i am wrong.
ReplyDeleteYes thats correct. Forward rate underlying swaption will have a start date as option expiry date and maturity date as underlying swap maturity date. You can derive this from today;s yield curve.
Delete