def __init__(self, start, underlyings): myScheduler = Scheduler() myDelays = [] freqs = ['3M', '6M', '1Y', '3Y'] for i in range(0, len(freqs)): myDelays.append(myScheduler.extractDelay(freqs[i])) AAA = {} for i in range(0, len(freqs)): vas = MC_Vasicek_Sim(x=AAAx[freqs[i]], datelist=[start, myDelays[i] + start], t_step=1 / 365., simNumber=500) AAA[freqs[i]] = vas.getLibor()[0].loc[myDelays[i] + start] BBB = { '3M': MC_Vasicek_Sim(x=BBBx[freqs[0]], datelist=[start, myDelays[0] + start], t_step=1 / 365., simNumber=500).getLibor()[0].loc[myDelays[0] + start] } self.probs = {'AAA': AAA, 'BBB': BBB} self.underlyings = underlyings
class CorporateRates(object): def __init__(self): self.OIS = [] self.filename = WORKING_DIR + '/CorpData.dat' self.corporates = [] self.ratings = { 'AAA': "BAMLC0A1CAAA", 'AA': "BAMLC0A2CAA", 'A': "BAMLC0A3CA", 'BBB': "BAMLC0A4CBBB", 'BB': "BAMLH0A1HYBB", 'B': "BAMLH0A2HYB", 'CCC': "BAMLH0A3HYC" } self.corpSpreads = {} self.corporates = pd.DataFrame() self.Qcorporates = pd.DataFrame() # survival function for corporates self.tenors = [] self.unPickleMe(file=self.filename) self.myScheduler = Scheduler() self.myVasicek = MC_Vasicek_Sim() self.R = 0.4 def getCorporatesFred(self, trim_start, trim_end): fred = Fred(api_key=FRED_API_KEY) curr_trim_end = trim_start if (self.corporates.size != 0): self.trim_start = self.corporates['OIS'].index.min().date() curr_trim_end = self.corporates['OIS'].index.max().date() if trim_end <= curr_trim_end: self.trim_end = curr_trim_end return self.corporates self.trim_start = trim_start self.trim_end = trim_end self.OIS = OIS(trim_start=trim_start, trim_end=trim_end) self.datesAll = self.OIS.datesAll self.datesAll.columns = [x.upper() for x in self.datesAll.columns] self.datesAll.index = self.datesAll.DATE self.OISData = self.OIS.getOIS() for i in np.arange(len(self.OISData.columns)): freq = self.OISData.columns[i] self.tenors.append(self.myScheduler.extractDelay(freq=freq)) for rating in self.ratings.keys(): index = self.ratings[rating] try: corpSpreads = 1e-2 * (fred.get_series( index, observation_start=trim_start, observation_end=trim_end).to_frame()) corpSpreads.index = [x.date() for x in corpSpreads.index[:]] corpSpreads = pd.merge(left=self.datesAll, right=corpSpreads, left_index=True, right_index=True, how="left") corpSpreads = corpSpreads.fillna(method='ffill').fillna( method='bfill') corpSpreads = corpSpreads.drop("DATE", axis=1) self.corpSpreads[rating] = corpSpreads.T.fillna( method='ffill').fillna(method='bfill').T except Exception as e: print(e) print(index, " not found") self.corpSpreads = pd.Panel.from_dict(self.corpSpreads) self.corporates = {} self.OISData.drop('DATE', axis=1, inplace=True) ntenors = np.shape(self.OISData)[1] for rating in self.ratings: try: tiledCorps = np.tile(self.corpSpreads[rating][0], ntenors).reshape(np.shape(self.OISData)) self.corporates[rating] = pd.DataFrame( data=(tiledCorps + self.OISData.values), index=self.OISData.index, columns=self.OISData.columns) except: print("Error in addition of Corp Spreads") self.corporates['OIS'] = self.OISData self.corporates = pd.Panel(self.corporates) return self.corporates def getCorporateData(self, rating, datelist=None): # This method gets a curve for a given date or date list for a given rating (normally this will be just a date). # It returns a dict of curves read directly from the corporate rates created by getCorporatesFred. # Derive delays from self.corporates[rating].columns myDelays = self.myScheduler.extractDelay( freq=list(self.corporates[rating].columns)) if datelist is None: return outCurve = {} for day in datelist: # Create curves # .............. # .............. # add curve to outcurve dict outCurve[day] = myCurve return outCurve def getCorporateQData(self, rating, datelist=None, R=0.4): self.R = R if datelist is None: return outCurve = {} for day in datelist: # Create Q curves using q-tilde equation # .............. # .............. outCurve[day] = myCurve return outCurve def pickleMe(self): data = [self.corporates, self.corpSpreads] with open(self.filename, "wb") as f: pickle.dump(len(data), f) for value in data: pickle.dump(value, f) def unPickleMe(self, file): data = [] if (os.path.exists(file)): with open(file, "rb") as f: for _ in range(pickle.load(f)): data.append(pickle.load(f)) self.corporates = data[0] self.corpSpreads = data[1] def saveMeExcel(self, whichdata, fileName): try: df = pd.DataFrame(whichdata) except: df = whichdata df.to_excel(fileName)
class CouponBond(object): def __init__(self, fee, start,coupon, maturity, freq, referencedate, observationdate,notional): self.fee = fee self.coupon=coupon self.start = start self.maturity = maturity self.freq= freq self.referencedate = referencedate self.observationdate = observationdate self.myScheduler = Scheduler() self.delay = self.myScheduler.extractDelay(freq=freq) self.getScheduleComplete() self.ntimes=len(self.datelist) self.referencedate = referencedate self.pvAvg=0.0 self.cashFlows = DataFrame() self.cashFlowsAvg = [] self.yieldIn = 0.0 self.notional=notional def getScheduleComplete(self): self.datelist = self.myScheduler.getSchedule(start=self.start,end=self.maturity,freq=self.freq,referencedate=self.referencedate) fullset = list(sorted(list(set(self.datelist) .union([self.referencedate]) .union([self.start]) .union([self.maturity]) .union([self.observationdate]) ))) return fullset,self.datelist def setLibor(self,libor): self.libor = libor/libor.loc[self.referencedate] self.ntimes = np.shape(self.datelist)[0] self.ntrajectories = np.shape(self.libor)[1] self.ones = np.ones(shape=[self.ntrajectories]) def getExposure(self, referencedate): if self.referencedate!=referencedate: self.referencedate=referencedate self.getScheduleComplete() deltaT= np.zeros(self.ntrajectories) if self.ntimes==0: pdzeros= pd.DataFrame(data=np.zeros([1,self.ntrajectories]), index=[referencedate]) self.pv=pdzeros self.pvAvg=0.0 self.cashFlows=pdzeros self.cashFlowsAvg=0.0 return self.pv for i in range(1,self.ntimes): deltaTrow = ((self.datelist[i]-self.datelist[i-1]).days/365)*self.ones deltaT = np.vstack ((deltaT,deltaTrow) ) self.cashFlows= self.coupon*deltaT principal = self.ones if self.ntimes>1: self.cashFlows[-1:]+= principal else: self.cashFlows = self.cashFlows + principal if(self.datelist[0]<= self.start): self.cashFlows[0]=-self.fee*self.ones if self.ntimes>1: self.cashFlowsAvg = self.cashFlows.mean(axis=1)*self.notional else: self.cashFlowsAvg = self.cashFlows.mean() * self.notional pv = self.cashFlows*self.libor.loc[self.datelist] self.pv = pv.sum(axis=0)*self.notional self.pvAvg = np.average(self.pv)*self.notional return self.pv def getPV(self,referencedate): self.getExposure(referencedate=referencedate) return self.pv/self.libor.loc[self.observationdate] def getLiborAvg(self, yieldIn, datelist): self.yieldIn = yieldIn time0 = datelist[0] # this function is used to calculate exponential single parameter (r or lambda) Survival or Libor Functions Z = np.exp(-self.yieldIn * pd.DataFrame(np.tile([(x - time0).days / 365.0 for x in self.datelist], reps=[self.ntrajectories,1]).T,index=self.datelist)) return Z def getYield(self,price): # Fit model to curve data yield0 = 0.05 * self.ones self.price = price self.yieldIn = self.fitModel2Curve(x=yield0) return self.yieldIn def fitModel2Curve(self, x ): # Minimization procedure to fit curve to model results = minimize(fun=self.fCurve, x0=x) return results.x def fCurve(self, x): # raw data error function calcCurve = self.getLiborAvg(x, self.datelist) thisPV = np.multiply(self.cashFlows,calcCurve).mean(axis=1).sum(axis=0) error = 1e4 * (self.price - thisPV) ** 2 return error
class CouponBond(object): def __init__(self, fee, coupon, start, maturity, freq, referencedate, observationdate, rating, notional=1, recovery=0.4): self.numSim = 10 self.t_step = 1.0 / 365 self.fee = fee self.coupon = coupon self.start = start self.maturity = maturity self.freq = freq self.recovery = recovery self.rating = rating self.referencedate = referencedate self.observationdate = observationdate self.myScheduler = Scheduler() self.delay = self.myScheduler.extractDelay(freq=freq) self.referencedate = referencedate self.getScheduleComplete() self.fullDateList = self.datelist self.ntimes = len(self.datelist) self.pvAvg = 0.0 self.cashFlows = DataFrame() self.cashFlowsAvg = [] self.yieldIn = 0.0 self.notional = notional self.errorCurve = [] self.mcSim = MC_Vasicek_Sim() def getScheduleComplete(self): self.datelist = self.myScheduler.getSchedule( start=self.start, end=self.maturity, freq=self.freq, referencedate=self.referencedate) self.ntimes = len(self.datelist) fullset = sorted( set(self.datelist).union([self.referencedate]).union([ self.start ]).union([self.maturity]).union([self.observationdate])) a = 1 return fullset, self.datelist def setLibor(self, libor): self.libor = libor / libor.loc[self.referencedate] #self.ntimes = np.shape(self.datelist)[0] self.ntrajectories = np.shape(self.libor)[1] self.ones = np.ones(shape=[self.ntrajectories]) def getExposure(self, referencedate): if self.referencedate != referencedate: self.referencedate = referencedate self.getScheduleComplete() deltaT = np.zeros(self.ntrajectories) if self.ntimes == 0: pdzeros = pd.DataFrame(data=np.zeros([1, self.ntrajectories]), index=[referencedate]) self.pv = pdzeros self.pvAvg = 0.0 self.cashFlows = pdzeros self.cashFlowsAvg = 0.0 return self.pv for i in range(1, self.ntimes): deltaTrow = ((self.datelist[i] - self.datelist[i - 1]).days / 365) * self.ones deltaT = np.vstack((deltaT, deltaTrow)) self.cashFlows = self.coupon * deltaT principal = self.ones if self.ntimes > 1: self.cashFlows[-1:] += principal else: self.cashFlows = self.cashFlows + principal if (self.datelist[0] <= self.start): self.cashFlows[0] = -self.fee * self.ones if self.ntimes > 1: self.cashFlowsAvg = self.cashFlows.mean(axis=1) * self.notional else: self.cashFlowsAvg = self.cashFlows.mean() * self.notional pv = self.cashFlows * self.libor.loc[self.datelist] self.pv = pv.sum(axis=0) * self.notional self.pvAvg = np.average(self.pv) * self.notional return self.pv def getPV(self, referencedate): self.getExposure(referencedate=referencedate) return self.pv / self.libor.loc[self.referencedate] def getFullExposure(self): fullExposure = pd.DataFrame(data=np.zeros(len(self.fullDateList)), index=self.fullDateList) for days in self.fullDateList: fullExposure.loc[days] = self.getExposure(days) self.fullExposure = fullExposure def setCorpData(self, corpData): self.corpData = corpData.loc[self.rating, :, "1 MO"] def setxQ(self, xQ): res = optimize.fmin(func=self.mcSim.errorFunction, x0=np.array([xQ[0], xQ[1]]), args=(self.corpData.values[:-1], self.corpData.values[1:], self.t_step)) xQSigma = np.std(self.corpData.values[:-1] - self.corpData.values[1:]) self.xQ = np.append(res, np.array([xQSigma, xQ[3]])) self.mcSim.setVasicek(minDay=self.start, maxDay=self.maturity, x=self.xQ, simNumber=self.numSim, t_step=self.t_step) def setQCurve(self): self.qCurve = self.mcSim.getLibor().loc[:, 0] return def getCVA(self): self.CVA = (1 - self.recovery) * self.fullExposure.loc[ self.fullDateList, 0].values * self.qCurve.loc[ self.fullDateList] * self.libor.loc[self.fullDateList, 0] def getLiborAvg(self, yieldIn, datelist): self.yieldIn = yieldIn time0 = datelist[0] # this function is used to calculate exponential single parameter (r or lambda) Survival or Libor Functions Z = np.exp(-self.yieldIn * pd.DataFrame(np.tile([(x - time0).days / 365.0 for x in self.datelist], reps=[self.ntrajectories, 1]).T, index=self.datelist)) return Z def getYield(self, price): # Fit model to curve data yield0 = 0.05 * self.ones self.price = price self.yieldIn = self.fitModel2Curve(x=yield0) return self.yieldIn def fitModel2Curve(self, x): # Minimization procedure to fit curve to model results = optimize.minimize(fun=self.fCurve, x0=x) a = 1 return results.x def fCurve(self, x): # raw data error function calcCurve = self.getLiborAvg(x, self.datelist) thisPV = np.multiply(self.cashFlows, calcCurve).mean(axis=1).sum(axis=0) error = 1e4 * (self.price - thisPV)**2 self.errorCurve = error return error
class CorporateRates(object): def __init__(self): self.OIS = [] self.filename = WORKING_DIR + '/CorpData.dat' self.corporates = [] self.ratings = { 'AAA': "BAMLC0A1CAAA", 'AA': "BAMLC0A2CAA", 'A': "BAMLC0A3CA", 'BBB': "BAMLC0A4CBBB", 'BB': "BAMLH0A1HYBB", 'B': "BAMLH0A2HYB", 'CCC': "BAMLH0A3HYC" } self.corpSpreads = {} self.corporates = pd.DataFrame() self.Qcorporates = pd.DataFrame() # survival function for corporates self.tenors = [] self.unPickleMe(file=self.filename) self.myScheduler = Scheduler() #self.myVasicek = MC_Vasicek_Sim() self.R = 0.4 def getCorporatesFred(self, trim_start, trim_end): self.corpSpreads = {} self.corpSpreads = {} fred = Fred(api_key=FRED_API_KEY) curr_trim_end = trim_start if (self.corporates.size != 0): self.trim_start = self.corporates['OIS'].index.min().date() curr_trim_end = self.corporates['OIS'].index.max().date() self.trim_start = trim_start self.trim_end = trim_end self.OIS = OIS(trim_start=trim_start, trim_end=trim_end) self.datesAll = self.OIS.datesAll #print(self.datesAll) self.datesAll.columns = [x.upper() for x in self.datesAll.columns] #print(self.datesAll) self.datesAll.index = self.datesAll.DATE self.OISData = self.OIS.getOIS() #print(self.OISData) for i in np.arange(len(self.OISData.columns)): freq = self.OISData.columns[i] self.tenors.append(self.myScheduler.extractDelay(freq=freq)) #print(self.tenors) for rating in self.ratings.keys(): index = self.ratings[rating] try: corpSpreads = 1e-2 * (fred.get_series( index, observation_start=trim_start, observation_end=trim_end).to_frame()) corpSpreads.index = [x.date() for x in corpSpreads.index[:]] corpSpreads = pd.merge(left=self.datesAll, right=corpSpreads, left_index=True, right_index=True, how="left") corpSpreads = corpSpreads.fillna(method='ffill').fillna( method='bfill') corpSpreads = corpSpreads.drop("DATE", axis=1) self.corpSpreads[rating] = corpSpreads.T.fillna( method='ffill').fillna(method='bfill').T except Exception as e: print(e) print(index, " not found") self.corpSpreads = pd.Panel.from_dict(self.corpSpreads) #print(self.corpSpreads) self.corporates = {} self.OISData.drop('DATE', axis=1, inplace=True) ntenors = np.shape(self.OISData)[1] for rating in self.ratings: try: tiledCorps = np.tile(self.corpSpreads[rating][0], ntenors).reshape(np.shape(self.OISData)) self.corporates[rating] = pd.DataFrame( data=(tiledCorps + self.OISData.values), index=self.OISData.index, columns=self.OISData.columns) except: print(rating) print("Error in addition of Corp Spreads") self.corporates['OIS'] = self.OISData self.corporates = pd.Panel(self.corporates) return self.corporates def convertCols(self, list): out = [] for i in range(0, len(list)): out.append(list[i].split(' ', 1)[0] + list[i].split(' ', 1)[1][0]) return out def getCorporateData(self, rating, datelist=None): # This method gets a curve for a given date or date list for a given rating (normally this will be just a date). # It returns a dict of curves read directly from the corporate rates created by getCorporatesFred. # Derive delays from self.corporates[rating].columns #print("GEt corporate data ") if datelist is None: return outCurve = {} datelist.sort() #need an iteratble object cols = self.convertCols(list(self.corporates[rating].columns)) myDelays = [] myDelays.append(relativedelta.relativedelta(days=0)) #just putting an iterable object together for i in range(0, len(cols)): myDelays.append(self.myScheduler.extractDelay(freq=cols[i])) myCurve = self.corporates[rating] cols = ['0D'] + cols #print("My curve") #print(myCurve) #print(myDelays) #grabbing only the info with the rating I want and making a datelist to calulate time differences dates = [] for day in range(0, len(datelist)): #print(day) dates.append([(myDelays[x] + datelist[day]) for x in range(0, len(myDelays))]) #print(dates) #my array of interest rates r = np.zeros((len(datelist), len(dates[0]))) #print(r) nrows = len(dates) #multiplying the rate by the delta t in days and saving it to a spot in the array for j in range(0, len(datelist)): #print(datelist[j]) day_tenors = myCurve.loc[datelist[j]] for i in range(1, len(day_tenors) + 1): r[j, i] = r[j, i - 1] + day_tenors[i - 1] * ( (dates[j][i] - datelist[j]).days / 365) # Create curves # .............. # .............. # add curve to outcurve dict #integrating and taking e^- intR = r.cumsum(axis=1) outCurve = np.exp(-intR) out = pd.DataFrame(outCurve) out.columns = cols return out def getSpreads(self, rating, datelist=None): if datelist is None: return outCurve = {} #need an iteratble object #just putting an iterable object together myCurve = self.corpSpreads[rating] #grabbing only the info with the rating I want and making a datelist to calulate time differences #my array of interest rates r = np.zeros(len(datelist)) #multiplying the rate by the delta t in days and saving it to a spot in the array for j in range(1, len(datelist)): r[j] = r[j - 1] + myCurve['VALUE'].loc[j - 1] * ( datelist[j] - datelist[j - 1]).days / 365 # Create curves # .............. # .............. # add curve to outcurve dict #integrating and taking e^- intR = r.cumsum(axis=0) outCurve = np.exp(-intR) out = pd.DataFrame(outCurve, index=datelist) return out def getCorporateQData(self, rating, datelist, R=0.4): #print("Get gorporate Q data") self.R = R if datelist is None: return trim_start = datelist[0] trim_end = datelist[-1] # Create Q curves using q-tilde equation outCurve = ( (1 - (1 / (1 - R)) * (1 - (self.getCorporateData(rating=rating, datelist=datelist) / self.getCorporateData(rating='OIS', datelist=datelist))) ).values).tolist() out = pd.DataFrame(outCurve, index=datelist) out[out < 0] = 0 cols = self.convertCols(list(self.corporates[rating].columns)) cols = ['0D'] + cols ## Get OIS ### #getOIS = OIS(trim_start = trim_start,trim_end=trim_end) #print(getOIS.getOIS(datelist = datelist)) #outCurve = ((1 - (1 / (1 - R)) * (1 - (self.getCorporatesFred(trim_start) / self.getCorporateData(rating='OIS', datelist=datelist)))).values).tolist() out.columns = cols return out def pickleMe(self): data = [self.corporates, self.corpSpreads] with open(self.filename, "wb") as f: pickle.dump(len(data), f) for value in data: pickle.dump(value, f) def unPickleMe(self, file): data = [] if (os.path.exists(file)): with open(file, "rb") as f: for _ in range(pickle.load(f)): data.append(pickle.load(f)) self.corporates = data[0] self.corpSpreads = data[1] def saveMeExcel(self, whichdata, fileName): try: df = pd.DataFrame(whichdata) except: df = whichdata df.to_excel(fileName)
class PortfolioLossCalculation(object): def __init__(self, K1, K2, Fs, Rs, betas, start_dates, end_dates, freqs, coupons, referenceDates, ratings, bootstrap): self.bootstrap = bootstrap self.K1 = K1 self.K2 = K2 self.Fs = Fs self.Rs = Rs self.betas = betas self.referenceDates = referenceDates self.freqs = freqs self.coupons = coupons self.start_dates = start_dates self.end_dates = end_dates self.ratings = ratings self.R = Rs[0] self.beta = betas[0] self.referenceDate = referenceDates[0] self.freq = freqs[0] self.coupon = coupons[0] self.start_date = start_dates[0] self.end_date = end_dates[0] self.rating = ratings[0] self.maturity = end_dates[0] self.myScheduler = Scheduler() def setParameters(self, R, rating, coupon, beta, start_date, referenceDate, end_date, freq): self.R = R self.beta = beta self.referenceDate = referenceDate self.freq = freq self.coupon = coupon self.start_date = start_date self.end_date = end_date self.rating = rating self.maturity = end_date def getQ(self, start_date, referenceDate, end_date, freq, coupon, rating, R): ## Use CorporateDaily to get Q for referencedates ## # print("GET Q") # print(self.portfolioScheduleOfCF) if self.bootstrap: print("Q bootstrap") CDSClass = CDS(start_date=start_date, end_date=end_date, freq=freq, coupon=coupon, referenceDate=referenceDate, rating=rating, R=R) myLad = BootstrapperCDSLadder(start=self.start_date, freq=[freq], CDSList=[CDSClass], R=CDSClass.R).getXList(x0Vas)[freq] self.Q1M = MC_Vasicek_Sim( x=myLad, t_step=1 / 365, datelist=[CDSClass.referenceDate, CDSClass.end_date], simNumber=simNumber).getLibor()[0] print(self.Q1M) else: myQ = CorporateRates() myQ.getCorporatesFred(trim_start=referenceDate, trim_end=end_date) ## Return the calculated Q(t,t_i) for bonds ranging over maturities for a given rating daterange = pd.date_range(start=referenceDate, end=end_date).date myQ = myQ.getCorporateQData(rating=rating, datelist=daterange, R=R) Q1M = myQ[freq] print(Q1M) return (Q1M) def Q_lhp(self, t, K1, K2, R, beta, Q): """Calculates the Tranche survival curve for the LHP model. Args: T (datetime.date): Should be given in "days" (no hours, minutes, etc.) K1 (float): The starting value of the tranche. Its value should be between 0 & 1. K2 (float): The final value of the tranche. beta (float): Correlation perameter. Its value should be between 0 and 1. R (float): Recovery rate. Usually between 0 and 1. Q (callable function): A function Q that takes in a dateime.date and outputs a float from 0 to 1. It is assumed that Q(0) = 1 and Q is decreasing. Represents survival curve of each credit. """ if Q(t) == 1: return 1 # prevents infinity def emin(K): # Calculates E[min(L(T), K)] in LHP model C = norm.ppf(1 - Q(t)) A = (1 / beta) * (C - sqrt(1 - beta * beta) * norm.ppf(K / (1 - R))) return (1 - R) * mvn.mvndst( upper=[C, -1 * A], lower=[0, 0], infin=[0, 0], # set lower bounds = -infty correl=-1 * beta)[1] + K * norm.cdf(A) return 1 - (emin(K2) - emin(K1)) / (K2 - K1) def Q_gauss(self, t, K1, K2, Fs, Rs, betas, Qs): """ Calculate the tranche survival probability in the Gaussian heterogenous model. Arguments: t (float): Time. Positive. K1 (float): Starting tranche value. Between 0 and 1. K2 (float): Ending tranche value. Between 0 and 1. Fs (list): List of fractional face values for each credit. Each entry must be between 0 and 1. Rs (list): List of recovery rates for each credit. Each entry must be between 0 and 1. betas (list): Correlation perameters for each credit. Each entry must be between 0 and 1. Qs (list): Survival curves for each credit. Each entry must be a callable function that takes in a datetime.date argument and returns a number from 0 to 1. """ Cs = [norm.ppf(1 - q(t)) for q in Qs] N = len(Fs) def f(K, z): ps = [ norm.cdf((C - beta * z) / sqrt(1 - beta**2)) for C, beta in zip(Cs, betas) ] mu = 1 / N * sum([p * F * (1 - R) for p, F, R in zip(ps, Fs, Rs)]) sigma_squared = 1 / N / N * sum([ p * (1 - p) * F**2 * (1 - R)**2 for p, F, R in zip(ps, Fs, Rs) ]) sigma = sqrt(sigma_squared) return -1 * sigma * norm.pdf( (mu - K) / sigma) - (mu - K) * norm.cdf((mu - K) / sigma) emin = lambda K: quad(lambda z: norm.pdf(z) * f(K, z), -10, 10)[0] return 1 - (emin(K2) - emin(K1)) / (K2 - K1) def Q_adjbinom(self, t, K1, K2, Fs, Rs, betas, Qs): """ Calculates the tranche survival probability under the adjusted binomial model. Arguments: t (datetime.date): Time. K1 (float): Starting tranche value (0 to 1). K2 (float): Final tranche value (0 to 1). Fs (list): List of fractional face values (floats) for each credit. Rs (list): List of recovery rates (floats) for each credit. betas (list): List of correlation perameters Qs (list): List of survival probabilities. These are callable functions that takes in a single datetime.date argument and returns a float. Returns: float: The value of the tranche survival curve. """ if Qs[0](t) == 1: return 1.0 # initial value -- avoids weird nan return N = len(Fs) Cs = [norm.ppf(1 - Q(t)) for Q in Qs] L = sum([(1 - R) * F for R, F in zip(Rs, Fs)]) / N def choose(n, k): # Calculates binomial coeffecient: n choose k. if k == 0 or k == n: return 1 return choose(n - 1, k - 1) + choose(n - 1, k) def g(k, z): ps = [ norm.cdf((C - beta * z) / sqrt(1 - beta * beta)) for C, beta in zip(Cs, betas) ] p_avg = sum([(1 - R) * F / L * p for R, F, p in zip(Rs, Fs, ps)]) / N f = lambda k: choose(N, k) * p_avg**k * (1 - p_avg)**(N - k) vA = p_avg * (1 - p_avg) / N vE = 1 / N / N * sum([((1 - R) * F / L)**2 * p * (1 - p) for R, F, p in zip(Rs, Fs, ps)]) m = p_avg * N l = int(m) u = l + 1 o = (u - m)**2 + ((l - m)**2 - (u - m)**2) * (u - m) alpha = (vE * N + o) / (vA * N + o) if k == l: return f(l) + (1 - alpha) * (u - m) if k == u: return f(u) - (1 - alpha) * (l - m) return alpha * f(k) I = lambda k: quad(lambda z: norm.pdf(z) * g(k, z), -10, 10)[0] emin = lambda K: sum([I(k) * min(L * k, K) for k in range(0, N + 1)]) return 1 - (emin(K2) - emin(K1)) / (K2 - K1) def gcd(self, a, b): if a * b == 0: return max(a, b) if a < b: return self.gcd(a, b % a) return self.gcd(a % b, b) def Q_exact(self, t, K1, K2, Fs, Rs, betas, Qs): Cs = [norm.ppf(1 - Q(t)) for Q in Qs] N = len(Fs) g = round(3600 * Fs[0] * (1 - Rs[0])) for j in range(1, N): g = self.gcd(g, round(3600 * Fs[j] * (1 - Rs[j]))) g = g / 3600 ns = [round(F * (1 - R) / g) for F, R in zip(Fs, Rs)] def f(j, k, z): if (j, k) == (0, 0): return 1.0 if j == 0: return 0.0 ps = [ norm.cdf((C - beta * z) / sqrt(1 - beta**2)) for C, beta in zip(Cs, betas) ] if k < ns[j - 1]: return f(j - 1, k, z) * (1 - ps[j - 1]) return f(j - 1, k, z) * (1 - ps[j - 1]) + f( j - 1, k - ns[j - 1], z) * ps[j - 1] I = [ quad(lambda z: norm.pdf(z) * f(N, k, z), -12, 12)[0] for k in range(sum(ns)) ] emin = lambda K: sum([I[k] * min(k * g, K) for k in range(sum(ns))]) return 1 - (emin(K2) - emin(K1)) / (K2 - K1) def getZ_Vasicek(self): ### Get Z(t,t_i) for t_i in datelist ##### ## Simulate Vasicek model with paramters given in workspace.parameters # xR = [5.0, 0.05, 0.01, 0.05] # kappa = x[0] # theta = x[1] # sigma = x[2] # r0 = x[3] vasicekMC = MC_Vasicek_Sim( datelist=[self.referenceDate, self.end_date], x=xR, simNumber=10, t_step=t_step) self.myZ = vasicekMC.getLibor() self.myZ = self.myZ.loc[:, 0] def getScheduleComplete(self): datelist = self.myScheduler.getSchedule( start=self.start_date, end=self.maturity, freq=self.freq, referencedate=self.referenceDate) ntimes = len(datelist) fullset = list( sorted( list( set(datelist).union([self.referenceDate]).union([ self.start_date ]).union([self.maturity]).union([self.referenceDate])))) return fullset, datelist def getPremiumLegZ(self, myQ): Q1M = myQ # Q1M = self.myQ["QTranche"] fulllist, datelist = self.getScheduleComplete() portfolioScheduleOfCF = fulllist timed = portfolioScheduleOfCF[portfolioScheduleOfCF. index(self.referenceDate):] Q1M = Q1M.loc[timed] zbarPremLeg = self.myZ / self.myZ.loc[self.referenceDate] zbarPremLeg = zbarPremLeg.loc[timed] ## Calculate Q(t_i) + Q(t_(i-1)) Qplus = [] out = 0 for i in range(1, len(Q1M)): out = out + (Q1M[(i - 1)] + Q1M[i]) * float( (timed[i] - timed[i - 1]).days / 365) * zbarPremLeg[i] zbarPremLeg = pd.DataFrame(zbarPremLeg, index=timed) # print("Premium LEg") PVpremiumLeg = out * (1 / 2) # print(PVpremiumLeg) ## Get coupon bond ### print(PVpremiumLeg) return PVpremiumLeg # //////////////// Get Protection leg Z(t_i)( Q(t_(i-1)) - Q(t_i) ) def getProtectionLeg(self, myQ): print("Protection Leg ") Q1M = myQ #Qminus = np.gradient(Q1M) zbarProtectionLeg = self.myZ out = 0 for i in range(1, zbarProtectionLeg.shape[0]): #out = -Qminus[i] * zbarProtectionLeg.iloc[i]*float(1/365) out = out - (Q1M.iloc[i] - Q1M.iloc[i - 1]) * zbarProtectionLeg.iloc[i] ## Calculate the PV of the premium leg using the bond class print(out) return out def CDOPortfolio(self): self.setParameters(R=self.Rs[0], rating=self.ratings[0], coupon=self.coupons[0], beta=self.betas[0], start_date=start_dates[0], referenceDate=referenceDates[0], end_date=end_dates[0], freq=self.freqs[0]) ## price CDOs using LHP Q_now1 = self.getQ(start_date=self.start_dates[0], referenceDate=self.referenceDates[0], end_date=self.end_dates[0], freq=self.freqs[0], coupon=self.coupons[0], rating=self.ratings[0], R=self.Rs[0]) ## Estimate default probabilites from Qtranche list ### ## Assume that lambda is constant over a small period of time def getApproxDefaultProbs(Qvals, freq, tvalues): t_start = tvalues[0] delay = self.myScheduler.extractDelay(freq) delay_days = ((t_start + delay) - t_start).days ## Estimate constant lambda lam = -(1 / delay_days) * np.log(Qvals[t_start]) Qvals = [ ((Qvals[t_start] * exp(-lam * (t - t_start).days)) / Qvals[t]) for t in tvalues ] return (Qvals) ######################################################## print(Q_now1) ### Create Q dataframe tvalues = Q_now1.index.tolist() Cs = pd.Series(Q_now1, index=tvalues) for cds_num in range(1, len(Fs)): Q_add = self.getQ(start_date=self.start_dates[cds_num], referenceDate=self.referenceDates[cds_num], end_date=self.end_dates[cds_num], freq=self.freqs[cds_num], coupon=self.coupons[cds_num], rating=self.ratings[cds_num], R=self.Rs[cds_num]) Q_add = pd.Series(Q_add, index=tvalues) Cs = pd.concat([Cs, Q_add], axis=1) def expdecay(n): return lambda t: Cs.ix[t, n] ## #Qs = [expdecay(0),expdecay(1)] Qs = [expdecay(n) for n in range(0, Cs.shape[1])] self.getZ_Vasicek() ###### LHP Method ##################################################################### Rs_mean = np.mean(self.Rs) betas_mean = np.mean(betas) lhpcurve = [ self.Q_lhp(t, self.K1, self.K2, R=Rs[0], beta=betas[0], Q=Qs[0]) for t in tvalues ] lhpcurve = pd.Series(lhpcurve, index=tvalues) lhpcurve = getApproxDefaultProbs(lhpcurve, freq=self.freq, tvalues=tvalues) lhpcurve = pd.Series(lhpcurve, index=tvalues) ProtectionLeg = self.getProtectionLeg(myQ=lhpcurve) PremiumLeg = self.getPremiumLegZ(myQ=lhpcurve) spreads = ProtectionLeg / PremiumLeg print("The spread for LHP is: ", 10000 * spreads, ".") ######################################################################################## ###### Gaussian Method ##################################################################### print('Gaussian progression: ', end="") gaussiancurve = [ self.Q_gauss(t, self.K1, self.K2, Fs=self.Fs, Rs=self.Rs, betas=self.betas, Qs=Qs) for t in tvalues ] gaussiancurve = pd.Series(gaussiancurve, index=tvalues) gaussiancurve = getApproxDefaultProbs(gaussiancurve, freq=self.freq, tvalues=tvalues) gaussiancurve = pd.Series(gaussiancurve, index=tvalues) ProtectionLeg = self.getProtectionLeg(myQ=gaussiancurve) PremiumLeg = self.getPremiumLegZ(myQ=gaussiancurve) spreads = ProtectionLeg / PremiumLeg print("The spread for Gaussian is: ", 10000 * spreads, ".") ######################################################################################## ###### Adjusted Binomial Method ##################################################################### adjustedbinomialcurve = [ self.Q_adjbinom(t, self.K1, self.K2, Fs=self.Fs, Rs=self.Rs, betas=self.betas, Qs=Qs) for t in tvalues ] adjustedbinomialcurve = pd.Series(adjustedbinomialcurve, index=tvalues) adjustedbinomialcurve = getApproxDefaultProbs(adjustedbinomialcurve, freq=self.freq, tvalues=tvalues) adjustedbinomialcurve = pd.Series(adjustedbinomialcurve, index=tvalues) #adjustedbinomialcurve = adjustedbinomialcurve.to_frame(self.freqs[0]) ProtectionLeg = self.getProtectionLeg(myQ=adjustedbinomialcurve) PremiumLeg = self.getPremiumLegZ(myQ=adjustedbinomialcurve) spreads = ProtectionLeg / PremiumLeg print("The spread for Ajusted Binomial is: ", 10000 * spreads, ".") ######################################################################################## ###### Exact Method ##################################################################### exactcurve = [ self.Q_exact(t, self.K1, self.K2, Fs=self.Fs, Rs=self.Rs, betas=self.betas, Qs=Qs) for t in tvalues ] exactcurve = pd.Series(exactcurve, index=tvalues) exactcurve = getApproxDefaultProbs(exactcurve, freq=self.freq, tvalues=tvalues) exactcurve = pd.Series(exactcurve, index=tvalues) #exactcurve = exactcurve.to_frame(self.freqs[0]) ProtectionLeg = self.getProtectionLeg(myQ=exactcurve) PremiumLeg = self.getPremiumLegZ(myQ=exactcurve) spreads = ProtectionLeg / PremiumLeg print("The spread for Exact is: ", 10000 * spreads, ".")
myScheduler = Scheduler() ReferenceDateList = myScheduler.getSchedule(start=referenceDate, end=trim_end, freq="1M", referencedate=referenceDate) # Create Simulator xOIS = [3.0, 0.07536509, -0.208477, 0.07536509] myVasicek = MC_Vasicek_Sim(datelist=[trim_start, trim_end], x=xOIS, simNumber=simNumber, t_step=1 / 365.0) #myVasicek.setVasicek(x=xOIS,minDay=trim_start,maxDay=trim_end,simNumber=simNumber,t_step=1/365.0) myVasicek.getLibor() # Create Coupon Bond with several startDates. SixMonthDelay = myScheduler.extractDelay("6M") TwoYearsDelay = myScheduler.extractDelay("2Y") startDates = [ referenceDate + nprnd.randint(0, 3) * SixMonthDelay for r in range(10) ] # For debugging uncomment this to choose a single date for the forward bond # print(startDates) startDates = [ date(2005, 3, 10) + SixMonthDelay, date(2005, 3, 10) + TwoYearsDelay ] maturities = [(x + TwoYearsDelay) for x in startDates] myPortfolio = {} coupon = 0.07536509
class CDS(object): def __init__(self, start_date, end_date, freq, coupon, referenceDate, rating, R=.4): ### Parameters passed to external functions self.start_date = start_date self.end_date = end_date self.freq = freq self.R = R self.notional = 1 self.fee = fee self.rating = rating self.coupon = coupon self.referenceDate = referenceDate self.observationDate = referenceDate ## Get datelist ## self.myScheduler = Scheduler() # ReferenceDateList = self.myScheduler.getSchedule(start=referenceDate,end=end_date,freq=freq, referencedate=referenceDate) ## Delay start and maturity delay = self.myScheduler.extractDelay(self.freq) cashFlowDays = self.myScheduler.extractDelay("3M") ## Delay maturity and start date # self.start_date = self.start_date +SixMonthDelay # self.maturity =self.start_date + delay self.maturity = self.start_date + delay fulllist, datelist = self.getScheduleComplete() self.datelist = datelist self.portfolioScheduleOfCF = fulllist self.myZ = None self.myQ = None # ////////////////////////////////////////////////////////////////////////////////////////////# # ////////////////////////////////////////////////////////////////////////////////////////////# # ////////////////////////////// SET FUNCTIONS ///////////////////////////////////////////////# # ////////////////////////////////////////////////////////////////////////////////////////////# # ////////////////////////////////////////////////////////////////////////////////////////////# def setZ(self, Zin): ## Zin needs to be a pandas dataframe self.myZ = Zin def setQ(self, Qin): self.myQ = Qin # ////////////////////////////////////////////////////////////////////////////////////////////# # ////////////////////////////////////////////////////////////////////////////////////////////# # ////////////////////////////// GET FUNCTIONS ///////////////////////////////////////////////# # ////////////////////////////////////////////////////////////////////////////////////////////# # ////////////////////////////////////////////////////////////////////////////////////////////# # //////////////// Get Vasicek simulated Z(t,t_j) def getZ_Vasicek(self): ### Get Z(t,t_i) for t_i in datelist ##### ## Simulate Vasicek model with paramters given in workspace.parameters # xR = [5.0, 0.05, 0.01, 0.05] # kappa = x[0] # theta = x[1] # sigma = x[2] # r0 = x[3] vasicekMC = MC_Vasicek_Sim( datelist=[self.referenceDate, self.maturity], x=xR, simNumber=100, t_step=t_step) self.myZ = vasicekMC.getLibor() self.myLibor = self.myZ self.myZ = self.myZ.loc[:, 0] ## get Libor Z for reference dates ## # self.myZ = vasicekMC.getSmallLibor(datelist= self.portfolioScheduleOfCF).loc[:,0] # //////////////// Get Corporates simulated Q(t,t_j) def getQ_Corporate(self): ## Use CorporateDaily to get Q for referencedates ## # print("GET Q") # print(self.portfolioScheduleOfCF) myQ = CorporateRates() myQ.getCorporatesFred(trim_start=self.referenceDate, trim_end=self.end_date) ## Return the calculated Q(t,t_i) for bonds ranging over maturities for a given rating daterange = pd.date_range(start=self.referenceDate, end=self.maturity).date self.myQ = myQ.getCorporateQData(rating=self.rating, datelist=daterange, R=self.R) return (self.myQ) # //////////////// Get Premium leg Z(t_i)( Q(t_(i-1)) + Q(t_i) ) def getPremiumLegZ(self): ## Check if Z and Q exists if self.myZ is None: self.getZ_Vasicek() if self.myQ is None: self.getQ_Corporate() ## Choose 1month Q ''' Q1M = self.myQ[self.freq] # Q1M = self.myQ["QTranche"] timed = self.portfolioScheduleOfCF[self.portfolioScheduleOfCF.index(self.referenceDate):] Q1M = Q1M.loc[timed] Q1M = Q1M.cumprod(axis=0) zbarPremLeg = self.myZ / self.myZ.loc[self.referenceDate] zbarPremLeg = zbarPremLeg.loc[timed] ## Calculate Q(t_i) + Q(t_(i-1)) out = 0 for i in range(1, len(Q1M)): out = out + (Q1M[(i - 1)] + Q1M[i]) * float((timed[i] - timed[i - 1]).days / 365) * zbarPremLeg[i] ## Calculate the PV of the premium leg using the bond class # zbarPremLeg = zbarPremLeg.cumsum(axis=0) zbarPremLeg = pd.DataFrame(zbarPremLeg, index=timed) # print("Premium LEg") PVpremiumLeg = out * (1 / 2) # print(PVpremiumLeg) ## Get coupon bond ### return PVpremiumLeg ''' Q1M = self.myQ[self.freq] # Q1M = self.myQ["QTranche"] timed = self.portfolioScheduleOfCF[self.portfolioScheduleOfCF. index(self.referenceDate):] Q1M = Q1M.loc[timed] Q1M = Q1M.cumprod() zbarPremLeg = self.myZ / self.myZ.loc[self.referenceDate] zbarPremLeg = zbarPremLeg.loc[timed] ## Calculate Q(t_i) + Q(t_(i-1)) Qplus = [] out = 0 for i in range(1, len(Q1M)): out = out + (Q1M[(i - 1)] + Q1M[i]) * float( (timed[i] - timed[i - 1]).days / 365) * zbarPremLeg[i] ## Calculate the PV of the premium leg using the bond class # zbarPremLeg = zbarPremLeg.cumsum(axis=0) zbarPremLeg = pd.DataFrame(zbarPremLeg, index=timed) # print("Premium LEg") PVpremiumLeg = out * (1 / 2) # print(PVpremiumLeg) ## Get coupon bond ### return PVpremiumLeg # //////////////// Get Protection leg Z(t_i)( Q(t_(i-1)) - Q(t_i) ) def getProtectionLeg(self): if self.myZ is None: self.getZ_Vasicek() if self.myQ is None: self.getQ_Corporate() # Q1M = self.myQ["QTranche"] ## Calculate Q(t_i) + Q(t_(i-1)) # Qminus = np.gradient(np.array(Q1M)) # print(Qminus) # QArray = np.array(Q1M) # QArray = np.insert(QArray, obj = 0, values = 1) # print(QArray) # Q1M = self.myQ["QTranche"] ''' Q1M = self.myQ[self.freq] Q1M = Q1M.cumprod() timed = Q1M.index.tolist() timed = self.portfolioScheduleOfCF[self.portfolioScheduleOfCF.index(self.referenceDate):] Q1M = Q1M.loc[timed] zbarPremLeg = self.myZ / self.myZ.loc[self.referenceDate] zbarPremLeg = zbarPremLeg.loc[timed] ## Calculate Q(t_i) + Q(t_(i-1)) Qplus = [] out = 0 for i in range(1, len(Q1M)): out = out + (Q1M[(i-1)] - Q1M[(i)]) * float((timed[i] - timed[i - 1]).days / 365) * zbarPremLeg[i] return(out) ## Calculate Z Bar ## Qminus = np.gradient(Q1M) zbarProtectionLeg = self.myZ / self.myZ.loc[self.referenceDate] for i in range(1,zbarProtectionLeg.shape[0]): zbarProtectionLeg.iloc[i] = -Qminus[i] * zbarProtectionLeg.iloc[i] * (1/365) ## Calculate the PV of the premium leg using the bond class zbarProtectionLeg = zbarProtectionLeg.cumsum(axis=0) zbarProtectionLeg = pd.DataFrame(zbarProtectionLeg, index=Q1M.index) PVprotectionLeg = (1 - self.R) * zbarProtectionLeg ## Get coupon bond ### return PVprotectionLeg.loc[self.maturity] ''' Q1M = self.myQ[self.freq] Q1M = Q1M.cumprod() Qminus = np.gradient(Q1M) zbarProtectionLeg = self.myZ / self.myZ.loc[self.referenceDate] for i in range(zbarProtectionLeg.shape[0]): zbarProtectionLeg.iloc[ i] = -Qminus[i] * zbarProtectionLeg.iloc[i] * (1 / 365) ## Calculate the PV of the premium leg using the bond class zbarProtectionLeg = zbarProtectionLeg.cumsum(axis=0) zbarProtectionLeg = pd.DataFrame(zbarProtectionLeg, index=Q1M.index) PVprotectionLeg = (1 - self.R) * zbarProtectionLeg ## Get coupon bond ### return PVprotectionLeg.loc[self.maturity] # /////////////////////// Functions to get the exposure sum [delta Z(t,t_j)( Q(t,t_(j-1)) +/- Q(t,t_j) ) # ////// These are copied from Coupon bond def getScheduleComplete(self): self.datelist = self.myScheduler.getSchedule( start=self.start_date, end=self.maturity, freq='3M', referencedate=self.referenceDate) self.ntimes = len(self.datelist) fullset = list( sorted( list( set(self.datelist).union([self.referenceDate]).union([ self.start_date ]).union([self.maturity]).union([self.observationDate])))) return fullset, self.datelist # /////// Get exposure def getExposure(self, referencedate, libor): self.ntrajectories = np.shape(libor)[1] self.ones = np.ones(shape=[self.ntrajectories]) deltaT = np.zeros(self.ntrajectories) if self.ntimes == 0: pdzeros = pd.DataFrame(data=np.zeros([1, self.ntrajectories]), index=[referencedate]) self.pv = pdzeros self.pvAvg = 0.0 self.cashFlows = pdzeros self.cashFlowsAvg = 0.0 return self.pv for i in range(1, self.ntimes): deltaTrow = ((self.datelist[i] - self.datelist[i - 1]).days / 365) * self.ones deltaT = np.vstack((deltaT, deltaTrow)) self.cashFlows = self.coupon * deltaT principal = self.ones if self.ntimes > 1: self.cashFlows[-1:] += principal else: self.cashFlows = self.cashFlows + principal if (self.datelist[0] <= self.start_date): self.cashFlows[0] = -self.fee * self.ones if self.ntimes > 1: self.cashFlowsAvg = self.cashFlows.mean(axis=1) * self.notional else: self.cashFlowsAvg = self.cashFlows.mean() * self.notional pv = self.cashFlows * libor.loc[self.datelist] self.pv = pv.sum(axis=0) * self.notional self.pvAvg = np.average(self.pv) * self.notional return self.pv # ///// Get the calculated Market to Market based on spread def getValue(self, spread=1, R=0, buyer=True): ## Assume V(t) = S/2 sum Z(t_i) (Q(t_i) + Q(t_{i-1})) - (1-R)sum Z(t_i) (Q(t_{i-1}) - Q(t_{i})) ## Premium leg = S/2 sum Z(t_i) (Q(t_i) + Q(t_{i-1})) ## Protection leg =sum Z(t_i) (Q(t_i) + Q(t_{i-1})) premiumLeg = self.getPremiumLegZ() protectionLeg = self.getProtectionLeg() mtm = (spread / 2) * premiumLeg - (1 - R) * protectionLeg if buyer is True: return mtm else: return -mtm def getSpread(self): out = self.getProtectionLeg() / self.getPremiumLegZ() return out.values[0] def changeGuessForSpread(self, x): ''' inputs a x list of guesses for the Vasicek simulator than changes the myQ ''' vasicekMC = MC_Vasicek_Sim( datelist=[self.referenceDate, self.maturity], x=x, simNumber=20, t_step=t_step) self.myQ = vasicekMC.getLibor()[0] self.myQ = pd.DataFrame(self.myQ, index=self.myQ.index) self.myQ.columns = [self.freq] spread = self.bootProtec() / self.bootPrem() return spread.values[0] def bootProtec(self): Q1M = self.myQ[self.freq] Q1M = Q1M.cumprod() Qminus = np.gradient(Q1M) zbarProtectionLeg = self.myZ / self.myZ.loc[self.referenceDate] for i in range(zbarProtectionLeg.shape[0]): zbarProtectionLeg.iloc[ i] = -Qminus[i] * zbarProtectionLeg.iloc[i] * (1 / 365) ## Calculate the PV of the premium leg using the bond class zbarProtectionLeg = zbarProtectionLeg.cumsum(axis=0) zbarProtectionLeg = pd.DataFrame(zbarProtectionLeg, index=Q1M.index) PVprotectionLeg = (1 - self.R) * zbarProtectionLeg ## Get coupon bond ### return PVprotectionLeg.loc[self.maturity] def bootPrem(self): Q1M = self.myQ[self.freq] # Q1M = self.myQ["QTranche"] timed = self.portfolioScheduleOfCF[self.portfolioScheduleOfCF. index(self.referenceDate):] Q1M = Q1M.loc[timed] Q1M = Q1M.cumprod() zbarPremLeg = self.myZ / self.myZ.loc[self.referenceDate] zbarPremLeg = zbarPremLeg.loc[timed] ## Calculate Q(t_i) + Q(t_(i-1)) Qplus = [] out = 0 for i in range(1, len(Q1M)): out = out + (Q1M[(i - 1)] + Q1M[i]) * float( (timed[i] - timed[i - 1]).days / 365) * zbarPremLeg[i] ## Calculate the PV of the premium leg using the bond class # zbarPremLeg = zbarPremLeg.cumsum(axis=0) zbarPremLeg = pd.DataFrame(zbarPremLeg, index=timed) # print("Premium LEg") PVpremiumLeg = out * (1 / 2) # print(PVpremiumLeg) ## Get coupon bond ### return PVpremiumLeg
from Scheduler.Scheduler import Scheduler import matplotlib.pyplot as plt import pandas as pd from datetime import date # dr(t) = k(θ − r(t))dt + σdW(t) # self.kappa = x[0] # self.theta = x[1] # self.sigma = x[2] # self.r0 = x[3] myScheduler = Scheduler() observationdate = minDay = date(2005, 1, 10) # Observation Date maxDay = date(2010, 1, 10) # Last Date of the Portfolio start = date(2005, 3, 30) maturity = start + myScheduler.extractDelay("1Y") referenceDate = date(2005, 5, 3) # 6 months after trim_start simNumber = 5 R = 0.4 inArrears = True freq = '3M' t_step = 1.0 / 365 simNumber = 5 coupon = 0.08 fee = 1.0 xR = [3.0, 0.05, 0.04, 0.03] myBond = CouponBond(fee=fee, start=start, maturity=maturity,
class CorporateRates(object): def __init__(self): self.OIS = [] self.filename = WORKING_DIR + '/CorpData.dat' self.corporates = [] self.ratings = ['AAA', 'AA', 'A', 'BBB', 'BB', 'B', 'CCC'] self.corpSpreads = {} self.corporates = pd.DataFrame() self.tenors = [] self.unPickleMe(file=self.filename) self.myScheduler=Scheduler() def getCorporates(self, trim_start, trim_end): curr_trim_end=trim_start if(self.corporates.size!=0): self.trim_start = self.corporates['OIS'].index.min().date() curr_trim_end = self.corporates['OIS'].index.max().date() if trim_end<=curr_trim_end: self.trim_end = curr_trim_end return self.corporates self.trim_start = trim_start self.trim_end = trim_end self.OIS = OIS(trim_start=trim_start, trim_end=trim_end) self.datesAll = self.OIS.datesAll self.datesAll.columns= [x.upper() for x in self.datesAll.columns] self.OISData = self.OIS.getOIS() for i in np.arange(len(self.OISData.columns)): freq = self.OISData.columns[i] self.tenors.append(self.myScheduler.extractDelay(freq=freq)) for rating in self.ratings: index = 'ML/' + rating + 'TRI' try: corpSpreads = 1e-4 * ( quandl.get(index, authtoken="Lqsxas8ieaKqpztgYHxk", trim_start=trim_start, trim_end=trim_end)) corpSpreads.reset_index(level=0, inplace=True) corpSpreads = pd.merge(left=self.datesAll, right=corpSpreads, how='left') corpSpreads = corpSpreads.fillna(method='ffill').fillna(method='bfill') self.corpSpreads[rating] = corpSpreads.T.fillna(method='ffill').fillna(method='bfill').T except: print(index, " not found") self.corpSpreads = pd.Panel.from_dict(self.corpSpreads) self.corporates = {} self.OISData.drop('DATE', axis=1, inplace=True) ntenors = np.shape(self.OISData)[1] for rating in self.ratings: try: tiledCorps = np.tile(self.corpSpreads[rating]['VALUE'], ntenors).reshape(np.shape(self.OISData)) self.corporates[rating] = pd.DataFrame(data=(tiledCorps + self.OISData.values), index=self.OISData.index, columns=self.OISData.columns) except: print("Error in addition of Corp Spreads") self.corporates['OIS'] = self.OISData self.corporates = pd.Panel(self.corporates) return self.corporates def getOISData(self, datelist=[]): if (len(datelist) != 0): return self.corporates["OIS"].loc[datelist] else: return self.self.corporates["OIS"] def getCorporateData(self, rating, datelist=[]): if (len(datelist) != 0): return self.corporates[rating].loc[datelist] else: return self.corporates[rating] def pickleMe(self): data = [self.corporates, self.corpSpreads] with open(self.filename, "wb") as f: pickle.dump(len(data), f) for value in data: pickle.dump(value, f) def unPickleMe(self, file): data = [] if (os.path.exists(file)): with open(file, "rb") as f: for _ in range(pickle.load(f)): data.append(pickle.load(f)) self.corporates = data[0] self.corpSpreads = data[1] def saveMeExcel(self, whichdata, fileName): try: df = pd.DataFrame(whichdata) except: df = whichdata df.to_excel(fileName)
class CorporateRates(object): def __init__(self): self.OIS = [] self.filename = WORKING_DIR + '/CorpData.dat' self.corporates = [] self.ratings = { 'AAA': "BAMLC0A1CAAA", 'AA': "BAMLC0A2CAA", 'A': "BAMLC0A3CA", 'BBB': "BAMLC0A4CBBB", 'BB': "BAMLH0A1HYBB", 'B': "BAMLH0A2HYB", 'CCC': "BAMLH0A3HYC" } self.corpSpreads = {} self.corporates = pd.DataFrame() self.Qcorporates = pd.DataFrame() # survival function for corporates self.tenors = [] self.unPickleMe(file=self.filename) self.myScheduler = Scheduler() self.myVasicek = MC_Vasicek_Sim() self.R = 0.4 def getCorporatesFred(self, trim_start, trim_end): fred = Fred(api_key=FRED_API_KEY) curr_trim_end = trim_start if (self.corporates.size != 0): self.trim_start = self.corporates['OIS'].index.min().date() curr_trim_end = self.corporates['OIS'].index.max().date() if trim_end <= curr_trim_end: self.trim_end = curr_trim_end return self.corporates self.trim_start = trim_start self.trim_end = trim_end self.OIS = OIS(trim_start=trim_start, trim_end=trim_end) self.datesAll = self.OIS.datesAll self.datesAll.columns = [x.upper() for x in self.datesAll.columns] self.datesAll.index = self.datesAll.DATE self.OISData = self.OIS.getOIS() for i in np.arange(len(self.OISData.columns)): freq = self.OISData.columns[i] self.tenors.append(self.myScheduler.extractDelay(freq=freq)) for rating in self.ratings.keys(): index = self.ratings[rating] try: corpSpreads = 1e-2 * (fred.get_series( index, observation_start=trim_start, observation_end=trim_end).to_frame()) corpSpreads.index = [x.date() for x in corpSpreads.index[:]] corpSpreads = pd.merge(left=self.datesAll, right=corpSpreads, left_index=True, right_index=True, how="left") corpSpreads = corpSpreads.fillna(method='ffill').fillna( method='bfill') corpSpreads = corpSpreads.drop("DATE", axis=1) self.corpSpreads[rating] = corpSpreads.T.fillna( method='ffill').fillna(method='bfill').T except Exception as e: print(e) print(index, " not found") self.corpSpreads = pd.Panel.from_dict(self.corpSpreads) self.corporates = {} self.OISData.drop('DATE', axis=1, inplace=True) ntenors = np.shape(self.OISData)[1] for rating in self.ratings: try: tiledCorps = np.tile(self.corpSpreads[rating][0], ntenors).reshape(np.shape(self.OISData)) self.corporates[rating] = pd.DataFrame( data=(tiledCorps + self.OISData.values), index=self.OISData.index, columns=self.OISData.columns) except: print("Error in addition of Corp Spreads") self.corporates['OIS'] = self.OISData self.corporates = pd.Panel(self.corporates) return self.corporates def getCorporateData(self, rating, datelist=None): # This method gets a curve for a given date or date list for a given rating (normally this will be just a date). # It returns a dict of curves read directly from the corporate rates created by getCorporatesFred. # Derive delays from self.corporates[rating].columns myDelays = self.myScheduler.extractDelay( freq=list(self.corporates[rating].columns)) if datelist is None: return outCurve = {} for day in datelist: # add curve to outcurve dict myCurve = self.corporates.loc[rating, datelist] outCurve[day] = myCurve return outCurve def getCorporateQData(self, rating, datelist=None, R=0.4): self.R = R self.OIS = OIS() # there is a difference in labels between Fred and Quandl... colLabel = [ "1M", "3M", "6M", "1Y", "2Y", "3Y", "5Y", "7Y", "10Y", "20Y", "30Y" ] outCurve = {} if datelist is None: return myCurve = np.ones(11) tempCurve = np.ones(11) outCurve[datelist[0]] = myCurve z = self.OIS.getOIS(datelist) spread = self.getCorporateData(rating, datelist) delta = (datelist[1] - datelist[0]).days / 365 interimSum = np.zeros(11) for day in datelist[1:]: # Create Q curves using q-tilde equation endDate = datelist.slice_indexer(start=day).start for day2 in datelist[endDate - 1:endDate]: if day2 == datelist[0]: qPrev = np.ones(11) else: qPrev = outCurve[day2 - 1] qCurr = outCurve[day2] spread = self.getCorporateData( rating, pd.date_range(start=day, end=day)) interimSum = interimSum + z.loc[day2].values[1:] * ( outCurve[day2] * delta * spread[day].values - (1 - R) * (qPrev - qCurr)) myCurve = (z.loc[day].values[1:] * (1 - R) * outCurve[day - 1] - interimSum) / (z.loc[day].values[1:] * (delta * spread[day] + 1 - R)) tempCurve = np.vstack((tempCurve, myCurve.values.ravel())) outCurve[day] = myCurve.values.ravel() a = 1 tempCurve = pd.DataFrame(data=tempCurve, columns=colLabel, index=datelist.values) return tempCurve def pickleMe(self): data = [self.corporates, self.corpSpreads] with open(self.filename, "wb") as f: pickle.dump(len(data), f) for value in data: pickle.dump(value, f) def unPickleMe(self, file): data = [] if (os.path.exists(file)): with open(file, "rb") as f: for _ in range(pickle.load(f)): data.append(pickle.load(f)) self.corporates = data[0] self.corpSpreads = data[1] def saveMeExcel(self, whichdata, fileName): try: df = pd.DataFrame(whichdata) except: df = whichdata df.to_excel(fileName)