Site icon R-bloggers

Modeling Interest Rates Meucci Style

[This article was first published on Return and Risk, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

I have signed up for Attilio Meucci’s ARPM Bootcamp next month (July 13-18) in NYC http://www.symmys.com/arpm-bootcamp, and need to do quite a bit of prep as it’s going to be a deep-dive…

The Advanced Risk and Portfolio Management Bootcamp provides in-depth understanding of buy-side modeling from the foundations to the most advanced statistical and optimization techniques, in 6 intensive days of theory and MATLAB live examples and exercises:

  • Market modeling: random walk, ARMA, GARCH, Levy, long memory, stochastic volatility
  • Multivariate statistics: non-parametric, non-normal MLE, shrinkage, robust, Bayesian estimation; copula/marginal factorization; location-dispersion ellipsoid
  • Factor modeling: theory and pitfalls of time-series and cross-sectional factor models, CAPM, APT, principal components analysis, random matrix theory
  • Pricing: full evaluation, Greeks, stress-matrix interpolation; analytical, Monte Carlo, historical
  • Risk analysis: diversification, stochastic dominance, expected utility, Sharpe ratio, Omega, Kappa, Sortino, value at risk, expected shortfall, coherent and spectral measures
  • Portfolio construction: robust/SOCP optimization, shrinkage/Bayesian allocations, Black-Litterman and beyond; transaction costs, liquidity, market impact; statistical arbitrage; convex/concave dynamic strategies, CPPI, delta-replication

So I thought I would delve into one of his many interesting papers, “Neither ”Normal" not “Lognormal”: Modeling Interest Rates Across all Regimes“, co-authored by Angela Loregian. The paper and Matlab code can be downloaded at http://www.symmys.com/node/601.

However, since I’m still an R fan I thought it would be interesting to port his code to R (although there is an R package, “Meucci”, under development, it failed to build when I looked). Here is my port of the code and the resulting chart showing JGB rates, log-rates, and shadow rates (derived from the inverse call transformation):

+ Show R code
################################################################################
# load packaages                                                               #
################################################################################
library(RCurl)
library(xts)
library(matlab)
library(pracma)
library(reshape2)
library(ggplot2)
library(gridExtra)
################################################################################
# get JGB data from file                                                       #
################################################################################
csvfile = getURLContent(
    "https://docs.google.com/uc?export=download&id=0B4oNodML7SgSZGdVQXYxVWRwMW8",
    ssl.verifypeer = FALSE, followlocation = TRUE, binary = FALSE)
JGB <- read.zoo(textConnection(csvfile), format = "%Y-%m-%d", index = 1,
       header = TRUE, tz = "UTC", sep = ",")
JGB <- as.xts(JGB)

# process data
tau <- as.numeric(gsub("Y", "", names(JGB)[c 1="2," 2="3," 3="5," 4="7," 5="10," 6="11," 7="12)" language="(1,"][/c]))

# rates
y <- coredata(JGB[, c(1, 2, 3, 5, 7, 10, 11, 12)])
Date <- index(JGB)
TimeStep <- 5 # daily (1) / weekly (6) observations
date <- Date[seq(1, NROW(Date), TimeStep)]
y <- t(y[seq(1, NROW(Date), TimeStep), ])

################################################################################
# Inverse Call Transformation function                                         #
################################################################################
# this function computes the Inverse Call Transformation and returns shadow 
# rates see A. Meucci, A. Loregian - "Neither "Normal" not "Lognormal": Modeling
# Interest Rates Across all Regimes" to appear (2013)
# Last version of code and article available at http://symmys.com/node/601
#
# INPUT
# rates: matrix containing the time series of the rates to be transformed; 
#        size(rates)= lenght(tau) x t_, where t_ is the length of the time series
# tau: vector containing the times to maturity corresponding to the rows of the 
#      rates matrix
# eta, zeta: Inverse-call transformation parameters (the smoothing parameter s 
#            is obtained as s=eta*exp(zeta*tau))
#
# OUTPUT
# x: shadow rates, computed from rates via inverse-call transformation
################################################################################
InverseCallTransformation <- function(rates, tau, eta, zeta) {
    t_=size(rates,2);
    x=zeros(size(rates)[1], size(rates)[2]); 
    s=eta*exp(zeta*tau);
    
    # Bachelier call pricing function
    BachelierCallPrice <- function(x,s) {
        # Call function (zero-strike call option price profile according to the 
        # Bachelier pricing function)
        # s: smoothing parameter 
        C = x * pnorm(x/s) + s * dnorm(x/s);
    }
    
    call_fit <- function(tmpX,y,sigma) {
        c=BachelierCallPrice(tmpX,sigma);
        F= y - c;
    }
    
    for (v in 1:length(tau)) {
        # inverse call transformation
        # Optimization options. 
        opts <- list(tau = 0.01, tolx = 1e-10, tolg = 1e-10, maxeval = 7*500)
        x0=0; #initialization
        for (t in 1:t_) {
            # fitting inverse call
            x[v,t] <- lsqnonlin(call_fit, x0, opts, y = rates[v,t], sigma = s[v])$x;
        }
    }
    return(x)
}

################################################################################

# shadow rates, via Inverse Call Transformation
eta <- 0.005;
zeta <- 0;
icy <- InverseCallTransformation(y, tau, eta, zeta);

# log-rates
lny <- log(y)

# rate changes
dy <- diff(t(y));
dlny <- diff(t(log(y)));
dx <- diff(t(icy));

################################################################################
# plot rates/log-rates/shadow-rates                                            #
################################################################################
fmt <- function(x) {
    format(x, nsmall = 2, scientific = FALSE)
}

get_legend<-function(myggplot){
    tmp <- ggplot_gtable(ggplot_build(myggplot))
    leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
    legend <- tmp$grobs[[leg]]
    return(legend)
}

y_df <- melt(data.frame(date = as.Date(date), t(y)), id.vars = "date", 
             variable.name = "Maturity")
gp <- ggplot(y_df, aes(x = date, y = value)) +
    geom_line(aes(colour = Maturity)) + 
    labs(list(title = "JGB Rates", x = "", y = "")) + 
    scale_x_date(expand = c(0, 0)) + 
    theme(legend.position = "bottom") +
    theme(plot.margin = unit(c(0, .5, -0.5, .5), "lines"))


lny_df <- melt(data.frame(date = as.Date(date), t(lny)), id.vars = "date",
               variable.name = "Maturity")
gp1 <- ggplot(lny_df, aes(x = date, y = value)) +
    geom_line(aes(colour = Maturity)) + 
    labs(list(title = "JGB Log-Rates", x = "", y = "")) + 
    scale_x_date(expand = c(0, 0)) + 
    theme(legend.position="none") +
    theme(plot.margin = unit(c(0, .5, -0.5, .5), "lines")) +
    scale_y_continuous(labels = fmt)

icy_df <- melt(data.frame(date = as.Date(date), t(icy)), id.vars = "date",
               variable.name = "Maturity")
gp2 <- ggplot(icy_df, aes(x = date, y = value)) +
    geom_line(aes(colour = Maturity)) + 
    labs(list(title = "JGB Shadow Rates", x = "", y = "")) + 
    scale_x_date(expand = c(0, 0)) + 
    theme(legend.position = "none") +
    theme(plot.margin = unit(c(0, .5, -0.5, .5), "lines"))

legend <- get_legend(gp)
gp <- gp + theme(legend.position="none")
grid.arrange(gp, gp1, gp2, legend, nrow=4, heights=c(3, 3, 3, 0.5))

The main point regarding the shadow rates is to…

Notice how the behavior of the unconstrained risk drivers attained by means of the inverse call transformation looks smoother than those of yields and log-yields, displaying a homogeneous evolution for both short and long-term series.

… which is further explained in Meucci’s “The Prayer”…

Homogeneity ensures that we can apply statistical techniques to the observed time series of the risk drivers {yt}t=1,…,T and project future distributions.

You can find out more about this quest for invariance in “The Prayer” at http://papers.ssrn.com/sol3/papers.cfm?abstract_id=1753788.

One question I’ll be sure to ask is how to model “real-life” negative interest rates that we observed recently in the Euro area – this Meucci model doesn’t appear to admit such…

If you are also taking the plunge and attending the course, do drop me an email and we’ll meet up!

Click here for the R code on GitHub.

To leave a comment for the author, please follow the link and comment on their blog: Return and Risk.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.