R/CIndex.R

# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.

#' An S4 class to represent a time index for frequency or severity distribution.
#'
#' @slot indexID A string to identify the index.
#' @slot startDate The date the index starts. It is expected to be consistent with the start date of the claim analysis.
#' @slot tabulate A boolean to indicate whether the index is determined by a constant rate (FALSE) or a series of index values (TRUE).
#' @slot annualizedRate A yearly index growth rate. It is only used when tabulate == FALSE.
#' @slot yearlyIndex A vector that contains index value on a yearly basis.
#' @slot monthlyIndex A vector that contains index value on a monthly basis.
#' @slot seasonality A vector that contains seasonal adjustment factor on a monthly basis.

setClass("Index",
  slots = c(
    indexID = "character",
    startDate = "Date",
    tabulate = "logical",
    annualizedRate = "numeric",
    yearlyIndex = "vector",
    monthlyIndex = "vector",
    seasonality = "vector"
  ),
  prototype = list(
    indexID = "XXXXXX",
    startDate = as.Date("2012-01-01"),
    tabulate = FALSE,
    annualizedRate = 0.02,
    yearlyIndex = vector(),
    monthlyIndex = vector(),
    seasonality = rep(1, 12)
  )
)

#' @rdname setID-methods
#' @aliases setID,ANY-method
setReplaceMethod("setID", signature("Index", "character"), function(this, value) {
  this@indexID <- as.character(value)
  this
})

#' @rdname setStartDate-methods
#' @aliases setStartDate,ANY-method
setReplaceMethod("setStartDate", signature("Index", "Date"), function(this, value) {
  this@startDate <- as.Date(value)
  this
})

#' Determine whether the index values are constructed from a constant rate or provided directly
#' @name setTabulate<-
#' @param this Index Object
#' @param ... Additional function arguments
#' @param value Logical Value (default:FALSE)
#' @examples
#' xindex <- new("Index")
#' setID(xindex) <- "IDX1"
#' setTabulate(xindex) <- FALSE
#' setAnnualizedRate(xindex) <- 0.03
#' xindex <- setIndex(xindex)
#' xindex@monthlyIndex
#' @rdname setTabulate-methods
#' @exportMethod setTabulate<-
setGeneric("setTabulate<-", function(this, ..., value) standardGeneric("setTabulate<-"))
#' @rdname setTabulate-methods
#' @aliases setTabulate,ANY-method
setReplaceMethod("setTabulate", signature("Index", "logical"), function(this, value) {
  this@tabulate <- value
  this
})

#' Set the annualized level rate to construct the index.
#' Only used when tabulate == FALSE.
#' @name setAnnualizedRate<-
#' @param this Index Object
#' @param ... Additional function arguments
#' @param value Numeric Value (default:0.02)
#' @examples
#' xindex <- new("Index")
#' setID(xindex) <- "IDX1"
#' setTabulate(xindex) <- FALSE
#' setAnnualizedRate(xindex) <- 0.03
#' xindex <- setIndex(xindex)
#' xindex@monthlyIndex
#' @rdname setAnnualizedRate-methods
#' @exportMethod setAnnualizedRate<-
setGeneric("setAnnualizedRate<-", function(this, ..., value) standardGeneric("setAnnualizedRate<-"))
#' @rdname setAnnualizedRate-methods
#' @aliases setAnnualizedRate,ANY-method
setReplaceMethod("setAnnualizedRate", signature("Index", "numeric"), function(this, value) {
  this@annualizedRate <- value
  this
})

#' @title Set yearly index values.
#' @description
#' \code{setYearlyIndex<-} sets yearly index values. Monthly index will be constructed assuming constant growth rate during a year.
#' @name setYearlyIndex<-
#' @param this Index Object
#' @param ... Additional function arguments
#' @param value Numeric Vector
#' @examples
#' xindex <- new("Index")
#' setID(xindex) <- "IDX1"
#' setTabulate(xindex) <- TRUE
#' setYearlyIndex(xindex) <- c(1, 1.05, 1.2, 0.95, 1.3)
#' xindex@yearlyIndex
#' @rdname setYearlyIndex-methods
#' @exportMethod setYearlyIndex<-
setGeneric("setYearlyIndex<-", function(this, ..., value) standardGeneric("setYearlyIndex<-"))
#' @rdname setYearlyIndex-methods
#' @aliases setYearlyIndex,ANY-method
setReplaceMethod("setYearlyIndex", signature("Index", "vector"), function(this, value) {
  this@yearlyIndex <- value
  this@tabulate <- TRUE
  this
})

#' @title Set monthly index values.
#' @description
#' \code{setMonthlyIndex<-} sets monthly index values.
#' @name setMonthlyIndex<-
#' @param this Index Object
#' @param ... Additional function arguments
#' @param value Numeric Vector
#' @examples
#' xindex <- new("Index")
#' setID(xindex) <- "IDX1"
#' setTabulate(xindex) <- TRUE
#' setMonthlyIndex(xindex) <- rep(1, 360)
#' xindex <- setIndex(xindex)
#' xindex@monthlyIndex
#' @rdname setMonthlyIndex-methods
#' @exportMethod setMonthlyIndex<-
setGeneric("setMonthlyIndex<-", function(this, ..., value) standardGeneric("setMonthlyIndex<-"))
#' @rdname setMonthlyIndex-methods
#' @aliases setMonthlyIndex,ANY-method
setReplaceMethod("setMonthlyIndex", signature("Index", "vector"), function(this, value) {
  this@monthlyIndex <- value
  this@tabulate <- TRUE
  this
})

#' @title Set seasonality on a monthly basis.
#' @description
#' \code{setSeasonality<-} sets monthly multiplier to reflect seasonal impact.
#' @name setSeasonality<-
#' @param this Index Object
#' @param ... Additional function arguments
#' @param value Numeric Vector (default:rep(1,12))
#' @examples
#' xindex <- new("Index")
#' setID(xindex) <- "IDX1"
#' setTabulate(xindex) <- TRUE
#' setAnnualizedRate(xindex) <- 0.03
#' setYearlyIndex(xindex) <- c(1, 1.05, 1.2, 0.95, 1.3)
#' set.seed(123)
#' setSeasonality(xindex) <- rnorm(12, mean = 1, sd = 0.03)
#' xindex <- setIndex(xindex)
#' xindex@monthlyIndex
#' @rdname setSeasonality-methods
#' @exportMethod setSeasonality<-
setGeneric("setSeasonality<-", function(this, ..., value) standardGeneric("setSeasonality<-"))
#' @rdname setSeasonality-methods
#' @aliases setSeasonality,ANY-method
setReplaceMethod("setSeasonality", signature("Index", "vector"), function(this, value) {
  this@seasonality <- value
  this
})

#' @title Set up a time index for frequency or severity.
#' @description
#' \code{setIndex} sets a time index to reflect inflation, underwriting cycle or seasonality.
#' @name setIndex
#' @param object Index Object
#' @param ... Additional function arguments
#' @examples
#' xindex <- new("Index", indexID = "IDX1", tabulate = FALSE, annualizedRate = 0.03)
#' xindex <- setIndex(xindex)
#' xindex@monthlyIndex
#'
#' xindex <- new("Index")
#' setID(xindex) <- "IDX1"
#' setTabulate(xindex) <- TRUE
#' setAnnualizedRate(xindex) <- 0.03
#' setYearlyIndex(xindex) <- c(1, 1.05, 1.2, 0.95, 1.3)
#' set.seed(123)
#' setSeasonality(xindex) <- rnorm(12, mean = 1, sd = 0.03)
#' xindex <- setIndex(xindex)
#' xindex@monthlyIndex
#' @rdname setIndex-methods
#' @exportMethod setIndex
setGeneric("setIndex", function(object, ...) standardGeneric("setIndex"))
#' @rdname setIndex-methods
#' @aliases setIndex,ANY-method
setMethod("setIndex", signature("Index"), function(object) {
  tryCatch(
    {
      yearlylen <- length(object@yearlyIndex)
      if (object@tabulate == FALSE) {
        object@monthlyIndex <- cumprod(c(1, rep((1 + object@annualizedRate)^(1 / 12), 359)))
        for (i in c(1:length(object@monthlyIndex))) {
          mth <- i %% 12
          if (mth == 0) {
            mth <- 12
          }
          object@monthlyIndex[i] <- object@monthlyIndex[i] * object@seasonality[mth]
        }
      } else if (length(object@monthlyIndex) == 0 && yearlylen > 0) {
        if (yearlylen < 30) {
          warning(paste0("Index ", object@indexID, ": yearly index input is less than 30 years and is extrapolated using annualized rate."))
          object@yearlyIndex <- c(object@yearlyIndex, cumprod(c(object@yearlyIndex[yearlylen] * (1 + object@annualizedRate), rep(1 + object@annualizedRate, 30 - yearlylen - 1))))
        }

        if (sum(object@yearlyIndex < 0) > 0) {
          stop("yearlyIndex cannot be negative.")
        }

        object@monthlyIndex <- rep(1, 360)
        object@monthlyIndex[1] <- object@yearlyIndex[1]
        for (i in c(2:length(object@monthlyIndex))) {
          yr <- ceiling(i / 12)
          rte <- (object@yearlyIndex[yr] / object@yearlyIndex[max(1, yr - 1)])^(1 / 12)
          # print(rte)
          object@monthlyIndex[i] <- object@monthlyIndex[i - 1] * rte
        }
        for (i in c(1:length(object@monthlyIndex))) {
          mth <- i %% 12
          if (mth == 0) {
            mth <- 12
          }
          object@monthlyIndex[i] <- object@monthlyIndex[i] * object@seasonality[mth]
        }
      } else if (length(object@monthlyIndex) == 0) {
        warning(paste0("Index ", object@indexID, ": No index value provided. Index value will be set to 1 with seasonal adjustment."))
        object@monthlyIndex <- rep(1, 360)
        for (i in c(1:length(object@monthlyIndex))) {
          mth <- i %% 12
          if (mth == 0) {
            mth <- 12
          }
          object@monthlyIndex[i] <- object@monthlyIndex[i] * object@seasonality[mth]
        }
      }

      if (sum(object@monthlyIndex < 0) > 0) {
        stop("monthlyIndex cannot be negative.")
      }


      gc()
      object
    },
    error = function(err) {
      message(paste0(">>>Critical Error for ", "Index ", object@indexID, ": ", err))
      gc()
      return(-1)
    }
  )
})

#' @title Retrieve index value based on dates.
#' @description
#' \code{getIndex} get a time index to reflect inflation, underwriting cycle or seasonality.
#' @name getIndex
#' @param object Index Object
#' @param ... Additional function arguments
#' @examples
#' xindex <- new("Index", indexID = "IDX1", tabulate = FALSE, annualizedRate = 0.03)
#' xindex <- setIndex(xindex)
#' xindex@monthlyIndex
#' dates <- as.Date("2015-12-31")
#' getIndex(xindex, dates)
#' @rdname getIndex-methods
#' @exportMethod getIndex
setGeneric("getIndex", function(object, ...) standardGeneric("getIndex"))
#' @param dates dates to get index information
#' @rdname getIndex-methods
#' @aliases getIndex,ANY-method
setMethod("getIndex", signature("Index"), function(object, dates) {
  tryCatch(
    {
      years <- as.numeric(substr(as.character(dates), 1, 4))
      months <- as.numeric(substr(as.character(dates), 6, 7))
      startyear <- as.numeric(substr(as.character(object@startDate), 1, 4))
      startmonth <- as.numeric(substr(as.character(object@startDate), 6, 7))
      indices <- pmax(1, pmin(360, (years - startyear) * 12 + (months - startmonth) + 1))
      gc()
      return(object@monthlyIndex[indices])
    },
    error = function(err) {
      message(paste0(">>>Critical Error for getting index values", "Index ", object@indexID, ": ", err))
      gc()
      return(-1)
    }
  )
})

#' @title Shift monthly index with a new start date and replace the unknown index value with zero.
#' @name shiftIndex
#' @param object Index Object
#' @param ... Additional function arguments
#' @examples
#' xindex <- new("Index", indexID = "IDX1", tabulate = FALSE, annualizedRate = 0.03)
#' xindex <- setIndex(xindex)
#' xindex@monthlyIndex
#' shiftIndex(xindex, as.Date("2016-10-15"), as.Date("2018-10-15"))
#' shiftIndex(xindex, as.Date("2010-10-15"), as.Date("2013-10-15"))
#' @rdname shiftIndex-methods
#' @exportMethod shiftIndex
setGeneric("shiftIndex", function(object, ...) standardGeneric("shiftIndex"))
#' @param newStartDate new start date
#' @param endDate end date
#' @rdname shiftIndex-methods
#' @aliases shiftIndex,ANY-method
setMethod("shiftIndex", signature("Index"), function(object, newStartDate, endDate) {
  tryCatch(
    {
      newyear <- as.numeric(substr(as.character(newStartDate), 1, 4))
      newmonth <- as.numeric(substr(as.character(newStartDate), 6, 7))
      endyear <- as.numeric(substr(as.character(endDate), 1, 4))
      endmonth <- as.numeric(substr(as.character(endDate), 6, 7))
      startyear <- as.numeric(substr(as.character(object@startDate), 1, 4))
      startmonth <- as.numeric(substr(as.character(object@startDate), 6, 7))
      sindex <- (newyear - startyear) * 12 + (newmonth - startmonth) + 1
      nlen <- (endyear - newyear) * 12 + (endmonth - newmonth) + 1
      newMI <- c(rep(0, max(0, 1 - sindex)), object@monthlyIndex[max(1, sindex):min(length(object@monthlyIndex), length(object@monthlyIndex) + sindex - 1)])
      if (nlen > length(newMI)) {
        newMI <- c(newMI, rep(0, nlen - length(newMI)))
      } else {
        newMI <- newMI[1:nlen]
      }
      gc()
      return(newMI)
    },
    error = function(err) {
      message(paste0(">>>Critical Error for shifting index values", "Index ", object@indexID, ": ", err))
      gc()
      return(-1)
    }
  )
})

setMethod("toString", signature("Index"), function(x) {
  return(paste("Index ", x@indexID, " start date=", x@startDate, sep = ""))
})
casact/cascsim documentation built on Nov. 12, 2022, 11:53 p.m.