R – Multi Day Hold Trading Logic – Replacing a for loop to back test multi hold day trading rules

I wanted to expand on some trading logic written over at FOSS trading blog. Joshua demonstrates how to back test 1 day hold strategies. Here is an example of one of his back test scripts.

RSI2 Back Test Script

http://blog.fosstrading.com/2009/04/testing-rsi2-with-r.html

We can look at one of the trading rules from the above back test script:

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

This is basically saying if rsi is below 10 go long, any time its not over 10 get out (it means you cant hold from 10 all way up to 90 for example). Then for going short, we short over 90 and any time rsi is not over 90 we are not short ( this means we cant short over 90 and hold all way to 10 for example)

The above long and short rules are essentially designed mostly for short term trading or 1 day trading hold times.

Lets expand on the above example and create a multi day trading rule.

We will use R and use a dummy data set to simulate an indicator:


# Random Indicator
any.indicator <- c(runif(1500, min=0, max=100))   #create random numbers between 0 and 100, create 1500 data points between that range
df <- data.frame(any.indicator)   # place the vector above into a data frame

# Create Entry and Exit Rule
# Ifelse statement (print 1 else 0)
# We want to buy when any.indicator is below 10, when below 10 we want to buy so signal.enter will = 1
# We want to exit our trade when any.indicator is over 90, when over  90 we want to sell so signal.exit will = 1
# This sets the boundary for our multi day hold
df$signal.enter <- ifelse(any.indicator < 10, 1,0)  # Buy when indicator is over 0
df$signal.exit <- ifelse(any.indicator > 90, 1,0)   # Sell when indicator is less than 0.  

# Generate Multi Day Hold Trading logic # Use this for loop
# This will find the first 1, in df$signal.enter, it will continue to loop until # we meet a df$signal.exit = 1. During our entry / exit, the loop will create a # df$signal == 1 on each row of the data frame so that we can back test multi
# day hold trades. Once we exit, the we will print 0's until we meet another
# df$signal.enter == 1.

df$signal[[1]] = ifelse(df$signal.enter[[1]] == (1), 1, 0)

for (i in 2:nrow(df)){
  df$signal[i] = ifelse(df$signal.enter[i] == (1), 1,
                              ifelse(df$signal.exit[i] == (1), 0,
                                     df$signal.exit[i-1]))
}

The code above uses a for loop to create entry and exits for multi day hold trades. In this example we are buying any.indicator when its below 10 and selling when its over 90 . Or we can short when its above 90 and close the short when it reaches 10. The for loop above will become quite slow during large data sets. We can keep the ‘vectorized theme’ of R and we can use dplyr to replace the loop. This will speed up the code.

# Dplyr solution
library(dplyr) df %>%
  dplyr::mutate(signal = ifelse(signal.enter == 1, 1,
                    ifelse(signal.exit ==1, 0, 0)))

The code is more compact and produces the exact same result as the for loop above. It also runs much faster on larger data sets.

Full code with the dplyr solution replacing the for loop for multi day trading rules:


# Create random indicator
any.indicator <- c(runif(1500, min=0, max=100))
df <- data.frame(any.indicator)

# Ifelse statement (print 1 else 0)
# Create entry and exit signals
df$signal.enter <- ifelse(any.indicator < 10, 1,0)  # create enter signal
df$signal.exit <- ifelse(any.indicator > 90, 1,0) # create exit signal

# Multi day hold trading rules
library(dplyr)

df %>%
  dplyr::mutate(signal = ifelse(signal.enter == 1, 1,
                    ifelse(signal.exit ==1, 0, 0)))

Author: Andrew Bannerman

Integrity Inspector. Quantitative Analysis is a favorite past time.

2 thoughts on “R – Multi Day Hold Trading Logic – Replacing a for loop to back test multi hold day trading rules”

  1. Note that you need to be careful when using dplyr with time-series, because dplyr::lag() masks the stats::lag() generic function and also prevents method dispatch. So calling lag() on a “ts”, “xts”, “zoo”, etc object might not do what you expect.

    Like

    1. Hi Joshua. Thanks for your note. For now I work everything in data frames and convert to “ts”, “xts”, “zoo” at the very end of a back test script in order be able to use PortfolioAnalytics package. I only export the date / equity curve as time series objects. For now, I have not noticed the lag not working and I check my .csv file each time. Not sure if I would run into complications with it specifically with time series objects.

      Like

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 )

Google+ photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s