library(lubridate) library(doParallel) library(dplyr) library(tidyr) hostname <- system("hostname", intern=TRUE) registerDoParallel(if(hostname=="debian") 4 else 8) source("db.R") source("cds_functions_generic.R") source("yieldcurve.R") serenitasdb <- dbConn("serenitasdb") get.indexquotes.table <- function(indextype, series, tenors=c("3yr", "5yr", "7yr"), onlymissing=TRUE) { df <- serenitasdb %>% tbl("index_quotes") %>% filter(index == !!indextype, series == !!series, tenor %in% !!tenors) %>% {if(onlymissing) filter(., is.null(duration)) else .} %>% arrange(date) %>% select(date, tenor, close_price) %>% collect() %>% mutate(tenor=factor(tenor, !!tenors)) if(nrow(df) == 0) { return( df ) } else { return( df %>% spread(tenor, close_price) ) } } get.indexmaturity <- function(index, series){ index.maturity <- tbl(serenitasdb, "index_maturity") df <- index.maturity %>% filter(index == !!index, series == !!series) %>% mutate(running = coupon/10000) %>% select(maturity, running, tenor) %>% arrange(maturity) %>% collect() return( df ) } fastduration <- function(sc, cs, tradedate, maturities){ r <- rep(NA, length(maturities)) if(is.null(sc)) { return( r ) } startdate <- tradedate + 1 acc <- cdsAccrued(tradedate, 1) for(i in seq_along(maturities)) { if(startdate <= maturities[i]) { r[i] <- couponleg(cs[cs$unadj.dates<=maturities[i],], sc, startdate, accruedondefault=TRUE) - acc } } return( r ) } fasttheta <- function(sc, cs, recov, tradedate, maturities, quotes, fixedrate=0.05){ r <- rep(NA, length(maturities)) if(is.null(sc)) { return(r) } startdate <- tradedate + 1 acc <- cdsAccrued(tradedate, 1) newmaturities <- maturities + years(-1) for(i in seq_along(newmaturities)){ ## never extrapolate, and do not attempt to compute theta if within 1 year if(startdate > newmaturities[i] || is.na(quotes[i])) { next } else { newcs <- cs[cs$unadj.dates<=newmaturities[i],] upfront <- defaultleg(newcs, sc, recov, startdate) - (couponleg(newcs, sc, startdate, accruedondefault=TRUE)-acc)*fixedrate r[i] <- quotes[i] - upfront + fixedrate } } return( r ) } sqlstr.duration <- paste("UPDATE index_quotes set duration=$1 where date=$2 and index=$3", "and series=$4 and tenor=$5") sqlstr.theta <- paste("UPDATE index_quotes set theta=$1 where date=$2 and index=$3", "and series=$4 and tenor=$5") for(index in c('IG', 'HY', 'EU', 'XO')) { recov <- if(index %in% c('IG', 'EU')) 0.4 else 0.3 coupon <- if(index %in% c('IG', 'EU')) 0.01 else 0.05 for(series in 18:30) { tenors <- switch(index, IG = c("3yr", "5yr", "7yr", "10yr"), HY = c("3yr", "5yr", "7yr"), EU = c("3yr", "5yr", "7yr", "10yr"), XO = c("3yr", "5yr", "7yr", "10yr")) if( index %in% c('EU', 'XO') && series == 30) { next } indexquotes <- get.indexquotes.table(index, series, tenors) if(nrow(indexquotes) == 0) { next } maturities <- get.indexmaturity(index, series) maturities <- maturities[maturities$tenor %in% colnames(indexquotes)[-1],] durations <- matrix(0, nrow(indexquotes), nrow(maturities)) thetas <- matrix(0, nrow(indexquotes), nrow(maturities)) last_maturity <- maturities %>% slice(n()) %>% pull(maturity) durandthetas <- foreach(i = 1:nrow(indexquotes), .combine='rbind') %dopar% { tradedate <- indexquotes %>% slice(i) %>% pull(date) exportYC(tradedate, currency = if (index %in% c("IG", "HY")) "USD" else "EUR") cs <- couponSchedule(IMMDate(tradedate, noadj=TRUE), last_maturity, "Q", "FIXED", 1, 0, tradedate, IMMDate(tradedate, "prev")) quotes <- data.frame(upfront=(100-as.numeric(indexquotes[i,maturities$tenor]))/100, maturities) ## prevent negative hazardrates quotes$upfront <- pmax(-yearFrac(tradedate + 1, quotes$maturity, "act/360") * quotes$running + 1e-6, quotes$upfront) sc <- cdshazardrate(quotes, recov, tradedate, cs) c(fastduration(sc, cs, tradedate, maturities$maturity), fasttheta(sc, cs, recov, tradedate, maturities$maturity, quotes$upfront, coupon)) } ## non parallel version for easier debugging ## durandthetas <- c() ## for(i in 1:nrow(indexquotes)) { ## tradedate <- indexquotes %>% slice(i) %>% pull(date) ## exportYC(tradedate, currency = if (index %in% c("IG", "HY")) "USD" else "EUR") ## cs <- couponSchedule(IMMDate(tradedate, noadj=TRUE), last_maturity,"Q", "FIXED", 1, ## 0, tradedate, IMMDate(tradedate, "prev")) ## quotes <- data.frame(upfront=(100-as.numeric(indexquotes[i,maturities$tenor]))/100, ## maturities) ## quotes$upfront <- pmax(-yearFrac(tradedate + 1, quotes$maturity, "act/360") * ## quotes$running + 1e-6, quotes$upfront) ## sc <- cdshazardrate(quotes, recov, tradedate, cs) ## durandthetas <- rbind(durandthetas, c(fastduration(sc, cs, tradedate, maturities$maturity), ## fasttheta(sc, cs, recov, tradedate, maturities$maturity, quotes$upfront, coupon))) ## } if(is.null(dim(durandthetas))) { dim(durandthetas) <- c(1, length(durandthetas)) } n <- nrow(maturities) df.durations <- data.frame(date=indexquotes$date, durandthetas[,1:n, drop=F]) df.thetas <- data.frame(date=indexquotes$date, durandthetas[,(n+1):(2*n), drop=F]) colnames(df.durations) <- c("date", maturities$tenor) colnames(df.thetas) <- c("date", maturities$tenor) for(i in 1:nrow(df.durations)) { for(tenor in maturities$tenor) { if(!is.na(df.durations[i, tenor])) { r <- dbSendQuery(serenitasdb, sqlstr.duration, params = list(df.durations[i, tenor], df.durations[i,"date"], index, series, tenor)) if(dbHasCompleted(r)) { dbClearResult(r) } } if(!is.na(df.thetas[i,tenor])) { r <- dbSendQuery(serenitasdb, sqlstr.theta, params = list(df.thetas[i, tenor], df.thetas[i, "date"], index, series, tenor)) if(dbHasCompleted(r)) { dbClearResult(r) } } } } } } ## ## nice plot, now I'm just showing off ## library(ggplot2) ## ggplot(df.durations, aes(x=date))+geom_line(aes(y=`3yr`, colour="3yr"))+ ## geom_line(aes(y=`5yr`, colour="5yr"))+ ## geom_line(aes(y=`7yr`, colour="7yr"))+ylab("duration")+labs(colour="tenor") ## ggsave(filename=paste0("HY", series, " durations.png")) ## ## plot thetas ## ggplot(df.thetas, aes(x=date))+geom_line(aes(y=`3yr`, colour="3yr"))+ ## geom_line(aes(y=`5yr`, colour="5yr"))+ ## geom_line(aes(y=`7yr`, colour="7yr"))+ylab("theta")+labs(colour="tenor") ## ggsave(filename=paste0("HY", series, " thetas.png"))