Pricing an Equity Linked Structure Note
By Karthik Misra
This Structure note, created by Barclays,
has an aggregates nominal amount of 1.507 million USD. The redemption date for
this note is August 1, 2018. Owners of the note will receive interest payments
once a month on review days till the redemption date. The interest payment is
based on a barrier condition, which requires the linked equity to be above
68.75% of the initial price or 30.09USD. This provides a interest payment of
0.58333. The final payment at maturity note holders will receive 1000USD as
long as the equity stays above the barrier of 68.75% of the initial price or
30.09USD. Else if it is below that barrier level whatever the equivalent return
should be based on performance.
Before going into pricing the note itself,
discount factors should be created to present value from all the interest
payments and principle on maturity. This is done using the ‘USD Yield Curve”
building script provided below on March 18, 2014 and modify it slightly on top
of the regular input change. The changes can be seen in the bottom where the
discount factor is matched with the review dates and saved as a csv.
# 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 import matplotlib.pyplot as plt import pandas as pd # global data # Here enter the Yield Curve reference Data calendar = TARGET() todaysDate = Date(31,March,2014); # should be a weekday Settings.instance().evaluationDate = todaysDate settlementDate = Date(2,April,2014); #add 2 days to todaysDate and should be a weekday # 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 ##Data from quantdl futures = { #Date(19,3,2014): 99.765, #date passed so it was removed 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, Date(16,3,2016): 99.085,} #Date was added to build curve # 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()) rdate=[] rrate=[] #replace array with whatever dates you want c1=[Date(28,4,2014),Date(28,5,2014),Date(26,6,2014),Date(1,8,2014),Date(27,8,2014),Date(26,9,2014),Date(29,10,2014),Date(25,11,2014),Date(29,12,2014),Date(28,1,2015),Date(25,2,2015),Date(27,3,2015),Date(28,4,2015),Date(27,5,2015),Date(26,6,2015),Date(29,7,2015),Date(27,8,2015),Date(28,9,2015),Date(28,10,2015),Date(25,11,2015),Date(29,12,2015),Date(27,1,2016),Date(25,2,2016),Date(29,3,2016),Date(27,4,2016),Date(26,5,2016),Date(28,6,2016),Date(27,7,2016),Date(29,8,2016),Date(28,9,2016),Date(27,10,2016),Date(28,11,2016),Date(28,12,2016),Date(27,1,2017),Date(24,2,2017),Date(29,3,2017),Date(26,4,2017),Date(26,5,2017),Date(28,6,2017),Date(27,7,2017),Date(29,8,2017),Date(27,9,2017),Date(27,10,2017),Date(28,11,2017),Date(27,12,2017),Date(29,1,2018),Date(26,2,2018),Date(27,3,2018),Date(26,4,2018),Date(29,5,2018),Date(27,6,2018),Date(25,7,2018)] #print c1 #print depoFuturesSwapCurve.dates() v=[] rdate.append(depoFuturesSwapCurve.dates()) for c in c1: #print depoFuturesSwapCurve.discount(c) v.append([c,depoFuturesSwapCurve.discount(c)]) rrate.append(depoFuturesSwapCurve.discount(c)) #pulls the discount factor that correlates with the dates and saves it to csv v=pd.DataFrame(v) v.columns=['Date','Df'] v.to_csv('Desktop/lsm_bmy.csv') Now the note can be priced. A tweak to the Monte-Carlo Simulation scrip allows this note to be priced. Specifically simulates the movement of the equity using a standard normal distribution allowing for random interest payments. Remember to change the csv import location to match the location of the saved csv from above along with all the other normal input changes to match the description on the note. Once the program is done running should see the value of the note as note price. import pandas as pd import numpy as np # numpy namespace from timeit import default_timer as timer # for timing from matplotlib import pyplot # for plotting import math def step_numpy(dt, prices, c0, c1, noises): return prices * np.exp(c0 * dt + c1 * noises) def pricer(paths, dt,Dividend, interest, volatility): c0 = interest - Dividend- 0.5 * volatility ** 2 c1 = volatility * np.sqrt(dt) for j in xrange(1, paths.shape[1]): # for each time step prices = paths[:, j - 1] # last prices # gaussian noises for simulation noises = np.random.normal(0., 1., prices.size) # simulate paths[:, j] = step_numpy(dt, prices, c0, c1, noises) StockPrice=53.15 #StockPrice StrikePrice=53.15 #StrikePrice Volatility=.25493 #Volatility InterestRate=.012 #InterestRate Maturity=4.58333 #Maturity Dividend=.005016 #Dividend CP=-1 #CP = 1/-1 for call/put ot=1 #ot = 1/2/3 for vinilla/asian/barrier NumPath=100000 #NumPath = number of runs NumStep=52 #NumStep = number of steps in each run should be the number of payments #MAX_PATH_IN_PLOT= #Preparation paths = np.zeros((NumPath, NumStep + 1), order='F') paths[:, 0] = StockPrice DT = Maturity / NumStep df = np.exp(-InterestRate * DT) #Simulation ts = timer() pricer(paths, DT,Dividend, InterestRate, Volatility) te = timer() elapsed = te - ts ST = paths[:, -1] #Creates coupon payment for simulation streams adjust as needed h1=np.where(paths[:,:]>=30.09,0.58333,0) #adjust as needed h= np.maximum(CP*(paths - StrikePrice), 0) V = np.zeros_like(h) # value matrix V[:,-1] = h[:,-1] # Valuation by LSM #for t in range(NumStep - 1, 0, -1): # rg = np.polyfit(paths[:,t], V[:, t+1] * df, 5) # regression # C = np.polyval(rg, paths[:,t]) # evaluation of regression # V[:,t] = np.where(h[:,t] > C, h[:,t],V[:, t+1] * df) # exercise decision/optimization #V0 = np.sum(V[:, 1] * df) / NumStep # LSM estimator #V1= np.average(V[:, 1] * df) / NumStep #if ot==1: # PaidOff = np.maximum(CP*(paths[:, -1] - StrikePrice), 0) #elif ot==2: # PaidOff = np.maximum(CP*(np.average(paths[:,]) - StrikePrice), 0) # #elif ot==3: # PaidOff = np.maximum(CP*((paths[:, -1]>=BarrierLevel) - StrikePrice), 0) #elif ot==4: # PaidOff = np.maximum(CP*((paths[:, -1]<=BarrierLevel) - StrikePrice), 0) #Accounts for payment on maturity adjust as needed h1[:,-1]=np.where(paths[:,-1]>=30.09,100.0,(100.0*(paths[:,-1]/43.76))) #adjust as needed #Creates an vector to discount q1=np.zeros_like(h1) #imports discount factor from csv as an array #df1=np.genfromtxt('Desktop/DF_BMY.csv', delimiter=',') df=pd.read_csv('Desktop/lsm_bmy.csv') df=df.ix[:,'Df'] df1=[] df1[:]=df #discounts all payments for x in range(0,NumStep): q1[:,x]=h1[:,x]*df1[x] #q1[:,x]=h1[:,x]/((1+InterestRate)**x) q2=np.sum(q1[:,], axis=1) gc=np.average(q2) #discounts final payment on maturity q3=h1[:,-1]*df1[-1] g=np.average(q3) print 'Result' fmt = '%20s: %s' print fmt % ('principal', g) print fmt % ('coupon cash flows', gc) print fmt % ('Note price', (g+gc)) print 'Performance' NumCompute = NumPath * NumStep print fmt % ('Mstep/second', '%.2f' % (NumCompute / elapsed / 1e6)) print fmt % ('time elapsed', '%.3fs' % (te - ts)) A run of this program, with a 100000 simulations, provided a result where the principle was 85.1438664683. The coupon cash flows is 27.140993899 with the note price being $113.284860367.
No comments:
Post a Comment