This project analyses the Commitment of Traders data with specific focus on the Euro / Dollar exchange. The COT report, published every week, shows the major positions held by big investors grouped by commercial or non commercial type. The goal is to verify if their short or long position can be a predictor for the future euro / dollar excange.

Data

Two sets of data are downloaded from the Quandl database.

With regards to the COT, the net position (commercial or non commercial) is calculated substracting short from long.

# Libraries
library(Quandl)
library(quantmod)
library(TTR)
library(PerformanceAnalytics)
library(ggplot2)
library(gridExtra)

# Data uploading and processing
euro <- Quandl('CHRIS/CME_EC2')
cot <- Quandl('CFTC/099741_FO_L_ALL')
noncommercial <- cot$`Noncommercial Long`-cot$`Noncommercial Short`
commercial <- cot$`Commercial Long`-cot$`Commercial Short`

Exploratory Analysis

In the following chart the time series of euro / dollar exchange and COT (commercial or non commercial) are compared. Non commercial investors trade the exchange to speculate on its appreciation or depreciation, for this reason their net long position could signal expectaions of future increase in the exchange, future decreases in case of net short position. Commercial investors trade to hedge their industrial activities versus the fluctuation of the exchange. In other words their net long position could signal expectations of future decreases, future increases in case of net short position.

df <- merge(euro,cot,by.y='Date',by.x='Date')
g1 <- ggplot(data = df, aes(Date,Settle))+
    geom_line()+ggtitle('Euro / Dollar Exchange')
g2 <- ggplot(data = df, aes(Date,`Noncommercial Long`/1000000))+
    geom_line(col='green')+
    geom_line(aes(y=`Noncommercial Short`/1000000),col='red')+
    ggtitle('Non Commercial Position')+ylab('Position')
g3 <- ggplot(data = df, aes(Date,`Commercial Long`/1000000))+
    geom_line(col='green')+
    geom_line(aes(Date,`Commercial Short`/1000000),col='red')+
    ggtitle('Commercial Position')+ylab('Position')
grid.arrange(g1,g2,g3,nrow=3)

In the following chart the net position for commercial or non commercial traders is calculated and compared to the exchange.

df$noncom <- df$`Noncommercial Long`-df$`Noncommercial Short`
df$com <- df$`Commercial Long`-df$`Commercial Short`
c1 <- ggplot(data = df, aes(Date,Settle))+
    geom_line()+ggtitle('Euro / Dollar Exchange')
c2 <- ggplot(data = df, aes(Date,noncom/100000))+
    geom_area(fill='blue')+
    ggtitle('Non Commercial Position')+ylab('Position')
c3 <- ggplot(data = df, aes(Date,com/100000))+
    geom_area(fill='purple')+
    ggtitle('Commercial Position')+ylab('Position')
grid.arrange(c1,c2,c3,nrow=3)

Investment Strategies Backtesting

The net position of the traders is tested as a signal to buy or sell the euro / dollar exchange.

First, the non commercial traders are considered. Their goal is to speculate on future appreciation or depreciation of the exchange, thus a net long position constitutes a buy signal, whereas a net short position constitutes a sell signal.

# Create signal from non commercial net position 
signal_noncom <- lag(ifelse(df$noncom>0,1,-1))
returns_noncom <- ROC(df$Settle)*signal_noncom

## Convert returns to xts to use PerformanceAnalytics
r_noncom <- xts(returns_noncom, order.by = df$Date)
p_noncom <- exp(cumsum(r_noncom))
charts.PerformanceSummary(r_noncom)

Then, we can repeat the same process with commercial traders. This time a long net position is considered a sell signal because their goal is to hedge their industrial activities against the exchange fluctuations.

# Create signal from commercial net position
signal_com <- lag(ifelse(df$com<0,1,-1))
returns_com <- ROC(df$Settle)*signal_com

# Convert returns to xts to use PerformanceAnalytics
r_com <- xts(returns_com, order.by = df$Date)
p_com <- exp(cumsum(r_com))
charts.PerformanceSummary(r_com)

The performance of the two strategies can be summarised in the following table, where annualized return, standard deviation and sharpe ratio are listed.

r_summary <- cbind(table.AnnualizedReturns(r_noncom),table.AnnualizedReturns(r_com))
colnames(r_summary) <- c('NonCom','Com')
r_summary
##                           NonCom    Com
## Annualized Return         0.1167 0.1045
## Annualized Std Dev        0.0976 0.0979
## Annualized Sharpe (Rf=0%) 1.1958 1.0674