Wednesday, November 18, 2009

Tactical asset allocation using blotter

NOTE: If you simply want to test strategies in R, please see the post: Tactical Asset Allocation Using quantstrat.  quantstrat uses blotter behind the scenes, but provides a higher level of abstraction.

blotter is an R package that tracks the P&L of your trading systems (or simulations), even if your portfolio spans many security types and/or currencies. This post uses blotter to track a simple two-ETF trading system.

The contents of this post borrow heavily from code and comments in the "longtrend" demo script in the blotter package. Many thanks to Peter Carl and Brian Peterson for their hard work.

The first chart shows the result of holding an equal-weight portfolio of SPY and IEF from 2002-07-31 to 2009-10-31. The 2008 bear market led to a 30% drawdown in this portfolio.


The second chart shows the result of following Mebane Faber's tactical asset allocation approach using the same ETFs and time period. Though it did not perform as well as buy-and-hold through 2007, the 2008 bear market only caused a 5% drawdown for this strategy. Both observations are consistent with the conclusion in Faber's article.


Without further ado, here's the code:

# This code implements the strategy found in:
# Faber, Mebane T., "A Quantitative Approach to Tactical Asset Allocation."
# Journal of Risk Management (Spring 2007).

# The article implements a simpler version of the 200-day SMA, opting for a
# 10-month SMA because monthly data are more easily available in earlier
# periods and lower granularity should translate to lower transaction costs.

# The rules of the system are relatively simple:
# - Buy when monthly price > 10-month SMA
# - Sell and move to cash when monthly price < 10-month SMA

# 1. All entry and exit prices are on the day of the signal at the close.
# 2. All data series are total return series including dividends, updated monthly.
# NOTE: For the purposes of this demo, we only use price returns.
# 3. Cash returns are estimated with 90-day commercial paper. Margin rates for
# leveraged models are estimated with the broker call rate.
# NOTE: For the purposes of this demo, we ignore interest and leverage.
# 4. Taxes, commissions, and slippage are excluded.

# Data:
# This demo uses monthly data downloaded from Yahoo Finance for two ETFs: SPY and
# IEF. These were chosen to illustrate the classic stock/bond asset portfolio.
# Though longer serires would be preferred, data for IEF begin in mid-2002.

# Load required libraries
library(quantmod)
library(TTR)
library(blotter) # r-forge revision 193
library(PerformanceAnalytics)

# Set initial values
initDate='2002-07-31'
endDate='2009-10-31'
initEq=100000

# Set currency and instruments
currency("USD")
stock("IEF",currency="USD",multiplier=1)
stock("SPY",currency="USD",multiplier=1)

# Load data with quantmod
print("Loading data")
symbols = c("IEF", "SPY")
getSymbols(symbols, from=initDate, to=endDate, index.class=c("POSIXt","POSIXct"))

# Adjust prices for splits/dividends (thanks pg)
#IEF = adjustOHLC(IEF)
#SPY = adjustOHLC(SPY)

# Convert data to monthly frequency (to.weekly() needs drop.time=FALSE)
IEF = to.monthly(IEF, indexAt='endof')
SPY = to.monthly(SPY, indexAt='endof')

# Set up indicators with TTR
print("Setting up indicators")
IEF$SMA = SMA(Cl(IEF), 10)
SPY$SMA = SMA(Cl(SPY), 10)

# Set up a portfolio object and an account object in blotter
initPortf(name='default', symbols=symbols, initDate=initDate)
initAcct(name='default', portfolios='default', initDate=initDate, initEq=initEq)
verbose = TRUE

# Create trades
for( i in 10:NROW(SPY) ) {
  CurrentDate=time(SPY)[i]
  equity = getEndEq(Account='default', CurrentDate)

  for( symbol in symbols ) {
    sym = get(symbol)
    ClosePrice = as.numeric(Cl(sym[i,]))
    Posn = getPosQty(Portfolio='default', Symbol=symbol, Date=CurrentDate)
    UnitSize = as.numeric(trunc((equity/NROW(symbols))/ClosePrice))

    # Position Entry (assume fill at close)
    if( Posn == 0 ) {
    # No position, so test to initiate Long position
      if( Cl(sym[i,]) > sym[i,'SMA'] ) {
        # Store trade with blotter
        addTxn('default', Symbol=symbol, TxnDate=CurrentDate,
          TxnPrice=ClosePrice, TxnQty=UnitSize, TxnFees=0, verbose=verbose)
      }
    } else {
      # Have a position, so check exit
      if( Cl(sym[i,]) < sym[i,'SMA'] ) {
        # Store trade with blotter
        addTxn(Portfolio='default', Symbol=symbol, TxnDate=CurrentDate,
          TxnPrice=ClosePrice, TxnQty=-Posn, TxnFees=0, verbose=verbose)
      }
    }
  } # End symbols loop

  # Calculate P&L and resulting equity with blotter
  updatePortf(Portfolio='default', Dates=CurrentDate)
  updateAcct(name='default', Dates=CurrentDate)
  updateEndEq(Account='default', Dates=CurrentDate)

} # End dates loop
 
# Buy and Hold cumulative equity
buyhold = exp(cumsum( ( 0.5*ROC(Cl(IEF)) + 0.5*ROC(Cl(SPY)) )[-1] ))

# Final values
cat('Tactical Asset Allocation Return: ',(getEndEq(Account='default', Date=CurrentDate)-initEq)/initEq,'\n')
cat('Buy and Hold Return: ',tail(buyhold,1)-1,'\n')

# Plot Strategy Summary
png(filename="20091118_blotter_strategy.png", 720, 720)
#charts.PerformanceSummary(ROC(getAccount('default')$TOTAL$End.Eq)[-1],main="Tactical Asset Allocation")
charts.PerformanceSummary(ROC(getAccount('default')$summary$End.Eq)[-1],main="Tactical Asset Allocation")
dev.off()

# Plot Buy and Hold Summary
png(filename="20091118_blotter_buyhold.png", 720, 720)
charts.PerformanceSummary(ROC(buyhold)[-1],main="Buy & Hold")
dev.off()

Thursday, November 5, 2009

opentick alternatives

I've been getting a bit of traffic from people searching for opentick (the defunct company), so I've started a list of similar (but non-free) data providers.

I'm not affiliated with any of these vendors, and the list is in no particular order. I'll update this post as more information becomes available.

IQFeed
- features, fees, API**
- starts at $60/month*
- OS: Windows
- Same API as DTN.IQ

eSignal
- features, fees, API**
- starts at $125/month*
- OS: Windows

eoddata
- features, fees, API
- some data are free, as low as 1-minute intraday data for $19.95/month
- OS: Windows

Marketfeed
- features, fees, API**
- starts at $20/month*, access via API starts at $400/year
- OS: Windows

Notes:
* plus exchange fees
** API access may cost extra

Sunday, October 18, 2009

Xasax closes shop

Six months after shutting down opentick completely Xasax (opentick's parent company) has followed suit.

It looks like Xasax hit funding problems in August... Inside Market Data mentions the above in this story. Here is the full story (subscription required).

Tuesday, September 22, 2009

Update

I can't believe it's been two months since I last posted... wow, time has a way of slipping through my fingers.  Here's a short list of some upcoming posts:
  • An introduction to LSPM -- a new R package that implements Ralph Vince's leverage space portfolio model (co-authored with Soren Macbeth).
  • Updated charting with quantmod.
  • An introduction to blotter -- a transaction-based infrastructure for trading systems and simulation, providing support for multi-asset class and multi-currency portfolios.

Sunday, July 19, 2009

David Varadi's RSI(2) alternative

Here's a quick R implementation of David Varadi's alternative to the RSI(2).  Michael Stokes over at the MarketSci blog has three great posts exploring this indicator:
  1. Varadi’s RSI(2) Alternative: The DV(2)
  2. RSI(2) vs. DV(2)
  3. Last Couple of Notes on DV(2)
Here's the R code:
 DV <- function(HLC, n=2, bounded=FALSE) {
  # "HLC" is an _xts_ object with "High", "Low", and "Close"
  # columns, in that order.
 
  # This is David Varadi's alternative to the RSI(2).  Calculations
  # taken from the marketsci blog -- http://marketsci.wordpress.com
  # Author of this implementation: Joshua Ulrich

  # Calculate each day's high/low mean
  hlMean <- rowMeans( HLC[,-3] )
 
  # Calculate the running Mean of the Close divided by the
  # high/low mean, then subtract 1.
  res <- runMean( HLC[,3] / hlMean, n ) - 1
 
  # If we want the bounded DV...
  if(bounded) {
    # Set the range to calculated the bounded DV
    rng <- 252:NROW(res)

    # Grab the index of the unbounded results, so we can convert
    # the bounded results back to an xts object.
    indx <- index(res)

    # A simple percent rank function hack
    pctRank <- function(x,i) match(x[i], sort(coredata(x[(i-251):i])))
    
    # Apply the percent rank function to the coredata of our results
    res <- sapply(rng, function(i) pctRank(res, i) / 252)

    # Convert the bounded results to xts
    res <- xts(c(rep(NA,251),res), indx)
  }

  # Return results
  return(res)
}

Sunday, June 28, 2009

RSI(2) Evaluation

Despite my best efforts, it's been a month since the last post of this series. The first post replicated this simple RSI(2) strategy from the MarketSci Blog using R. The second post showed how to replicate the strategy that scales in/out of RSI(2).

This post will use the PerformanceAnalytics package to evaluate the rules that scale in/out of positions. I've also provided a simple function that provides some summary statistics. There is a lot of code, so I put it at the end of the post.

Table 1 contains output from my simple trade summary function (the wins and losses are in percentages, i.e. 0.69 is 69 basis points). The short side of the rule traded more often and had a lower win rate. The short side overcame its lower win rate via much higher mean and median win/loss ratios.

Table 1: RSI(2) Trade Statistics - RSI steps = 5, Size steps = 0.25
Signal # Trades % Win Mean Win Mean Loss Median Win Median Loss Mean W/L Median W/L
-1.00133580.69-0.440.53-0.251.552.12
-0.75173490.62-0.390.37-0.251.601.48
-0.50143540.43-0.360.28-0.191.191.51
-0.25158560.21-0.190.14-0.131.151.08
0.0012620NaNNaNNANANaNNA
0.25117530.26-0.310.18-0.210.830.86
0.50137580.51-0.580.31-0.350.870.89
0.75143620.88-0.890.50-0.710.990.70
1.00119631.34-1.410.80-1.110.950.71

Table 2 shows the output from the PerformanceAnalytics table.Drawdowns() function. The largest percentage drawdown occurred in late 2008, but only lasted a few weeks.

The table also shows the system is prone to drawdowns that trough quickly and take months to recover from. A week of bad trades can take months to recover from.

Table 2: RSI(2) Drawdowns - RSI steps = 5, Size steps = 0.25
From Trough To Depth Length To Trough Recovery
2008-10-062008-10-102008-10-28-0.15717512
2001-08-302001-09-212002-01-23-0.091961284
2002-07-192002-07-232002-08-20-0.08823320
2000-03-222000-04-142000-07-05-0.076731855
2009-02-172009-02-232009-04-27-0.07049544
2003-03-142003-03-212003-05-09-0.05540634
2000-10-092000-10-122000-12-06-0.05242438
2002-08-292002-09-242002-10-10-0.051301812
2008-01-022008-01-222008-03-11-0.045481434
2001-04-182001-06-182001-08-10-0.045814338

Table 3 shows the output from the PerformanceAnalytics table.DownsideRisk() function. The ratio of gain/loss deviation is encouraging. I have to defer to the PerformanceAnalytics documentation and vingettes to describe the rest of the table.

Table 3: RSI(2) Downside Risk - RSI steps = 5, Size steps = 0.25
StatisticReturn
Semi Deviation0.0050
Gain Deviation0.0094
Loss Deviation0.0076
Downside Deviation (MAR=10%)0.0099
Downside Deviation (rf=0%)0.0092
Downside Deviation (0%)0.0092
Maximum Drawdown-0.1572
VaR (99%)0.0160
Beyond VaR0.0160
Modified VaR (99%)0.0705

The chart below shows the output from the PerformanceAnalytics charts.PerformanceSummary() function. It shows the equity curves and drawdown from peak for the long and short sides of the strategy. The middle graph shows the *daily* returns for the combined strategy.


The code below has everything that created the results above. It also contains the same results for a modified RSI(2) strategy. The modified strategy uses RSI steps of 10 and sizing steps of 0.3 (i.e. RSI<10 -> size=1, 10<20 -> size=0.7, etc.).

# Attach packages. You can install packages via:
# install.packages(c("quantmod","TTR","PerformanceAnalytics"))
library(quantmod)
library(TTR)
library(PerformanceAnalytics)

# Pull S&P500 index data from Yahoo! Finance
getSymbols("^GSPC", from="2000-01-01")

# Calculate the RSI indicator
rsi <- RSI(Cl(GSPC), 2)

# Calculate Close-to-Close returns
ret <- ROC(Cl(GSPC))
ret[1] <- 0

# This function gives us some standard summary
# statistics for our trades.
tradeStats <- function(signals, returns) {
# Inputs:
# signals : trading signals
# returns : returns corresponding to signals

# Combine data and convert to data.frame
sysRet <- signals * returns * 100
posRet <- sysRet > 0 # Positive rule returns
negRet <- sysRet < 0 # Negative rule returns
dat <- cbind(signals,posRet*100,sysRet[posRet],sysRet[negRet],1)
dat <- as.data.frame(dat)

# Aggreate data for summary statistics
means <- aggregate(dat[,2:4], by=list(dat[,1]), mean, na.rm=TRUE)
medians <- aggregate(dat[,3:4], by=list(dat[,1]), median, na.rm=TRUE)
sums <- aggregate(dat[,5], by=list(dat[,1]), sum)

colnames(means) <- c("Signal","% Win","Mean Win","Mean Loss")
colnames(medians) <- c("Signal","Median Win","Median Loss")
colnames(sums) <- c("Signal","# Trades")

all <- merge(sums,means)
all <- merge(all,medians)

wl <- cbind( abs(all[,"Mean Win"]/all[,"Mean Loss"]),
abs(all[,"Median Win"]/all[,"Median Loss"]) )
colnames(wl) <- c("Mean W/L","Median W/L")

all <- cbind(all,wl)
return(all)
}

# This function determines position size and
# enables us to test several ideas with much
# greater speed and flexibility.
rsi2pos <- function(ind, indIncr=5, posIncr=0.25) {
# Inputs:
# ind : indicator vector
# indIncr : indicator value increments/breakpoints
# posIncr : position value increments/breakpoints

# Initialize result vector
size <- rep(0,NROW(ind))

# Long
size <- ifelse(ind < 4*indIncr, (1-posIncr*3), size)
size <- ifelse(ind < 3*indIncr, (1-posIncr*2), size)
size <- ifelse(ind < 2*indIncr, (1-posIncr*1), size)
size <- ifelse(ind < 1*indIncr, (1-posIncr*0), size)

# Short
size <- ifelse(ind > 100-4*indIncr, 3*posIncr-1, size)
size <- ifelse(ind > 100-3*indIncr, 2*posIncr-1, size)
size <- ifelse(ind > 100-2*indIncr, 1*posIncr-1, size)
size <- ifelse(ind > 100-1*indIncr, 0*posIncr-1, size)

# Today's position ('size') is based on today's
# indicator, but we need to apply today's position
# to the Close-to-Close return at tomorrow's close.
size <- lag(size)

# Replace missing signals with no position
# (generally just at beginning of series)
size[is.na(size)] <- 0

# Return results
return(size)
}

# Calculate signals with the 'rsi2pos()' function,
# using 5 as the RSI step: 5, 10, 15, 20, 80, 85, 90, 95
# and 0.25 as the size step: 0.25, 0.50, 0.75, 1.00
sig <- rsi2pos(rsi, 5, 0.25)

# Break out the long (up) and short (dn) signals
sigup <- ifelse(sig > 0, sig, 0)
sigdn <- ifelse(sig < 0, sig, 0)

# Calculate rule returns
ret_up <- ret * sigup
colnames(ret_up) <- 'Long System Return'
ret_dn <- ret * sigdn
colnames(ret_dn) <- 'Short System Return'
ret_all <- ret * sig
colnames(ret_all) <- 'Total System Return'

# Create performance graphs
png(filename="20090606_rsi2_performance.png", 720, 720)
charts.PerformanceSummary(cbind(ret_up,ret_dn),methods='none',
main='RSI(2) Performance - RSI steps = 5, Size steps = 0.25')
dev.off()

# Print trade statistics table
cat('\nRSI(2) Trade Statistics - RSI steps = 5, Size steps = 0.25\n')
print(tradeStats(sig,ret))

# Print drawdown table
cat('\nRSI(2) Drawdowns - RSI steps = 5, Size steps = 0.25\n')
print(table.Drawdowns(ret_all, top=10))

# Print downside risk table
cat('\nRSI(2) Downside Risk - RSI steps = 5, Size steps = 0.25\n')
print(table.DownsideRisk(ret_all))

# Calculate signals with the 'rsi2pos()' function
# using new RSI and size step values
sig <- rsi2pos(rsi, 10, 0.3)

# Break out the long (up) and short (dn) signals
sigup <- ifelse(sig > 0, sig, 0)
sigdn <- ifelse(sig < 0, sig, 0)

# Calculate rule returns
ret_up <- ret * sigup
colnames(ret_up) <- 'Long System Return'
ret_dn <- ret * sigdn
colnames(ret_dn) <- 'Short System Return'
ret_all <- ret * sig
colnames(ret_all) <- 'Total System Return'

# Calculate performance statistics
png(filename="20090606_rsi2_performance_updated.png", 720, 720)
charts.PerformanceSummary(cbind(ret_up,ret_dn),methods='none',
main='RSI(2) Performance - RSI steps = 10, Size steps = 0.30')
dev.off()

# Print trade statistics table
cat('\nRSI(2) Trade Statistics - RSI steps = 10, Size steps = 0.30\n')
print(tradeStats(sig,ret))

# Print drawdown table
cat('\nRSI(2) Drawdowns - RSI steps = 10, Size steps = 0.30\n')
print(table.Drawdowns(ret_all, top=10))

# Print downside risk table
cat('\nRSI(2) Downside Risk - RSI steps = 10, Size steps = 0.30\n')
print(table.DownsideRisk(ret_all))

Tuesday, May 12, 2009

Packages featured with Inference for R

quantmod, TTR, and xts were (not so) recently featured on the Inference for R Blog. Inference for R is a Integrated Development Environment (IDE) designed specifically for R.

The post gives an example of how to easily perform advanced financial stock analysis using Inference in Excel.

I appreciate how they're making R more accessible to a general audience, even though I like a command line interface and my preferred development environment is vim. :-)

Monday, May 4, 2009

R/Finance 2009 Presentations Online

Posted to the R-SIG-Finance mailing list today:

For those who missed it, the slides for the R/Finance 2009 tutorials
and presentations are now available on RinFinance.com

http://www.RinFinance.com/presentations

We want to thank everyone who traveled to Chicago to make this happen. With nearly 200 attendees, coming from 8 countries and 20+ states in the US, the conference exceeded all of our expectations. A very big thank you to our presenters for taking the time to join us, UIC's International Center for Futures and Derivatives for hosting the event, and our sponsors REvolution Computing and Microsoft for their support.

We look forward to doing it again in the future. Stay tuned...

On behalf of the committee and sponsors.

Friday, May 1, 2009

RSI(2) with Position Sizing

Though it's more than two weeks later, here's the second post in the series that will demonstrate how to build, test, and implement a trading strategy with R. You can find the first post here.

The first post replicated this simple RSI(2) strategy from the MarketSci Blog. This second post will demonstrate how to replicate this strategy that scales in/out of RSI(2).

A couple notes before moving to the code:
  1. The rsi2pos() function isn't necessary, but it provides an example of how to define a function. Plus, it enables us to test several ideas with much greater speed and flexibility.

  2. The ifelse() function works on entire vectors at once, avoiding costly loops (loops are costly in R because it's an interpreted language). Since we can potentially modify the entire 'size' vector, we must be mindful of the order of the tests.

On to the code!
# Attach the quantmod and TTR packages.
# You can install packages via:
# install.packages(c("quantmod","TTR"))
library(quantmod)
library(TTR)

# Pull S&P500 index data from Yahoo! Finance
getSymbols("^GSPC", from="2000-01-01", to="2008-12-07")

# Calculate the RSI indicator
rsi <- RSI(Cl(GSPC),2)

# Calculate Close-to-Close returns
# (this assumes we open/close our positions
# at each day's close)
ret <- ROC(Cl(GSPC))
ret[1] <- 0

# Define our position-sizing function
rsi2pos <- function(ind, indIncr=5, posIncr=0.25) {
# Inputs:
# ind : indicator vector
# indIncr : indicator value increments/breakpoints
# posIncr : position value increments/breakpoints

# Initialize result vector
size <- rep(0,NROW(ind))

# Long
size <- ifelse(ind < 4*indIncr, (1-posIncr*3), size)
size <- ifelse(ind < 3*indIncr, (1-posIncr*2), size)
size <- ifelse(ind < 2*indIncr, (1-posIncr*1), size)
size <- ifelse(ind < 1*indIncr, (1-posIncr*0), size)

# Short
size <- ifelse(ind > 100-4*indIncr, 3*posIncr-1, size)
size <- ifelse(ind > 100-3*indIncr, 2*posIncr-1, size)
size <- ifelse(ind > 100-2*indIncr, 1*posIncr-1, size)
size <- ifelse(ind > 100-1*indIncr, 0*posIncr-1, size)

# Today's position ('size') is based on today's
# indicator, but we need to apply today's position
# to the Close-to-Close return at tomorrow's close.
size <- lag(size)

# Replace missing signals with no position
# (generally just at beginning of series)
size[is.na(size)] <- 0

# Return results
return(size)
}

# Calculate signals using the 'rsi2pos()' function
sig <- rsi2pos(rsi, 5, 0.25)

# Break out the long (up) and short (dn) signals
sigup <- ifelse(sig > 0, sig, 0)
sigdn <- ifelse(sig < 0, sig, 0)

# Calculate equity curves
eq_up <- exp(cumsum(ret*sigup)
)
eq_dn <- exp(cumsum(ret*sigdn))
eq_all <- exp(cumsum(ret*sig))

# Replicate Michael's nice chart (again)
png(filename="20090430_rsi2_replication.png")
plot.zoo( cbind(eq_up, eq_dn), plot.type="single",
ylab=c("Long","Short"), col=c("green","red"),
main="RSI(2) Strategy, with Position Scaling:\n 2000-01-03 through 2008-12-07" )
dev.off()


# Calculate signals using the 'rsi2pos()' function
# with new values
sig <- rsi2pos(rsi, 10, 0.3)

# Break out the long (up) and short (dn) signals
sigup <- ifelse(sig > 0, sig, 0)
sigdn <- ifelse(sig < 0, sig, 0)

# Calculate equity curves
eq_up <- exp(cumsum(ret*sigup)
)
eq_dn <- exp(cumsum(ret*sigdn))
eq_all <- exp(cumsum(ret*sig))

# Re-plot equity curves using updated values
png(filename="20090501_rsi2_updated.png")
plot.zoo( cbind(eq_up, eq_dn), plot.type="single",
ylab=c("Long","Short"), col=c("green","red"),
main="Updated RSI(2) Strategy, with Position Scaling:\n 2000-01-03 through 2008-12-07" )
dev.off()


Visual inspection of the charts seems to indicate the updated RSI(2) strategy has slightly higher returns, but more volatility and larger drawdowns. The next post will use the PerformanceAnalytics package to evaluate the volatility, drawdowns, and related metrics associated with these strategies. I will do my best to post it less than two weeks from now!

Tuesday, April 28, 2009

R/Finance 2009 Overview

The first international R/Finance 2009 conference in Chicago, IL was a huge success! David Smith from REvolution Computing has written a great summary of the entire event. I'll take the lazy route and point you to his blog post. :)
The first international conference dedicated to the use of R in the finance industry, R/Finance 2009, was a great success. With over 150 attendees (my poor estimation skills notwithstanding), sold-out tutorials, and an outstanding lineup of invited and contributing speakers from around the world, this event really demonstrated the importance of R in the world of financial analysis.

Source:
R/Finance 2009 roundup
David Smith
Revolutions, 4/27/2009

Friday, April 17, 2009

opentick is no more

After a year of "we plan to accept new subscribers shortly", opentick has shut its doors completely. As of March 20th, the opentick service is no longer available.

From opentick.com:

3/16/2009

To opentick subscribers, friends, supporters, contributors and the rest of the community...

It has been quite a journey for opentick, and for those of you who have been with us for the ride we cannot thank you enough for the support, contributions and guidance you have given us over the course of the last 5 years. We could not have come as far as we have without you.

However, we are sad to say that the time has come for us to close the doors for opentick. However, this isn't goodbye. In fact, it's a new beginning. We will be introducing a fresh service with all the bells and whistles we've been slaving on over the course of the last year under a new name, a new website and a new level of service. Check back here in the near future for more information about the forthcoming new company and service. Of course amongst all these changes, there are some things that will remain the same - we still aim to provide a reliable free market data service, with an open architecture for a wide range of software platform support.

If you are a current paying subscriber, this March billing cycle will be your final billing cycle; at your next billing date service will be terminated. If you are a delayed or historical data user not currently paying for service, your account will be deactivated as of Friday, March 20th.

Sincerely,
opentick Staff

One day, we shall come back. Yes, we shall come back. Until then, there must be no regrets, no tears, no anxieties. Just go forward in all your beliefs, and prove to us we are not mistaken in ours.

I'm not optimistic their new offering (most likely their sister company, Xasax) will be available anytime soon. The managers of the two companies sub-optimally managed expectations with opentick and potential Xasax customers are sure to keep that in mind.

At least I learned quite a bit creating the 'opentick' package, since it is now useless... perhaps Xasax will have a similar API, but I won't get my hopes up.

Related Posts:
Xasax closes shop
opentick alternatives

Monday, April 13, 2009

Testing RSI(2) with R, First Steps

This is the first of a series of posts that will demonstrate how to build, test, and implement a trading strategy using my favorite FOSS, R. I chose the RSI(2) strategy because it has gotten considerable attention on trading blogs over the past 6 months.

In particular, I will be replicating and extending some of the results from Michael Stokes' excellent MarketSci Blog. This post will focus on replicating this simple RSI(2) strategy.

Without further ado, let's get to some code...
# We will need the quantmod package for charting and pulling
# data and the TTR package to calculate RSI(2).
# You can install packages via: install.packages("packageName")
# install.packages(c("quantmod","TTR"))
library(quantmod)
library(TTR)

# Pull S&P500 index data from Yahoo! Finance
getSymbols("^GSPC", from="2000-01-01", to="2008-12-07")

# Calculate the RSI indicator
rsi <- RSI(Cl(GSPC),2)

# Create the long (up) and short (dn) signals
sigup <- ifelse(rsi < 10, 1, 0)
sigdn <- ifelse(rsi > 90, -1, 0)

# Lag signals to align with days in market,
# not days signals were generated
#sigup <- Lag(sigup,1) # Use lag() to avoid Toby's error
#sigdn <- Lag(sigdn,1) # Use lag() to avoid Toby's error
sigup <- lag(sigup,1) # Note k=1 implies a move *forward*
sigdn <- lag(sigdn,1) # Note k=1 implies a move *forward*

# Replace missing signals with no position
# (generally just at beginning of series)
sigup[is.na(sigup)] <- 0
sigdn[is.na(sigdn)] <- 0

# Combine both signals into one vector
sig <- sigup + sigdn

# Calculate Close-to-Close returns
ret <- ROC(Cl(GSPC))
ret[1] <- 0

# Calculate equity curves
eq_up <- exp(cumsum(ret*sigup))
eq_dn <-
exp(cumsum(ret*sigdn*-1))
eq_all <-
exp(cumsum(ret*sig))

# Replicate Michael's nice chart
plot.zoo( cbind(eq_up, eq_dn),
ylab=c("Long","Short"), col=c("green","red"),
main="Simple RSI(2) Strategy: 2000-01-02 through 2008-12-07" )

# Wait a few seconds before making next chart...
#Sys.sleep(5)

# Create a chart showing the S&P500
#chartSeries(GSPC, type="line")

# Add the total equity line
#addTA(eq_all)
My version of Michael's chart is below. Up next, scaling in/out of positions with RSI(2).

Sunday, February 15, 2009

TTR_0.2 on CRAN

I am happy to announce a long-overdue update to the TTR package (version 0.2) is now on CRAN.

This update represents a major milestone, as TTR useRs are no longer restricted to using matrix objects. TTR 0.2 uses xts internally, so all major time series classes are now supported.

NEW FEATURES:

- Added the zig zag indicator: ZigZag()

- Added volatility estimators/indicators: volatility(), with the following calculations
- Close-to-Close
- Garman Klass
- Parkinson
- Rogers Satchell

- Added Money Flow Index: MFI()

- Added Donchian channel: DonchianChannel()


CHANGES:

- All functions now use xts internally, adding support for all major time series classes. If try.xts() fails on the input object(s), they will be converted to a matrix and a matrix object will be returned.

- Added 'bounded' arg to stoch() and SMI(), which includes the current period in the calculation.

- Added naCheck() and implemented it in the moving average functions.

- Moved maType argument default values from function formals to function body for the following functions:
ADX, ATR, CCI, DPO, EMV, KST, MACD, RSI, TRIX, BBands, chaikinVolatility, stoch, SMI

- momentum() in CMO() no longer sets na=100

- Replaced 'na' argument in momentum() and ROC() with 'na.pad'

- Added 'multiple' argument to TDI(), allowing more user control

- getYahooData() now returns an xts object

- Added colnames to output for ADX, EMV, and CLV (for xts)

- Added unit tests using the RUnit package
- Used checkEquals on object attributes as well as values

- Removed .First.lib function and added .onLoad with package version.


BUG FIXES:

- Corrected NaN replacement in CLV()

- Corrected williamsAD(): AD=0 if C(t)=C(t-1)

- Corrected runMedian() and runMAD(). The argument controlling which type of median to calculate for even-numbered samples wasn't being passed to the Fortran routine.

- aroon() calculation starts at period n+1, instead of n

- Added NA to first element of closeLag of ATR()

- Corrected BBands() and CCI() for rowMeans use on xts objects

- Made changes to Rd files to pass R CMD check on R-devel (2.9.0)


Please do contact me with any questions, concerns, bug reports, etc.

Saturday, January 17, 2009

IBrokers Featured on Quantitative Trading

Jeff Ryan's IBrokers package was mentioned on Ernie Chan's blog, Quantitative Trading. Though the package is still in pre-alpha stage, it is generating quite a bit of interest.

Source:
Ernie Chan
Friday, January 16, 2009
Quantitative Trading: Algorithmic Trading Technology Update