In this post, I briefly describe the event of negative prices market which occured on the NYMEX WTI crude oil May contract
Event description
On April 15, 2020, the CME clearing released an advisory notice mentioning the possibility of negative prices in some NYMEX energy contracts. They indicate that their environment and message format are ready to handle such situation, see the CME advisory notice.
On April 20, 2020, at the end of the trading day and one day before the contract expires, the May futures contract written on WTI crude oil and traded on NYMEX reached the negative territory for the first time and even dropped to -40.

In this tutorial, I explain how to implement, in a flexible way, the algorithm of Bai, Lumsdaine, and Stock (1998).
Step 1: Lag variables.
This function takes as argument a matrix of time series observations and lags it by an order (q).
Code
compute_lags <- function(Y #time series matrix Y
, q) #lag order q
{
p <- dim(Y)[1] #get the dimensions
n <- dim(Y)[2]
myDates <- rownames(Y)[(q + 1) : p] #optional: keep the rownames dates of the data frame with final matching
Y <- data.

In this tutorial, I quickly describe how to compute and update an efficient frontier in adding stocks to an existing porfolio with R.
Step 1: Efficient frontier
First let’s write a simple code for an efficient frontier computation
Code
efficient_frontier = function(MRet #matrix of returns (MRet)
, rangeMu) #range (sequence) of target expected returns (rangeMu)
{
uM <- dim(MRet)[1] #get the row (uM) and column (pM) dimensions of the matrix of returns
pM <- dim(MRet)[2]
expRet <- colMeans(MRet) #compute the portfolio's individual stocks expected returns
Omega = var(MRet) #compute the sample var-covar matrix
unityVec <- rep(1, pM) #define a constraints vector (weights of the portfolio must sum to one)
A <- rbind(expRet, unityVec) #define a matrix of constraints (weights sum to one and variance will match a #target level of expected returns) n <- length(rangeMu) #get the length of the target range (sequence)
myVar <- rep(NA, n) #define an empty variance vector
myWeights <- matrix(data = NA, nrow = n, ncol = pM) #define an empty matrix of weights for each stock at each level of target
#expected returns
for(i in 1:n) #loop over the target expected returns range and compute variances and weights { b <- matrix(data = c(rangeMu[i], 1), nrow = 2)
myVar[i] <- t(b) %*% solve(A %*% solve(Omega) %*% t(A)) %*% b
myWeights[i,] <- solve(Omega) %*% t(A) %*% solve(A %*% solve(Omega) %*% t(A)) %*% b
}
mySd <- myVar^0.

© Loïc Maréchal 2023 · Powered by the Academic theme for Hugo.