Site icon R-bloggers

The Limit of ATR Order Sizing

[This article was first published on QuantStrat TradeR » R, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

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" & "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.


To leave a comment for the author, please follow the link and comment on their blog: QuantStrat TradeR » R.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.