System Development

Energy Stat Arb

Back to my roots. Haven’t tested outright entry exit trading systems for a while now since the Mechanica and Tblox days but I aim to post more about these in the future.

I’ve been looking and reading about market neutral strategies lately to expand my knowledge. Long only strategies are great but sometimes constant outright directional exposure may leave your portfolio unprotected to the downside when all assets are moving in the same direction. A good reminder would be the May of last year when gold took a nose dive.

Below are some tests I conducted on trading related energy pairs. Note that I haven’t done any elaborate testing for whether the spread is mean reverting,etc. I just went with my instincts. No transaction costs. Spread construction based on stochastic differential, 10 day lookback, +-2/0 std normalized z score entry/exit, and delay 1 bar execution.

Crude Oil and Natural Gas Futures (Daily) (Daily don’t seem to work that well no more):

CL-NG

OIL and UNG ETF (1 Min Bar)

OIL-UNG

XLE and OIL ETF (1 Min Bar)

XLE-OIL

Pair trading is the simplest form of statistical arbitrage but what gets interesting is when you start dealing with a basket of assets. For example, XLE tracks both Crude Oil and Natural Gas companies, therefore a potential 3 legged trade would be to trade XLE against both OIL and UNG. Another well-known trade would be to derive value for the SPY against TLT (rates), HYG (corp spreads), and VXX (Vol).

The intuition behind relative value strategies is to derive a fair value of an asset “relative” to another. In basic pair trading, we are using one leg to derive the value of another, or vice versa. Any deviations are considered opportunities for arbitrage. In the case for multi legged portfolio, a set of assets are combined in some way (optimization, factor analysis, PCA) to measure the value. See (Avellaneda) for details.

While the equity lines above look nice, please remember that they don’t account for transaction costs and are modelled purely on adjusted last trade price. A more realistic simulation would be to test the sensitivity of entry and order fills given level 1 bid-ask spreads. For that, a more structured backtesting framework should be employed.

(Special thanks to QF for tremendous insight)

Thanks for reading,

Mike

Advertisements

Engineering Risks and Returns

In this post, I want to present a framework for formulating portfolio with targeted risk or return. The basic idea was inspired by controlling risk from a different point of view. The traditional way of controlling for portfolio risk was to apply a given set of weights to historical data to calculate historical risk. If estimated portfolio risk exceeds a threshold, we peel off allocation percentages for each asset. In this framework, I focus on constructing portfolios that target a given risk or return on a efficient risk return frontier.

First lets get some data to so we can visualize traditional portfolio optimization’s risk return characteristics. I will be using a 8 asset ETF universe.

rm(list=ls())
setInternet2(TRUE)
con = gzcon(url('http://www.systematicportfolio.com/sit.gz', 'rb'))
source(con)
close(con)
tickers = spl('EEM,EFA,GLD,IWM,IYR,QQQ,SPY,TLT')
data <- new.env()
getSymbols(tickers, src = 'yahoo', from = '1980-01-01', env = data, auto.assign = T)
for(i in ls(data)) data[[i]] = adjustOHLC(data[[i]], use.Adjusted=T)
bt.prep(data, align='keep.all', dates='2000:12::')

Here are the return streams we are working with

Rplot

The optimization algorithms I will employ are the following:

  • Minimum Variance Portfolio
  • Risk Parity Portfolio
  • Equal Risk Contribution Portfolio
  • Maximum Diversification Portfolio
  • Max Sharpe Portfolio

To construct the risk return plane, I will put together the necessary input assumptions (correlation, return, covariance, etc). This can be done with create.historical.ia function in the SIT tool box.

#input Assumptions
prices = data$prices
n=ncol(prices)
ret = prices/mlag(prices)-1
ia = create.historical.ia(ret,252)
# 0 <= x.i <= 1
constraints = new.constraints(n, lb = 0, ub = 1)
constraints = add.constraints(diag(n), type='>=', b=0, constraints)
constraints = add.constraints(diag(n), type='<=', b=1, constraints)

# SUM x.i = 1
constraints = add.constraints(rep(1, n), 1, type = '=', constraints)

With the above we can go ahead and input both ‘ia’ and ‘constraints’ in to the above optimization algorithms to get weights. With the weights, we can derive the portfolio risk and portfolio return. These then can be plotted on a risk return plain visually.

# create efficient frontier
ef = portopt(ia, constraints, 50, 'Efficient Frontier')
plot.ef(ia, list(ef), transition.map=F)

Rplot01

The risk return plain in the above image shows all the possible space to which a portfolio’s risk and return characteristic can reside. Anything that is beyond to the left side of the frontier do not exist (unless leverage, to which the EF will also shift leftward too). Since I am more of a visual guy, I tend to construct this risk return plain whenever I am working on new allocation algorithms. This allows me to compare with other portfolio the expected risk and return.

As you can see, each portfolio algorithm has their own set of characteristics. Note that these characteristics fluctuate across the frontier were we to frame this rolling through time. A logical extension to these risk return concepts is to construct a portfolio that aims to target ether a given risk or a given return on the frontier. To formulate this problem in SIT for the return component, simply modify the constraints as follows:

constraints = add.constraints(ia$expected.return,type='>=', b=target.return, constraints)

Note that the target.return variable is simply a variable storing the desired target return. After adding the constraint, simply run a minimum variance portfolio and you will get a target return portfolio. On the other hand, targeting risk is a bit more complicated. If you look at the efficient frontier, you will find that for a given level of risk there is two portfolios that line on it.  (The sub-optimal portion of the efficient frontier is hidden). I solved for the weights using a multi optimization framework which employed both linear and quadratic (dual) optimization.

target.risk.obj<-function(ia,constraints,target.risk){

 max.w = max.return.portfolio(ia, constraints)
 min.w = min.var.portfolio(ia, constraints)
 max.r = sum(max.w * ia$expected.return)
 min.r = sum(min.w * ia$expected.return)
 max.risk = portfolio.risk(max.w,ia)
 min.risk = portfolio.risk(min.w,ia)

 # If target risk exists as an efficient portfolio else
 # return weights of 0
 if(target.risk >= min.risk | target.risk <= max.risk){
 out <-optimize(f =target.return.risk.helper,
 interval = c(0,max.r),
 target.risk = target.risk,
 ia = ia,
 constraints = constraints)$minimum
 weight=target.return.portfolio(out)(ia,constraints)
 }else{
 weight=rep(0,ia$n)
 }

 return(weight)
}

Below is a simple backtest that takes the above assets and optimizes for the target return or target risk component. Each will run with a target of 8%.

Backtest1Now the model itself requires us to specify a return or risk component. What if instead we make that a dynamic component such that we extract ether the risk or return component of a alternative sizing algorithm. Below are the performance of the dynamic risk or return component extracted from naive risk parity.

Backtest2

 

Not surprisingly, whenever we target risk, the strategy tends to become more risky. This confirms confirms risk based allocations are superior if investors are aiming to achieve low long term volatility.

 

Thanks for reading,

Mike

FAA: A Lookback in Time…

In the spirit of wrapping up the FAA model investigation, I thought I would extend the backtest further back to 1926. Data used are all monthly total return series from proprietary databases and they are the best estimates that I have to work with. Looking back so far offers a LOT of insights. One will be able to stress test how the specific strategy performed in different environments.

I employed 7 different asset classes: commodities, emerging market equities, US equities, US 10 year bonds, US 30 year bonds, short term treasuries and European equities. For benchmarking purposes, I constructed a simply momentum portfolio that holds the top 3 assets, an equal weight portfolio, and a traditional sixty-forty portfolio. Lookbacks for momentum are 4 months, in line with what Keller and Putten used.

FAA-Long-SS FAA-Long-Performance

One very interesting aspect I found from this extended backtest is to see how the strategies performed during the Great Depression. While equal weight and sixty forty suffered large draw downs, FAA and relative momentum did comparatively well.  Below is a deeper analysis into the Great Depression. As you can see, momentum strategies in general provided a great buffer against drawdown.

Depression

 

GD-PERF

The main reason for this is that during the drawdown period, the FAA strategy were all loaded with bonds:

GD-Holdings-FAA

 

 

When I am researching trading systems, I really like to break down its components apart and analyse it as much as possible. It is only by understanding how they fit together will you be able to judge its future viability. When it will work and when it won’t work. And since these days TAA strategies have become so pervasive, it begs to questions whether we are taking appropriate precautions to its future performance.

Flexible Asset Allocation

In my last post, I broke down the individual components to look at the performance of each factor. Although by themselves, the correlation and volatility factors weren’t that attractive, as a whole combined together, its a different story.

I’ve always been a proponent of simplistic approaches in system design as adding too many nuts and bolts to ensure sophistication only brings over-fit. In my opinion, when you are designing the alpha portion of your portfolio, you should look to design multiple simplistic strategies that are different in nature (uncorrelated). Take these return streams and overlay a portfolio allocation strategy and you will find yourself with a decent alpha generator with >1 risk return. Ok back to FAA…

Keller and Putten in their FAA system combined the signals of each factor by a simple meta rank function. This ranking function took the following form:

where m, c and v represents the factor rank of momentum, correlation and volatility respectively. Each factor is then given a weight. The meta ranking function is than ranked again and filter based on absolute momentum to arrive at the assets to invest in. Note that any assets that don’t pass the absolute momentum filter will be invested in cash (VFISX). When coding the meta ranking function, I found that there are times when some assets share the same final meta rank. This caused problem for some rebalance period when the assets to hold will exceed top N. I consulted with the authors and they revealed that “with rank ties, we select more than 3 funds.” Below is a replication of the strategy; it is tested with daily data as oppose to monthly data used by the authors.

FAA-perf faa-perf1

The model results are pretty decent. One aspect I may change would be the use of the cash proxy in the volatility ranking factor. By including the theoretical risk free rate that is suppose to have volatility of zero will skew the results to bias cash.

A reader commented on a little error in coding I made in the last post. Don’t sweat, it doesn’t change the performance one bit. I’ve modified the code and placed everything including the current code in to the FAA dropbox folder. Should you have any questions please leave a comment below.

Thanks for reading,

Mike

Alternative Momentum Factors

Keller and Putten in their 2012 paper, “Generalized Momentum and FAA”, went on to combine multiple momentum ranking factors to form portfolios rebalanced monthly. I won’t go in to detail about their strategy as you can find a good commentary at Turnkey Analyst.

Here I took apart each ranking factors and constructed portfolios to see their individual performance. I thought this may be a good way to visualize the performance of each factor alone.

There are four portfolios, rebalanced monthly.

1. Relative Momentum- holds top n performing funds

2. Absolute Momentum- holds funds with positive momentum

3. Volatility Momentum- holds the n lowest volatility funds

4. Correlation Momentum- holds the n lowest average correlation fund; average of all pairwise correlation

Performance

Equity Performance

</pre>
############################################################
#Flexible Asset Allocation (Keller & Putten, 2012)
#
############################################################
rm(list=ls())
con = gzcon(url('http://www.systematicportfolio.com/sit.gz', 'rb'))
source(con)
close(con)
load.packages("TTR,PerformanceAnalytics,quantmod,lattice")

#######################################################
#Get and Prep Data
#######################################################
setwd("C:/Users/michaelguan326/Dropbox/Code Space/R/blog research/FAA")

data <- new.env()
#tickers<-spl("VTI,IEF,TLT,DBC,VNQ,GLD")

tickers<-spl("VTSMX,FDIVX,VEIEX,VFISX,VBMFX,QRAAX,VGSIX")
getSymbols(tickers, src = 'yahoo', from = '1980-01-01', env = data, auto.assign = T)
for(i in ls(data)) data[[i]] = adjustOHLC(data[[i]], use.Adjusted=T)

bt.prep(data, align='remove.na', dates='1990::2013')

#Helper
#Rank Helper Function
rank.mom<-function(x){
 if(ncol(x) == 1){
 r<-x
 r[1,1] <- 1
 }else{
 r <- as.xts(t(apply(-x, 1, rank, na.last = "keep")))
 }

 return(r)
}
#######################################################
#Run Strategies
#######################################################

source("C:/Users/michaelguan326/Dropbox/Code Space/R/blog research/FAA/FAA-mom.R")
source("C:/Users/michaelguan326/Dropbox/Code Space/R/blog research/FAA/FAA-abs-mom.R")
source("C:/Users/michaelguan326/Dropbox/Code Space/R/blog research/FAA/FAA-vol.R")
source("C:/Users/michaelguan326/Dropbox/Code Space/R/blog research/FAA/FAA-cor.R")
source("C:/Users/michaelguan326/Dropbox/Code Space/R/blog research/FAA/FAA-bench.R")
models<-list()
top<-3
lookback<-80

#run models
models$mom<-mom.bt(data,top,lookback) #relative momentum factor
models$abs.mom<-abs.mom.bt(data,lookback) #absolute momentum factor
models$vol<-vol.bt(data,top,lookback) #volatility momentum factor
models$cor<-cor.bt(data,top,lookback) #volatility factor
models$faber<-timing.strategy.local(data,'months',ma.len=200) #faber
models$ew<-equal.weight.bt(data) #equal weight benchmark
#report
plotbt.custom.report.part1(models)
plotbt.transition.map(models)
plotbt.strategy.sidebyside(models)
<pre>

The source codes can be downloaded in my DB folder,  can’t guarantee they are error free. Please leave comment of email me if you should find any mistakes.

Thanks for reading,

Mike

Systematic Edge Backtest Engine: plotting features

Here are some new additions to the plotting functions of the backtester. They are a great way of visualizing the performance of a trading strategy. As I wanted ease and simplicity, all of the following plots can be done in one line of code. Each image is followed by the code that  generated it.

plot.equity(equity,bench,testParameters)

 

plot.drawdown(perf,bench)

plot.horizon(equity,bench,testParameters,250)

plot.calendarHeatMap(equity)

The first two plots are very self-explanatory. The third plot is called horizon plot. It is a compact way of visualizing time series data. The intuition behind the way I use it is to chart rolling n (250 in my case) period performance of a strategy relative to its benchmark. The darker the red, the more underperformance, while the darker the blue, the more outperformance. If you refer to the plot and look at the 2008 crisis, you can see that in general, the colour of the strategy is lighter in red compared to the benchmark, showing hedging at its best.

The last chart is a calendar heat-map. It plots the n-period log return for the entire test period and shows you how it has performed in different periods of time. This plot is great for detailed consistency checking as it is a great way peeling in to the strategies performance as it cycles through time; a true time machine in my opinion.

A creative performance measure derived from the calendar heat map is the percent of daily return above a user defined return threshold. The threshold by definition should be the risk free rate. When doing strategy comparison, the higher the percent of days of out performance, the better.

Much more to come. Enjoy!

 

 

Systematic Edge Backtest Engine

In this post, I’d like to share some tools that the individual investors can use to backtest trading strategies. R is a powerful computing language that is composed of many statistical tools. I have taken and compiled several open source code and put together what I believe to be a easy to use backtesting interface. My functions act as wrappers so that it covers the unnecessary details for the end user.

The current frame work is very slow but it is very powerful as it has the capabilities of testing multiple strategies across multiple portfolios. For the user who want more customization, please visit my github page for the detail codes.

This is an example of simulating two trading strategies on a single portfolio; both are based on Faber moving average. First strategy is long when close > SMA(100) and for the sake of simplicity, the second one is when close > SMA(200). Note, the strategy is for demonstration purposes only. Here are the summary images created automatically.

The tools are currently in its infancy. There are a lot more features that I would like to add to the toolbox. The code is open source and I will organize it to put it up on github to share it. The following is the code that generated the backtest and it can be done in less than 20 lines of code! Any comments would be appreciated.

</p>
# Multi-Strategy Testing

#################################################################################
# 1. initialize backtest parameters
# (start date, end date, initial equity)
# and test name
#################################################################################
test.name<- "test1"
testParameters<-bt.testParameter("2011-01-01","2012-01-01",100000)
#################################################################################
# 2. create a list of symbols
#################################################################################
symbols<-c("SPY")
#################################################################################
# 3. get the adjusted data
#################################################################################
bt.dataSetup(symbols, "USD", 1, testParameters,adjust="True")

SPY.Strategy1<-SPY
SPY.Strategy2<-SPY

#################################################################################
# 4. configure indicator
# use ttr package to link indicators to each instrument
#################################################################################
SPY.Strategy1$SMA.100 = SMA(Cl(SPY.Strategy1), 100)
SPY.Strategy2$SMA.200 = SMA(Cl(SPY.Strategy2), 200)
n<-200
symbols<-c("SPY.Strategy1","SPY.Strategy2")

#################################################################################
# 5. Set up trading rules
# signal based traiding rule
#################################################################################

#for multi strategy
SPY.Strategy1$signal =(Cl(SPY) > SPY.Strategy1$SMA.100) + 0
SPY.Strategy2$signal =(Cl(SPY) > SPY.Strategy2$SMA.200) + 0
#################################################################################
# 6. Setup Portfolio and Account Object in Blotter
#################################################################################
bt.globalTestSetup(test.name, symbols, testParameters)
#################################################################################
# 7. Run Strategy
#################################################################################
bt.run(symbols,test.name,n)

#################################################################################
# 8. Performance Graphing
#################################################################################

equity<-getAccount(test.name)$summary$End.Eq
perf<-computePerformanceStatistics(equity)
bench<-generate.benchmark(c("SPY"),testParameters)
plot.calendarReturn(perf)
plot.equity(equity,bench,testParameters)

<strong>