# Bond Lab is a software application for the analysis of
# fixed income securities it provides a suite of applications
# mortgage backed, asset backed securities, and commerical mortgage backed
# securities Copyright (C) 2018 Bond Lab Technologies, Inc.
#' @include ScenarioConstructor.R MortgageCashFlow.R TermStructure.R
#' @include PrepaymentModel.R MortgageKeyRate.R ModelToCPR.R
NULL
#' @title MortgageReturn class
#' @family Mortgage Scenario Analysis
#' @description
#' The class MortgageReturn holds the results of mortgage return analysis.
#' MortgageReturn is the sum of coupon income, principal received, reinvestment
#' income and price appreciation or depreciation. In addition, most investors
#' also track horizon current balance.
#' @slot CouponIncome A numeric value the coupon income received over the
#' investment horizon
#' @slot ScheduledPrinReceived A numeric value the scheduled principal
#' received over the investment horizon
#' @slot PrepaidPrinReceived A numeric value the prepaid principal received
#' over the investment horizon
#' @slot ReinvestmentIncome A numeric value the reivestment income received
#' over the investment horizon
#' @slot HorizonCurrBal A numeric value the current balance at the end of the
#' investment horizon
#' @slot HorizonPrice A numeric the price at the end of the horizon
#' @slot HorizonReturn A numeric value the horizon total return
#' @slot HorizonMos A numeric value the number of months to
#' the scenario horizon date
#' @importFrom lubridate interval
#' @exportClass MortgageReturn
setClass("MortgageReturn",
representation(
CouponIncome = "numeric",
ScheduledPrinReceived = "numeric",
PrepaidPrinReceived = "numeric",
ReinvestmentIncome = "numeric",
HorizonCurrBal = "numeric",
HorizonPrice = "numeric",
HorizonReturn = "numeric",
HorizonMos = "numeric"))
#' @title CouponIncome generic
#' @family Scenario Analysis
#' @description A generic function for method dispatch
#' @param object An object of the type MtgScenario
#' @export
setGeneric("CouponIncome", function(object)
{standardGeneric("CouponIncome")})
#' @title ScheduledPrincipalReceived generic
#' @family Scenario Analysis
#' @description A generic function for method dispatch
#' @param object An object of the type MtgScenario
#' @export
setGeneric("ScheduledPrinReceived", function(object)
{standardGeneric("ScheduledPrinReceived")})
#' @title PrepaidPrinReceived generic
#' @family Scenario Analysis
#' @description A generic function for method dispatch
#' @param object An object of the type MtgScenario
#' @export
setGeneric("PrepaidPrinReceived", function(object)
{standardGeneric("PrepaidPrinReceived")})
#' @title ReinvestmentIncome generic
#' @family Scenario Analysis
#' @description A generic function for method dispatch
#' @param object an object of the type MtgScenario
#' @export
setGeneric("ReinvestmentIncome", function(object)
{standardGeneric("ReinvestmentIncome")})
#' @title HorizonCurrBal generic
#' @family Scenario Analysis
#' @description A generic function for method dispatch
#' @param object An object of the type MtgScenario
#' @export
setGeneric("HorizonCurrBal", function(object)
{standardGeneric("HorizonCurrBal")})
#' @title HorizonPrice generic
#' @family Scenario Analysis
#' @description A generic function for method dispatch
#' @param object an S4 object of the type MtgScenario
#' @export
setGeneric("HorizonPrice", function(object)
{standardGeneric("HorizonPrice")})
#' @title HorizonPrice generic
#' @family Scenario Analysis
#' @description A generic function for method dispatch
#' @param object An object of the type MtgScenario
#' @export
setGeneric("HorizonReturn", function(object)
{standardGeneric("HorizonReturn")})
#' @title HorizonMos generic
#' @family Scenario Analysis
#' @description A generic function for method dispatch
#' @param object An object of the type MtgScenario
#' @export
setGeneric("HorizonMos", function(object)
{standardGeneric("HorizonMos")})
setMethod("initialize",
signature("MortgageReturn"),
function(.Object,
CouponIncome = numeric(),
ScheduledPrinReceived = numeric(),
PrepaidPrinReceived = numeric(),
ReinvestmentIncome = numeric(),
HorizonCurrBal = numeric(),
HorizonPrice = numeric(),
HorizonReturn = numeric(),
HorizonMos = numeric(),
...)
{
callNextMethod(.Object,
CouponIncome = CouponIncome,
ScheduledPrinReceived = ScheduledPrinReceived,
PrepaidPrinReceived = PrepaidPrinReceived,
ReinvestmentIncome =ReinvestmentIncome,
HorizonCurrBal = HorizonCurrBal,
HorizonPrice = HorizonPrice,
HorizonReturn = HorizonReturn,
HorizonMos = HorizonMos,
...)
})
#' @title CouponIncome method, class MortgageReturn
#' @family Mortgage Scenario Analysis
#' @description A method to get the \strong{CouponIncome} paid to the
#' to the investor over the scenrio horizon. CouponIncome is reported as the
#' sum of the coupon income received by the investor.
#' @param object A class of the type MortgageReturn
#' @exportMethod CouponIncome
setMethod("CouponIncome", signature("MortgageReturn"),
function(object){object@CouponIncome})
#' @title ScheduledPrinReceived method, class MortgageReturn
#' @family Mortgage Scenario Analysis
#' @description A method to get the \strong{ScheduledPrinRecieved} paid to the
#' investor over the scenario horizon. ScheduledPrinReceived is reported as
#' the sum of the sceduled principal received over the horizon.
#' @param object An S4 class of type MortgageReturn
#' @exportMethod ScheduledPrinReceived
setMethod("ScheduledPrinReceived", signature("MortgageReturn"),
function(object){object@ScheduledPrinReceived})
#' @title PrepaidPrinReceived method, class MortgageReturn
#' @family Mortgage Scenario Analysis
#' @description A method to get \strong{PrepaidPrinReceived} paid to the
#' investor over the scenario horizon. PrepaidPrinReceived is reported as
#' the sum of the prepaid principal received over the horizon.
#' @param object An S4 class of type MortgageReturn
#' @exportMethod PrepaidPrinReceived
setMethod("PrepaidPrinReceived", signature("MortgageReturn"),
function(object){object@PrepaidPrinReceived})
#' @title ReinvestmentIncome method, class MortgageReturn
#' @family Mortgage Scenario Analysis
#' @description A method to get \strong{ReinvestmentIncome} paid to the
#' investor over the scenario horizon. ReinvesmentIncome is reported as
#' the sum of the reinvestment income received over the horizon.
#' @param object An S4 class of the type MortgageReturn
#' @exportMethod ReinvestmentIncome
setMethod("ReinvestmentIncome", signature("MortgageReturn"),
function(object){object@ReinvestmentIncome})
#' @title HorizonCurrBal method, class MortgageReturn
#' @family Mortgage Scenario Analysis
#' @description A method to get \strong{HorizonCurrBal} the mortgage current
#' balance after principal paydown.
#' @param object An S4 class of the type MortgageReturn
#' @exportMethod HorizonCurrBal
setMethod("HorizonCurrBal", signature("MortgageReturn"),
function(object){object@HorizonCurrBal})
#' @title HorizonPrice method, class MortgageReturn
#' @family Mortgage Scenario Analysis
#' @description A method to get \strong{HorizonPrice} used to calculate
#' mortgage total return.
#' @param object An S4 class of the type MortgageReturn
#' @exportMethod HorizonPrice
setMethod("HorizonPrice", signature("MortgageReturn"),
function(object){object@HorizonPrice})
#' @title HorizonReturn method, class MortgageReturn
#' @family Mortgage Scenario Analysis
#' @description A method to get \strong{HorizonReturn}
#' @param object An S4 class of the type MortgageReturn
#' @exportMethod HorizonReturn
setMethod("HorizonReturn", signature("MortgageReturn"),
function(object){object@HorizonReturn})
#' @title HorizonMos method, class MortgageReturn
#' @family Mortgage Scenario Analysis
#' @description A method to get \strong{HorizonMos}
#' @param object An S4 class of the type MortgageReturn
#' @exportMethod HorizonMos
setMethod("HorizonMos", signature("MortgageReturn"),
function(object){object@HorizonMos})
#' @title MortgageScenario class
#' @family Mortgage Scenario Analysis
#' @description The call MortgageScenario contains the following super classes:
#' TermStructure, PrepaymentAssumption, MortgageCashFlow, MortgageTermStructure
#' MortgageReturn, ModelToCPR. CurveSpreads, and Scenario. MortgageScenario
#' inherits the getters and setter of each of the above super classes.
#' @exportClass MortgageScenario
setClass("MortgageScenario",
representation(),
contains = c("MortgageReturn",
"ModelToCPR",
"CurveSpreads"))
setGeneric("MortgageScenario", function(bond.id ="character",
settlement.date = "character",
rates.data = "character",
price = "character",
original.bal = numeric(),
scenario = "character",
horizon.months = numeric(),
method = "character",
prepayment = "character",
...,
horizon.spot.spread = NULL,
horizon.nominal.spread = NULL,
horizon.OAS = NULL,
horizon.price = NULL,
begin.cpr = NULL,
end.cpr = NULL,
seasoning.period = NULL,
cpr = NULL,
cdr = NULL)
{standardGeneric("MortgageScenario")})
setMethod("initialize",
signature("MortgageScenario"),
function(.Object,
...)
{callNextMethod(.Object,
...)
})
#---------------------------------------------------------
# Scenario Total Return Analysis
# Calls the bond.id and applies the scenario
#---------------------------------------------------------
#' Mortgage Scenario Analysis
#'
#' A function to compute the total return of mortgage pass-throughs MBS
#' @param bond.id A character string referencing an object of type MBSDetails
#' @param settlement.date A charcter string the settlement date
#' @param price A character string in decimal equivalent (.) or 32nds (-)
#' @param original.bal A numeric value the price
#' @param scenario.curves A character string an object of type ScenarioCurves
#' @param prepayment.assumption A character string the prepayment assumption
#' @param ... Optional values when PSA or CPR is used or Yield Curve
#' is used
#' @param horizon.spot.spread A numeric value the horizon zero volatility
#' spread
#' @param horizon.nominal.spread A numeric value the horizon spread
#' @param horizon.OAS A numeric value the horizon option adjusted spread
#' (not currently implemented)
#' @param horizon.price A numeric value the horizon price in decimal form
#' @param begin.cpr A numeric value the beginning CPR value
#' @param end.cpr A numeric value the ending CPR value
#' @param seasoning.period A numeric value the length of the
#' seasoning ramp
#' @param cpr A numeric value the CPR speed
#' @param severity A numeric value the mortgage loss severity assumption
#' @importFrom stats loess
#' @export
MortgageScenario <- function(bond.id,
settlement.date,
price,
original.bal,
scenario.curves,
prepayment.assumption,
...,
horizon.spot.spread = NULL,
horizon.nominal.spread = NULL,
horizon.OAS = NULL,
horizon.price = NULL,
begin.cpr = NULL,
end.cpr = NULL,
seasoning.period = NULL,
cpr = NULL,
severity = 0) {
# Mortgage Scenario analysis is done in two steps
# The first is calculated the expected cash-flows received over the
# investment horizon The second is to "roll" the pass through security
# forward and price the expected future cash-flows
# logical declarations of scenario analysis are horzion pricing methods
# and horizon term structure assumption used shift the sopt curve or shift
# the coupon curve and refit the term structure model
if(is.null(horizon.spot.spread) != TRUE) {
horizon.price.type <- "spot"
} else if(is.null(horizon.nominal.spread) != TRUE) {
horizon.price.type <- "nominal"
} else if(is.null(horizon.OAS) != TRUE) {
horizon.price.type <- "oas"
} else {
horizon.price.type <- "price"
}
bond.id <- bond.id
mbs.factor <- MBSFactor(bond.id)
principal <- original.bal * mbs.factor
Price <- PriceTypes(price)
startcurve <- as.data.frame(StartCurve(scenario.curves), stringsAsFactors = FALSE)
starttermstrc <- StartTermStrc(scenario.curves)
horizoncurve <- as.data.frame(HorizonCurve(scenario.curves), stringsAsFactors = FALSE)
horizontermstrc <- HorizonTermStrc(scenario.curves)
horizonmonths = ScenarioHorizonMos(scenario.curves)
Prepayment <- PrepaymentModel(
bond.id = bond.id,
term.structure = starttermstrc,
PrepaymentAssumption = prepayment.assumption,
begin.cpr = begin.cpr,
end.cpr = end.cpr,
seasoning.period = seasoning.period,
cpr = cpr,
severity = severity)
MortgageCashFlow <- MortgageCashFlow(
bond.id = bond.id,
original.bal = original.bal,
settlement.date = settlement.date,
price = PriceDecimalString(Price),
PrepaymentAssumption = Prepayment)
proceeds <- Accrued(MortgageCashFlow) + (principal * PriceBasis(Price))
# Compute the CurvesSpreads based on the user price and prepayment vector
# given the user's scenario interest rate shift
CurveSpread <- CurveSpreads(rates.data = startcurve,
CashFlow = MortgageCashFlow,
TermStructure = starttermstrc,
proceeds = proceeds)
# Compute life CPR using the base case term structure fit
# stop here refactor ModelToCPR function
LifeCPR <- ModelToCPR(bond.id = bond.id,
settlement.date = settlement.date,
term.structure = starttermstrc,
original.bal = original.bal,
price = PriceDecimalString(Price),
yield = YieldToMaturity(MortgageCashFlow))
# This section of code rolls the MBS pass-though forward in time updating
# factor, current balance, lastpaymentdate, nextpaymentdate, wam and wala the
# first step is to assign bond.id to HorizonMBS object
HorizonMBS <- bond.id
paymentdates <- LastandNextPmtDate(issue.date = IssueDate(HorizonMBS),
dated.date = DatedDate(HorizonMBS),
maturity.date = Maturity(HorizonMBS),
settlement.date = as.character(as.Date(horizoncurve[1,1]),
format = '%m-%d-%Y'),
frequency = Frequency(HorizonMBS),
bond.basis = BondBasis(HorizonMBS))
# Use the projected cashflow to determine the current balance outstanding
# at the end of horizon
SchedPrincipal <- sum(ScheduledPrin(MortgageCashFlow)[1:horizonmonths])
PrepaidPrincipal <- sum(PrepaidPrin(MortgageCashFlow)[1:horizonmonths])
DefaultedPrincipal <- sum(DefaultedPrin(MortgageCashFlow)[1:horizonmonths])
TotalPrincipal <- SchedPrincipal + PrepaidPrincipal + DefaultedPrincipal
# Update the LastPmtDate and NextPmtDate to reflect the end of the horizon
# and compute and update the MBSFactor, CurrentBal, WAM, and WALA
HorizonMBS <- `LastPmtDate<-`(HorizonMBS,
as.character(paymentdates[1], format = '%m-%d-%Y'))
HorizonMBS <- `NextPmtDate<-`(HorizonMBS,
as.character(paymentdates[2], format = '%m-%d-%Y'))
MBSFactor(HorizonMBS) <- ((original.bal * mbs.factor) - TotalPrincipal)/ original.bal
CurrentBal(HorizonMBS) <- CurrentBal(bond.id) - TotalPrincipal
WAM(HorizonMBS) <- WAM(bond.id) - horizonmonths
WALA(HorizonMBS) <- WALA(bond.id) + horizonmonths
HorizonPrepayment <- PrepaymentModel(
bond.id = bond.id,
term.structure = horizontermstrc,
PrepaymentAssumption = prepayment.assumption,
begin.cpr = begin.cpr,
end.cpr = end.cpr,
seasoning.period = seasoning.period,
cpr = cpr,
severity = severity)
HorizonCashFlow <- MortgageCashFlow(
bond.id = HorizonMBS,
original.bal = original.bal,
settlement.date = as.character(as.Date(horizoncurve[1,1]),
format = '%m-%d-%Y'),
price = PriceDecimalString(Price),
PrepaymentAssumption = HorizonPrepayment)
# ========================================================================
# This section begins the calculation of horizon total return
# Cashflow Received + Reinvestment Income + Present Value at Horizon
# ========================================================================
PmtIndex <- which(
abs(as.Date(PmtDate(MortgageCashFlow)) - as.Date(horizoncurve[1,1])) ==
min(abs(as.Date(PmtDate(MortgageCashFlow)) - as.Date(horizoncurve[1,1])))
)[1]
PmtIndex <-
if(as.Date(PmtDate(MortgageCashFlow)[PmtIndex]) > as.Date(horizoncurve[1,1]))
{PmtIndex -1} else {PmtIndex}
#==========================================================================
# From the beginning cashflow calculation get the cashflow recieved by the
# investor. This should be the matrix in the book
#==========================================================================
CashFlowArray <- array(data = 0, dim = c(PmtIndex +2, horizonmonths + 2), dimnames = NULL)
# --------------------------------------------------------------------------
# set days to 01 forces all coupon payment calculations to month year basis
startdate <- paste(substr(startcurve[1,1],1,8),'01', sep ="")
horizonmonths.seq <- as.character(seq(as.Date(startdate), by = 'months',
length.out = horizonmonths + 1),format ='%Y-%m-%d')
coupon.months <- as.character(as.Date(PmtDate(MortgageCashFlow)), format = "%Y-%m-01")
mtgdelay <- interval(as.Date(startdate, format = '%Y-%m-%d'),
as.Date(coupon.months[1], format = '%Y-%m-%d')) %/% months(1)
colindex = NULL
for(col in seq_along(coupon.months)){
loc <- which(as.Date(coupon.months[col]) == as.Date(horizonmonths.seq))
if(length(loc) != 0){colindex <- append(colindex, loc, after = length(colindex))
} else {next()}}
CouponIncome = 0
for(pmtrow in seq_along(colindex)){
CashFlowArray[pmtrow + (mtgdelay -1),colindex[pmtrow]] <- PassThroughInterest(MortgageCashFlow)[pmtrow]
CouponIncome = CouponIncome + CashFlowArray[pmtrow + (mtgdelay -1),colindex[pmtrow]]}
# To price the bond at the horizon one must first determine the scenario cash
# flow in particular on must determine the amount of princial returned, if any,
# over the scenario to adjust the principal outstanding of the investor holdings
# -------------------------------note----------------------------------------
# the bond cash flow engine requires an upgrade to include the slot principal
# paid similar to that of the mortgage cash flow engine. The engine below will
# work for a non callable bond but will not work for sinking, putable or callable
# bonds. This is CashFlowBond problem and relates to the allocation of principal
prin.months <- as.character(as.Date(PmtDate(MortgageCashFlow)), format = "%Y-%m-01")
colindex = NULL
for(col in seq_along(prin.months)){
loc <- which(as.Date(prin.months[col]) == as.Date(horizonmonths.seq))
if(length(loc) != 0){colindex <- append(colindex, loc, after = length(colindex))
} else {next()}}
PrincipalRepaid = 0
ScheduledPrinReceived = 0
PrepaidPrinReceived = 0
RecoveredAmount = 0
for(pmtrow in seq_along(colindex)){
CashFlowArray[PmtIndex + mtgdelay,colindex[pmtrow]] <-
sum(ScheduledPrin(MortgageCashFlow)[pmtrow],
PrepaidPrin(MortgageCashFlow)[pmtrow],
RecoveredAmount(MortgageCashFlow)[pmtrow]) + CashFlowArray[PmtIndex + mtgdelay,colindex[pmtrow]]
ScheduledPrinReceived = ScheduledPrinReceived + ScheduledPrin(MortgageCashFlow)[pmtrow]
PrincipalRepaid = PrincipalRepaid + PrepaidPrin(MortgageCashFlow)[pmtrow]
PrepaidPrinReceived = PrepaidPrinReceived + CashFlowArray[PmtIndex + mtgdelay,colindex[pmtrow]]
RecoveredAmount = RecoveredAmount(MortgageCashFlow)[pmtrow] + RecoveredAmount(MortgageCashFlow)[pmtrow]
}
#CashFlowArray[PmtIndex + mtgdelay] <- sum(CashFlowArray[PmtIndex + 1,])
horizon.principal <- original.bal * MBSFactor(HorizonMBS)
# =========================================================================
# Horizon present value of MBS pass through using spot spread, nominal
# spread or OAS use switch here to compute the horizon present value based
# and on either spot spread, nominal spread, or horizon price. (At this
# time there is no OAS to price module) The functions Horizon.Spot.Value,
# Horizon.Nominal.Value, and Horizon.Price.Value are used to determine the
# present value of the remaining cash flows are the horizon date.
# The switch function determines which function is called based on
# horizon.price.type
# ========================================================================
HorizonPrice <- switch(horizon.price.type,
"spot" = ZVSpreadToPriceMBS(
bond.id = HorizonMBS,
settlement.date = as.character(as.Date(horizoncurve[1,1]),
format = '%m-%d-%Y'),
term.structure = horizontermstrc,
prepayment.assumption = HorizonPrepayment,
ZV.spread = horizon.spot.spread),
"nominal" = SpreadToPriceMBS(
bond.id = HorizonMBS,
rates.data = horizoncurve,
settlement.date = as.character(as.Date(horizoncurve[1,1]),
format = '%m-%d-%Y'),
prepayment.assumption = HorizonPrepayment,
spread = horizon.nominal.spread),
"price" = PriceTypes(horizon.price))
HorizonCashFlow <- MortgageCashFlow(bond.id = HorizonMBS,
original.bal = original.bal,
settlement.date = as.character(as.Date(horizoncurve[1,1]),
format = '%m-%d-%Y'),
price = PriceDecimalString(HorizonPrice),
PrepaymentAssumption = HorizonPrepayment)
HorizonProceeds <- (
(PriceBasis(HorizonPrice) * MBSFactor(HorizonMBS) *original.bal) +
Accrued(HorizonCashFlow)
)
HorizonSpread <- CurveSpreads(
rates.data = horizoncurve,
CashFlow = HorizonCashFlow,
TermStructure = horizontermstrc,
proceeds = HorizonProceeds)
#---------------------------------------------------------------------------
#Allocate reinvestment income to the bond cash flows
#---------------------------------------------------------------------------
reinvestment.rate <- as.numeric(horizoncurve[1,2])/(yield.basis * months.in.year)
#reinvestment.rate <- 0
for(rr in 1:nrow(CashFlowArray-1)){
for(month in 1:horizonmonths + 1){
if(month == 1){CashFlowArray[rr,month] = CashFlowArray[rr,month]
} else {CashFlowArray[rr,month] = CashFlowArray[rr,month] +
CashFlowArray[rr, month-1] *(1 + reinvestment.rate)}
}
}
#---------------------------------------------------------------------------
#Aggregate Cashflows
#---------------------------------------------------------------------------
for(row in 1:nrow(CashFlowArray)){
CashFlowArray[row, ncol(CashFlowArray)] = CashFlowArray[row,ncol(CashFlowArray)-1]
}
#---------------------------------------------------------------------------
#Assign horizon proceeds to array
#---------------------------------------------------------------------------
CashFlowArray[nrow(CashFlowArray), ncol(CashFlowArray)] = HorizonProceeds +
CashFlowArray[nrow(CashFlowArray), ncol(CashFlowArray)]
ReceivedCashFlow <- CouponIncome + PrincipalRepaid
TerminalValue <- sum(CashFlowArray[,ncol(CashFlowArray)])
ReinvestmentIncome <- as.numeric(TerminalValue - ReceivedCashFlow - HorizonProceeds)
# This needs to be changed by adding principal pmt to bond cashflow class
# and bond cashflow engine.
HorizonReturn <- (TerminalValue/proceeds)^(1/(months.in.year/horizonmonths))
HorizonReturn <- (HorizonReturn - 1) * yield.basis
new("MortgageScenario",
CPRLife = CPRLife(LifeCPR),
BenchMark = BenchMark(HorizonSpread),
SpreadToBenchmark = SpreadToBenchmark(HorizonSpread),
SpreadToCurve = SpreadToCurve(HorizonSpread),
ZeroVolSpread = ZeroVolSpread(HorizonSpread),
CouponIncome = CouponIncome,
ScheduledPrinReceived = ScheduledPrinReceived,
PrepaidPrinReceived = PrepaidPrinReceived,
ReinvestmentIncome = ReinvestmentIncome,
HorizonCurrBal = original.bal * MBSFactor(HorizonMBS),
HorizonPrice = PriceDecimal(HorizonPrice),
HorizonReturn = HorizonReturn,
HorizonMos = horizonmonths)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.