Skip to content

Instantly share code, notes, and snippets.

@timelyportfolio
Created November 29, 2011 15:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timelyportfolio/1405187 to your computer and use it in GitHub Desktop.
Save timelyportfolio/1405187 to your computer and use it in GitHub Desktop.
Improved Moving Average R Function
# Function to implement a version of the improved moving average functionality
# introduced and tested in:
#
# Papailias, Fotis and Thomakos, Dimitrios D.,
# "An Improved Moving Average Technical Trading Rule,
# (September 11, 2011). Available at SSRN: http://ssrn.com/abstract=1926376
#
# Original code written by Kent Russell @ timelyportfolio.com
#
# Cross-checked by Dimitrios Thomakos on 11/29/2011, @ quantf.com
# Cross-checked by Fotis Papailias on 11/30/2011, @quantf.com
#
# NOTE: this function does not perform the exact methodology
# of the paper and results in more trades. Is given for
# public use for research and back-testing purposes with
# no guarrantees whatsoever!!!
#
# Comment by Fotis Papailias: More care must be taken regarding
# the entry prices.
#
# All rights reserved & copyright by the above authors.
#
# Main function with additional comments & minor changes
#
# The research uses daily data, but the function can be used
# for any periodicity
MAImp <- function (x, k1=20, k2=0)
{
# Cross-checks
if ((k1 < 0) | (k2 < 0) | ((k1 > k2) & (k2 > 0))) { stop("k1,k2 must be positive and k1 < k2") }
# Select correctly the closing price if OHLC is loaded
if(NCOL(x) > 1) x <- x[,4]
# Get shorter moving average; will use this as the only
# moving average if not crossover and will assume no
# crossover if k2 is not defined
#
MAshort <- runMean(x,n=k1)
if( k2!=0 ) { MAlong <- runMean(x,n=k2) }
else if (k2 == 0)
{
# Swap MAshort for MAlong and assign MAshort as x series
# Do this so we can use the same function later for the
# evaluation of trading signals
MAlong <- MAshort
MAshort <- x
}
# Get the standard MA signals
signal.ma <- ifelse(MAshort > MAlong, 1, 0)
# Get the entry values for the trailing stop defined in the paper;
# note the difference with the above...
x.entry <- ifelse((MAshort > MAlong) & (lag(MAshort,k=1) < lag(MAlong,k=1)), 1, 0) * x
# This little block will fill in the entry x
#
#I have still have not found a better way to accomplish
#takes longer than I would like
# Select the values of modified cross-over signals only
entry <- x.entry[which(x.entry!=0)]
# Loop over these values to compute the trailing stop-based signals
for (i in 1:NROW(entry))
{
ifelse (i < NROW(entry),
x.entry[paste(index(entry)[i],index(entry)[i+1],sep="::"),] <- entry[i],
x.entry[paste(index(entry)[i],sep="::"),] <- entry[i])
}
# Compute and return the modified signal as well as the standard signal
signal.mod <- ifelse(x >= x.entry, 1, 0) # Fotis: Strange things here....
#the "strange things" are without changes later in this function
#is this signal will reenter whenver closing price crosses
#back above the entry price
#after converstation with the authors
#I realized that the system should not reenter until
#the standard ma or ma crossover exits and enters as a different signal
###changed here to insure that crossover still intact
###to try a different version
###remove comment on next line if you would like to experiment
###I prefer the results from this next signal
#signal.mod <- ifelse(x >= x.entry & signal.ma==1 , 1, 0)
#add additional logic to insure consistency with research paper
#if exit on improved signal there is no reentry until
#a new crossover
simple.entry <- ifelse((MAshort > MAlong) & (lag(MAshort,k=1) < lag(MAlong,k=1)), 1, 0)
improved.exit <- ifelse(signal.mod==0 & lag(signal.mod,k=1)==1,-1,0)
simple.date <- simple.entry[which(simple.entry!=0)]
improved.date <- improved.exit[which(improved.exit!=0)]
#initialize this adjusted as signal.mod (varition of implementation)
#signal.mod.adj will be the the paper's implementation
signal.mod.adj <- signal.mod
j=1
for (i in 1:NROW(improved.date)) {
#need two counters i and j for this to work
#since the standard ma (simple.date) and the improved(improved.date)
#are not aligned
while (index(improved.date)[i]>index(simple.date)[j])
if(j+1 < NROW(simple.date))
j=j+1
else break #exit while loop if we are at the end
#print(paste(i,j))
#turn off all reenter 1s since reentry on the improved
#should only occur when the standard crosses below/exits and
#standard crosses above/enters
signal.mod.adj[paste(index(improved.date)[i],index(simple.date)[j],sep="::"),1]=0
#fill 1 for the first day of simple.date to align signals
#if we do not do this then improved will always lag by 1
#which might amount to foresight on choppy entries
signal.mod.adj[index(simple.date)[j],1]=1
}
#since the signal.mod is now my own
#I will introduce the change I mentioned above
#to reenter on the improved only when standard simple is still intact
signal.mod <- ifelse(x >= x.entry & signal.ma==1 , 1, 0)
signals <- merge(standard=signal.ma,modified=signal.mod,modified.correct=signal.mod.adj)
colnames(signals) <- c("Simple","Improved-variation","Improved-original")
#I think returning xts makes it easier to calculate returns
#the return will be three signals
#first column will be standard simple moving average or ma crossover
#second column will be my unintentional variation of the signal
#third column will be the signal introduced and tested by the paper
return(signals)
}
##here is a sample of use of the function
#require(quantmod)
#require(PerformanceAnalytics)
#
#getSymbols("^GSPC", from="1896-01-01", to=Sys.Date())
##use 200-day moving average cross
#signals <- MAImp (x=GSPC, k1=200)
#ret <- merge(rep(ROC(GSPC[,4],type="discrete",n=1),3) * lag(signals,k=1),ROC(GSPC[,4],type="discrete",n=1))
#colnames(ret) <- c(colnames(signals),"BuyHold")
#charts.PerformanceSummary(ret,ylog=TRUE)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment