Tuesday, April 1, 2014

Pricing an Equity Linked Note - Python-Quantlib

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