Like many people, I came to R via excel. And like most of those people, there are still some things for which i still find myself (somewhat guiltily) firing up excel.

One of those guilty pleasures is the goal seek function. You know, set the value of this cell equal to something by changing the value of some other cell. I often found myself thinking: is there an R equivalent to excel’s goal seek … but i didn’t have the time to figure it out.

A bit of spare time and a real-life problem coincided recently.

The problem was to structure an interest rate curve trade such that it wasn’t correlated with USDJPY. Or to be more precise, to select some amount of 2yr bonds to hold against a short 10yr bond position such that the 2×10 curve trade was uncorrelated to USDJPY.

The basic problem is that US yield tends to increase at the same time as USDJPY appreciates, and in particular that USDJPY and 10yr US yields tend to move together more-so than USDJPY and 2yr yields. So when USDJPY rises the US yield curve also tends to steepen.

The problem is to find the weight on US 2yr bonds such that a US 2×10 steepener trade is uncorrelated to USDJPY.

The first task is to form the problem into a function of a single variable.

That’s done below:

corFun <- function(b, targetCor = 0) { spread <- portfolio_diff[, 'us10yr'] - b * portfolio_diff[, 'us2yr'] abs(cor(spread, portfolio_diff[, 'usdjpy']) - targetCor) }

The function `corFun`

takes a weight parameter `b`

, and a target correlation `targetCor`

and returns the absolute value of the difference between the empirical correlation and `targetCor`

.

Once the problem is packaged up into a function, you can pass the problem-function into `optimize`

and get the result.

The optimization function works as follows:

`optimize(f, lower, upper)`

In the present case, `f`

is `corFun`

, `lower`

is 0 (which would mean do not have a 2yr position), and `upper`

has been set to 2; this is arbitrary, but having a 2yr position that’s more than twice the size of the 10yr position would strain my sense of proportion.

It turns out that you want to have ~1.5x the position in US 2yrs.

So if you have one unit of risk in USDJPY, and you want to have a US 2×10 curve steepener that’s uncorrelated to USDJPY, you should sell one unit of US 10yrs (say that’s 100k DV01) and buy 1.5 times that amount in US 2yr bonds (say 150k DV01).

The full code is below:

library(fredr) | |

require(xts) | |

# note you'll need an API key | |

fredr_set_key("123YourFREDAPIKey321") | |

# helper function to convert tib to xts | |

xtsT <- function(TIB) xts(TIB$value, TIB$date) | |

# helper function to remove rows that have NA values | |

rmNA <- function(X) X[complete.cases(X), ] | |

# get data | |

us2yr <- xtsT(fredr("DGS2", observation_start=as.Date('2016-01-01'))) | |

us10yr <- xtsT(fredr("DGS10", observation_start=as.Date('2016-01-01'))) | |

usdjpy <- xtsT(fredr("DEXJPUS", observation_start=as.Date('2016-01-01'))) | |

# create portfolio: % returns for USDJPY and bps changes for 2×10 curve | |

portfolio <- rmNA(cbind(usdjpy, us2yr, us10yr)) | |

portfolio_diff <- rmNA(100 * cbind(diff(portfolio[, 1], log=T), | |

diff(portfolio[, 2:3]))) | |

# embed problem in a function | |

corFun <- function(b, targetCor = 0) { | |

spread <- portfolio_diff[, 'us10yr'] – b * portfolio_diff[, 'us2yr'] | |

abs(cor(spread, portfolio_diff[, 'usdjpy']) – targetCor) | |

} | |

# optimise the function | |

optWeight <- optimize(corFun, lower = 0, upper = 2) | |

# plot the range of results | |

plot(sapply(seq(0, 2, 0.01), corFun), x = seq(0, 2, 0.01), | |

main = "Correlation of weighted US 2×10 curve trade with USDJPY", | |

xlab = "weighting on 2yr", | |

ylab = "Correlation with USDJPY") | |

abline(v = 1, col=8, lty=2, lwd=1) | |

abline(h = 0, col=2, lty=2, lwd=1) | |

points(optWeight$minimum, 0, pch=17, col=2, type='p') |