R/Payments2Triangle.R

Defines functions Payments2Triangle

Documented in Payments2Triangle

#' Converts Payments to a triangle
#'
#' \code{Payments2Triangle} converts a list of cash flows to a triangle.
#'
#' @param accidentDate Date vector. Reference date for the triangle: accident date or underwriting date depending on the reference
#' @param transactionDate Date vector. Date of transaction.
#' @param cashFlows Numeric vector. Cash flows corresponding to the reference date and made at transaction date.
#' @param years Numeric vector. Years to include in the analysis. By default, all years are taken.
#' @param mode string. The mode of aggregation for triangle. The different possible modes are:
#'   \itemize{
#'   \item{semester: aggregation over semesters for accident period and semesters for development period. Implies evalDate in ("07-01", "01-01")}
#'   \item{quarter: aggregation over quarters for accident period and semesters for development period. Implies evalDate in ("10-01","07-01","04-01", "01-01")}
#'   \item{year: aggregation on yearly step from evalDate for both accident and development period}
#'   \item{yearRef: aggregation on fiscal years for accident periods and a first development period corresponding to the timelapse between beginning of the year and evalDate}}
#' @param evalDate String. Date of evaluation, with format "m-d" (ex: "12-23"). All data after this date for the maximal year will not be considered.
#'
#' @return Triangle as a matrix
#' 
#' @import data.table
#'
#' @examples library(lubridate)
#' @examples library(data.table)
#' @examples origin_claim <- as.Date("2010-01-03")
#' @examples origin_transaction <- as.Date("2010-01-25")
#' @examples dates_claim <- origin_claim %m+% months(0:59)
#' @examples dates_transaction <- origin_transaction %m+% months(0:59)
#' @examples d <- data.table(expand.grid(dates_claim, dates_transaction))
#' @examples colnames(d) <- c("date_claim", "date_transaction")
#' @examples d <- d[date_claim < date_transaction]
#' @examples d$amount <- 1:1
#' @examples Payments2Triangle(d$date_claim, d$date_transaction, d$amount, years = NA, mode = "yearRef", evalDate = "01-01")
#' @examples Payments2Triangle(d$date_claim, d$date_transaction, d$amount, years = 2011:2014, mode = "yearRef", evalDate = "01-01")
#' @examples Payments2Triangle(d$date_claim, d$date_transaction, d$amount, years = NA, mode = "yearRef", evalDate = "04-05")
#' @examples Payments2Triangle(d$date_claim, d$date_transaction, d$amount, years = NA, mode = "year", evalDate = "01-01")
#' @examples Payments2Triangle(d$date_claim, d$date_transaction, d$amount, years = NA, mode = "year", evalDate = "04-05")
#' @examples Payments2Triangle(d$date_claim, d$date_transaction, d$amount, years = NA, mode = "semester", evalDate = "01-01")
#' @examples Payments2Triangle(d$date_claim, d$date_transaction, d$amount, years = NA, mode = "quarter", evalDate = "01-01")

#'
#' @export
Payments2Triangle <- function(accidentDate, transactionDate, cashFlows, years = NA, mode = "year", evalDate = "01-01"){
  
  # Check the validity of the vector parameters
  if(!lubridate::is.Date(accidentDate)){stop("Invalid format for accidentDate. Should be a date type.")}
  if(!lubridate::is.Date(transactionDate)){stop("Invalid format for transactionDate. Should be a date type.")}
  if(!is.numeric(cashFlows)){stop("Invalid format for cashFlows. Should be a numeric type.")}
  n = length(accidentDate)
  m = length(transactionDate)
  l = length(cashFlows)
  if(m != n | m != l | n != l){stop("The three vectors should have the same lenghts.")}

  # Check that no accident date is posterior to a transaction date
  if(!all(accidentDate <= transactionDate)){stop("Some accident date are posterior to the associated transaction date.")}

  # Check the validity of the years vector
  if(is.na(years[1])){years <- min(lubridate::year(accidentDate)):max(lubridate::year(transactionDate)+1)}
  
  # Sort year vector if not in order
  years <- sort(years)
  
  # Verify that year vector is continuous
  if(!all( min(years):max(years) %in% years)){warning("Discontinuous years used")}

  # Check modes and evalDate
  if(!(mode %in% c("year", "quarter", "semester", "yearRef"))){stop("Invalid mode. Should be year, quarter, semester or yearRef.")}
  if(mode == "semester" & !(evalDate %in% c("01-01", "07-01"))){stop("Mode is semester. Please enter a proper evalDate (01-01 or 07-01).")}
  if(mode == "quarter" & !(evalDate %in% c("01-01", "04-01", "07-01", "10-01"))){stop("Mode is quarter Please enter a proper evalDate (01-01, 04-01, 07-01 or 10-01.")}

  # Limit dates
  dateLim <- as.Date(paste0(years, "-", evalDate), format = "%Y-%m-%d")

  # Construction of the table
  data <- data.table::data.table(accident = as.Date(accidentDate),
                                 transaction = as.Date(transactionDate),
                                 cashflows = cashFlows)

  # Removal of data outside bonds
  if(mode == "year"){
    data <- data[accident >= min(dateLim) & accident < max(dateLim) & transaction >= min(dateLim) & transaction < max(dateLim)]
  } else{
    data <- data[accident >= as.Date(paste0(min(years), "-01-01"), format = "%Y-%m-%d") & accident < max(dateLim) & transaction >= as.Date(paste0(min(years), "-01-01"), format = "%Y-%m-%d") & transaction < max(dateLim)]
  }

  # Completion of the table with potential missing dates

  comp <- data.table::data.table(accident =  seq(min(data$accident), max(data$accident), by = 1),
                                 transaction = seq(min(data$accident), max(data$accident), by = 1),
                                 cashflows = 0)

  data <- data.table::rbindlist(list(data, comp))
  
  # Reorder data by accident date
  data.table::setorder(data, "accident")

  # Create accident and development year values for further aggregation
  if(mode == "quarter"){
    data[, c("accidentYear", "developmentYear") := list(paste(lubridate::year(accident), lubridate::quarter(accident, with_year = FALSE), sep = "|"),
                                                        (lubridate::year(transaction) - lubridate::year(accident)) * 4 + lubridate::quarter(transaction, with_year = FALSE) - lubridate::quarter(accident, with_year = FALSE)
                                                        )
                 ]
  } else if(mode == "semester"){
    data[, c("accidentYear", "developmentYear") := list(paste(lubridate::year(accident), lubridate::semester(accident, with_year = FALSE), sep = "|"),
                                                        (lubridate::year(transaction) - lubridate::year(accident)) * 2 + lubridate::semester(transaction, with_year = FALSE) - lubridate::semester(accident, with_year = FALSE)
                                                        )
    ]
  } else if(mode == "yearRef"){
    data[, accidentYear := lubridate::year(accident)]
    data[, developmentYear := cut(transaction, breaks = c(as.Date(paste0(accidentYear, "-01-01"), format = "%Y-%m-%d"), dateLim[dateLim > as.Date(paste0(accidentYear, "-01-01"), format = "%Y-%m-%d")]), labels = FALSE) -1, by = accidentYear]
  } else if(mode == "year"){
    data[, c("accidentYear", "developmentYear") := list(cut(accident, breaks = dateLim, labels = paste(dateLim[-length(dateLim)], dateLim[-1], sep = "|")),
                                                        cut(transaction, breaks = dateLim, labels = FALSE) - cut(accident, breaks = dateLim, labels = FALSE))
    ]
  }
  
  
  # Prevent values from not existing when aggregating
  dataComp <- expand.grid(accidentYear = unique(data$accidentYear),
                          developmentYear = unique(data$developmentYear),
                          cashflows = NA)
  dataComp <- dataComp[!(paste(dataComp$accidentYear, dataComp$developmentYear) %in% unique(paste(data$accidentYear, data$developmentYear))),]

  data <- data.table::rbindlist(list(data, dataComp), fill = TRUE)

  # Value aggregation
  agg_data <- data[, .(cashflows = sum(cashflows, na.rm = T)), by = list(accidentYear, developmentYear)]
  data.table::setorderv(agg_data, c("accidentYear", "developmentYear"))
  
  # Put NAs on values that should not exist and aggregate
  agg_data[(cashflows == 0 & data.table::shift(cashflows, -1) == 0) | (cashflows == 0 & developmentYear > data.table::shift(developmentYear, -1)), cashflows := NA]
  agg_data[, cashflows := cumsum(cashflows), by = accidentYear]


  # Convert to triangle
  triangle <- data.table::dcast(agg_data, accidentYear ~ developmentYear, value.var = "cashflows")

  # Naming before converting to triangle to keep names
  rowNames <- triangle$accidentYear
  triangle <- as.matrix(triangle[,-1])

  
  # Name rows and columns
  colnames(triangle) <- 1:ncol(triangle)
  rownames(triangle) <- rowNames

  # Return value for function
  return(triangle)

}
ArnaudBu/ReservingLad documentation built on Sept. 21, 2021, 1:19 p.m.