The Limit of ATR Order Sizing

Before beginning this post, I’d like to notify readers that I have a webcast tomorrow (Wednesday, Sep. 3) at 4:30 EST for Big Mike’s Trading. Those that can follow the code and the analytics on this blog will see nothing new, but for those that effectively “nod and wait for the punchline” in the form of the equity curve, I’ll demonstrate how to build a strategy “in real time”.

Here’s the link.

Now onto the post:

While the last post showed how ATR did a better job than raw dollar positions of equalizing risk in the form of standard deviations across instruments, it isn’t the be-all, end-all method of order sizing. Something I learned about recently was portfolio component expected shortfall (along with portfolio component standard deviation). The rabbit hole on these methods runs very deep, including to a paper in the Journal of Risk. To give a quick summary of this computation, it’s one that takes into account not just the well-known mean and covariance, but also interactions between higher order moments, such as co-skewness, and co-kurtosis. The actual details of the math behind this is quite extensive, but luckily, it’s already programmed into the PerformanceAnalytics package, so computing it is as simple as calling a pre-programmed procedure. This demo will, along the way of making yet another comparison between ATR and dollar order sizes, demonstrate one way of doing this.

For those that are unfamiliar with the terminology, expected shortfall is also known as conditional value-at-risk (aka CVaR), which is a coherent risk measure, while regular value at risk is not (for instance, take the example of two bonds each with a default probability of less than 5%, say, 4.95% — the 5% VaR of either of them is 0, but the 5% VaR of the two bond portfolio is greater than zero (or less, depending on how you express the quantity–as a portfolio value, or loss value)).

In any case, here’s the code:

require(IKTrading)
require(quantstrat)
require(PerformanceAnalytics)

initDate="1990-01-01"
from="2003-01-01"
to="2012-12-31"
options(width=70)

source("demoData.R")

#trade sizing and initial equity settings
tradeSize <- 100000
initEq <- tradeSize*length(symbols)

strategy.st <- portfolio.st <- account.st <- "DollarVsATRos"
rm.strat(portfolio.st)
rm.strat(strategy.st)
initPortf(portfolio.st, symbols=symbols, initDate=initDate, currency='USD')
initAcct(account.st, portfolios=portfolio.st, initDate=initDate, currency='USD',initEq=initEq)
initOrders(portfolio.st, initDate=initDate)
strategy(strategy.st, store=TRUE)

#parameters
pctATR=.02
period=10
atrOrder <- TRUE

nRSI <- 2
buyThresh <- 20
sellThresh <- 80
nSMA <- 200

add.indicator(strategy.st, name="lagATR", 
              arguments=list(HLC=quote(HLC(mktdata)), n=period), 
              label="atrX")

add.indicator(strategy.st, name="RSI",
              arguments=list(price=quote(Cl(mktdata)), n=nRSI),
              label="rsi")

add.indicator(strategy.st, name="SMA",
              arguments=list(x=quote(Cl(mktdata)), n=nSMA),
              label="sma")

#signals
add.signal(strategy.st, name="sigComparison",
           arguments=list(columns=c("Close", "sma"), relationship="gt"),
           label="filter")

add.signal(strategy.st, name="sigThreshold",
           arguments=list(column="rsi", threshold=buyThresh, 
                          relationship="lt", cross=FALSE),
           label="rsiLtThresh")

add.signal(strategy.st, name="sigAND",
           arguments=list(columns=c("filter", "rsiLtThresh"), cross=TRUE),
           label="longEntry")

add.signal(strategy.st, name="sigThreshold",
           arguments=list(column="rsi", threshold=sellThresh,
                          relationship="gt", cross=TRUE),
           label="longExit")

add.signal(strategy.st, name="sigCrossover",
           arguments=list(columns=c("Close", "sma"), relationship="lt"),
           label="filterExit")

#rules
if(atrOrder) {
  
  add.rule(strategy.st, name="ruleSignal", 
           arguments=list(sigcol="longEntry", sigval=TRUE, ordertype="market", 
                          orderside="long", replace=FALSE, prefer="Open", osFUN=osDollarATR,
                          tradeSize=tradeSize, pctATR=pctATR, atrMod="X"), 
           type="enter", path.dep=TRUE)
} else { 
  add.rule(strategy.st, name="ruleSignal", 
           arguments=list(sigcol="longEntry", sigval=TRUE, ordertype="market", 
                          orderside="long", replace=FALSE, prefer="Open", osFUN=osMaxDollar,
                          tradeSize=tradeSize, maxSize=tradeSize), 
           type="enter", path.dep=TRUE)
}


add.rule(strategy.st, name="ruleSignal", 
         arguments=list(sigcol="longExit", sigval=TRUE, orderqty="all", ordertype="market", 
                        orderside="long", replace=FALSE, prefer="Open"), 
         type="exit", path.dep=TRUE)

add.rule(strategy.st, name="ruleSignal", 
         arguments=list(sigcol="filterExit", sigval=TRUE, orderqty="all", ordertype="market", 
                        orderside="long", replace=FALSE, prefer="Open"), 
         type="exit", path.dep=TRUE)

#apply strategy
t1 <- Sys.time()
out <- applyStrategy(strategy=strategy.st,portfolios=portfolio.st)
t2 <- Sys.time()
print(t2-t1)

#set up analytics
updatePortf(portfolio.st)
dateRange <- time(getPortfolio(portfolio.st)$summary)[-1]
updateAcct(portfolio.st,dateRange)
updateEndEq(account.st)


#Portfolio comparisons to SPY
instRets <- PortfReturns(account.st)

if(atrOrder) {  
  atrSd <- StdDev(na.omit(instRets), portfolio_method="component")
  t1  <- Sys.time()
  atrES <- ES(na.omit(instRets), portfolio_method="component")
  t2 <- Sys.time()
  print(t2-t1)
} else {
  dollarSd <- StdDev(na.omit(instRets), portfolio_method="component")
  t1  <- Sys.time()
  dollarES <- ES(na.omit(instRets), portfolio_method="component")
  t2 <- Sys.time()
  print(t2-t1)
}

if(("atrSd" %in% ls()) & ("dollarSd" %in% ls())){  
  boxPlotFrame <- cbind(atrSd$pct_contrib_StdDev, 
                        atrES$pct_contrib_MES,
                        dollarSd$pct_contrib_StdDev,
                        dollarES$pct_contrib_MES)
  colnames(boxPlotFrame) <- c("atrSd", "atrES", "dollarSd", "dollarES")
  boxplot(boxPlotFrame)
}

rownames(boxPlotFrame) <- gsub(".DailyEndEq", "", rownames(boxPlotFrame))

This is the resulting image:

And the corresponding data which was used to generate the box plot:

> boxPlotFrame
          atrSd       atrES     dollarSd      dollarES
EFA 0.049378400 0.032829510 0.0412678995  0.0414725387
EPP 0.053737115 0.061714539 0.0509966059  0.0701035651
EWA 0.044784653 0.064515175 0.0510242998  0.0728805941
EWC 0.036487179 0.019929189 0.0439868019  0.0422535671
EWG 0.043172662 0.028698773 0.0455195612  0.0505451367
EWH 0.039433573 0.038858046 0.0449196754  0.0362996367
EWJ 0.030736566 0.037979817 0.0257488355  0.0257318748
EWS 0.041005553 0.016891054 0.0453268353  0.0240447623
EWT 0.029300684 0.058477463 0.0378874688  0.0599281716
EWU 0.043907517 0.025657106 0.0403600522  0.0366783005
EWY 0.039628602 0.028507044 0.0550089098  0.0370010120
EWZ 0.039586224 0.057278661 0.0721037108  0.0867982223
EZU 0.042050678 0.026106675 0.0412176610  0.0288231226
IEF 0.008465791 0.027757444 0.0008097627  0.0046390295
IGE 0.038329663 0.062596052 0.0487744244  0.0797431561
IYR 0.029283546 0.009068168 0.0299819881 -0.0088073708
IYZ 0.034378964 0.042472847 0.0265562205  0.0285952033
LQD 0.008845486 0.020600278 0.0013406959  0.0007059662
RWR 0.027775214 0.014710031 0.0301350434 -0.0063985075
SHY 0.007692137 0.026727026 0.0001618876  0.0009506367
TLT 0.008471822 0.015863044 0.0025502684  0.0031127512
XLB 0.037847498 0.069639008 0.0407311722  0.0788174726
XLE 0.034833476 0.040059687 0.0463448900  0.0602607692
XLF 0.036103344 0.031322842 0.0306424885  0.0337304540
XLI 0.036248065 0.003602306 0.0324606725  0.0018744509
XLK 0.039230708 0.022105404 0.0330037953  0.0276545899
XLP 0.029559398 0.006160133 0.0161629448 -0.0067567464
XLU 0.024703361 0.047065790 0.0191419001  0.0393038102
XLV 0.028872857 0.027564044 0.0168619029  0.0173169198
XLY 0.036149264 0.035242843 0.0289716256  0.0326969108

As can be seen in the image, which is a box plot of the various ways of computing the percentage of portfolio component risk for the two order types, ATR order sizing still does a better job than raw dollar order sizing in terms of controlling risk. However, as evidenced by the atrES box plot, there still is a somewhat wide distribution in terms of contributions to portfolio risk between the various instruments. However, even in this instance of portfolio component risk, it’s readily visible how the ATR order sizing improves on dollar order sizing. However, this also demonstrates how ATR order sizing isn’t the be-all, end-all method of portfolio allocations.

For future note, the application of portfolio component risk metrics is to optimize them in one of two ways–by minimizing the difference between them, or by striving to set them as close to equal to each other as possible (that is, portfolio component risk balance). The PortfolioAnalytics package provides methods on how to do that, which I’ll visit in the future.

Thanks for reading.

7 thoughts on “The Limit of ATR Order Sizing

  1. Pingback: The Whole Street’s Daily Wrap for 9/2/2014 | The Whole Street

  2. Another way of sizing a signal is to do it relatively. For example, given a trading strategy that trades N assets, signals will be generated at different times. For higher frequency strategies, we can expect there are always some positions in our portfolio. Now, absolute order sizing is used initially to size a signal, but we can overlay a relative sizing algorithm that periodically “peel” quantities off when a position is contributing too much to risk. Or you can cluster the positions and reduce size accordingly to achieve better diversification. This above approach is the conservative one as we are only always peeling off, never adding on. Of course, the opposite also works too, albeit will add more variance to your return.

  3. Oh I was just thinking of MA verses RSI2 strategies for example.

    I’ve been rather busy with work and a side project of putting up an ATS on IB. Hoping to resume when I get the time. btw, great blog.

      • I don’t use R for production; its a prototyping language for me mainly. My prod stack is all in python though I am planning to use java or C++ in the future when I need more speed.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s