Parameter Optimisation & Backtesting – Part 2

This is a follow on from:

The code presented here will aim to optimise a strategy based upon the simple moving average indicator. The strategy will go Long when moving average A > moving average B. The optimisation is to determine what period to make each of the moving averages A & B.

Please note that this isn’t intended to be a good strategy, it is merely here to give an example of how to optimise a parameter.

Onto the code:


  • TradingStrategy this function implements the trading logic and calculates the returns
  • RunIterativeStrategy this function iterates through possible parameter combinations and calls TradingStrategy for each new parameter set
  • CalculatePerformanceMetric takes in a table of returns (from RunIterativeStrategy) and runs a function/metric over each set of returns.
  • PerformanceTable calls CalculatePerformanceMetric for lots of different metric and compiles the results into a table
  • OrderPerformanceTable lets us order the performance table by a given metric, ie order by highest sharpe ratio
  • SelectTopNStrategies selects the best N strategies for a specified performance metric (charts.PerformanceSummary can only plot ~20 strategies, hence this function to select a sample)
  • FindOptimumStrategy does what it says on the tin
Note that when performing the out of sample test, you will need to manual specify the parameter set that you wish to use.
?View Code RSPLUS
nameOfStrategy <- "GSPC Moving Average Strategy"
#Specify dates for downloading data, training models and running simulation
trainingStartDate = as.Date("2000-01-01")
trainingEndDate = as.Date("2010-01-01")
outofSampleStartDate = as.Date("2010-01-02")
#Download the data
symbolData <- new.env() #Make a new environment for quantmod to store data in
getSymbols("^GSPC", env = symbolData, src = "yahoo", from = trainingStartDate)
trainingData <- window(symbolData$GSPC, start = trainingStartDate, end = trainingEndDate)
testData <- window(symbolData$GSPC, start = outofSampleStartDate)
indexReturns <- Delt(Cl(window(symbolData$GSPC, start = outofSampleStartDate)))
colnames(indexReturns) <- "GSPC Buy&Hold"
TradingStrategy <- function(mktdata,mavga_period,mavgb_period){
  #This is where we define the trading strategy
  #Check moving averages at start of the day and use as the direciton signal
  #Enter trade at the start of the day and exit at the close
  #Lets print the name of whats running
  runName <- paste("MAVGa",mavga_period,".b",mavgb_period,sep="")
  print(paste("Running Strategy: ",runName))
  #Calculate the Open Close return
  returns <- (Cl(mktdata)/Op(mktdata))-1
  #Calculate the moving averages
  mavga <- SMA(Op(mktdata),n=mavga_period)
  mavgb <- SMA(Op(mktdata),n=mavgb_period)
  signal <- mavga / mavgb
  #If mavga > mavgb go long
  signal <- apply(signal,1,function (x) { if({ return (0) } else { if(x>1){return (1)} else {return (-1)}}})
  tradingreturns <- signal * returns
  colnames(tradingreturns) <- runName
  return (tradingreturns)
RunIterativeStrategy <- function(mktdata){
  #This function will run the TradingStrategy
  #It will iterate over a given set of input variables
  #In this case we try lots of different periods for the moving average
  firstRun <- TRUE
    for(a in 1:10) {
        for(b in 1:10) {
          runResult <- TradingStrategy(mktdata,a,b)
              firstRun <- FALSE
              results <- runResult
          } else {
              results <- cbind(results,runResult)
CalculatePerformanceMetric <- function(returns,metric){
  #Get given some returns in columns
  #Apply the function metric to the data
  print (paste("Calculating Performance Metric:",metric))
  metricFunction <-
  metricData <- as.matrix(metricFunction(returns))
  #Some functions return the data the wrong way round
  #Hence cant label columns to need to check and transpose it
  if(nrow(metricData) == 1){
    metricData <- t(metricData)
  colnames(metricData) <- metric
  return (metricData)
PerformanceTable <- function(returns){
  pMetric <- CalculatePerformanceMetric(returns,"colSums")
  pMetric <- cbind(pMetric,CalculatePerformanceMetric(returns,"SharpeRatio.annualized"))
  pMetric <- cbind(pMetric,CalculatePerformanceMetric(returns,"maxDrawdown"))
  colnames(pMetric) <- c("Profit","SharpeRatio","MaxDrawDown")
  print("Performance Table")
  return (pMetric)
OrderPerformanceTable <- function(performanceTable,metric){
return (performanceTable[order(performanceTable[,metric],decreasing=TRUE),])
SelectTopNStrategies <- function(returns,performanceTable,metric,n){
#Metric is the name of the function to apply to the column to select the Top N
#n is the number of strategies to select
  pTab <- OrderPerformanceTable(performanceTable,metric)
  if(n > ncol(returns)){
     n <- ncol(returns)
  strategyNames <- rownames(pTab)[1:n]
  topNMetrics <- returns[,strategyNames]
  return (topNMetrics)
FindOptimumStrategy <- function(trainingData){
  #Optimise the strategy
  trainingReturns <- RunIterativeStrategy(trainingData)
  pTab <- PerformanceTable(trainingReturns)
  toptrainingReturns <- SelectTopNStrategies(trainingReturns,pTab,"SharpeRatio",5)
  charts.PerformanceSummary(toptrainingReturns,main=paste(nameOfStrategy,"- Training"),geometric=FALSE)
  return (pTab)
pTab <- FindOptimumStrategy(trainingData) #pTab is the performance table of the various parameters tested
#Test out of sample
#Manually specify the parameter that we want to trade here, just because a strategy is at the top of
#pTab it might not be good (maybe due to overfit)
outOfSampleReturns <- TradingStrategy(testData,mavga_period=9,mavgb_period=6)
finalReturns <- cbind(outOfSampleReturns,indexReturns)
charts.PerformanceSummary(finalReturns,main=paste(nameOfStrategy,"- Out of Sample"),geometric=FALSE)

9 thoughts on “Parameter Optimisation & Backtesting – Part 2

  1. Hello from Poland!
    Excellent job. Your code is not only very well commented (which is not common) it’s also WELL DESCRIBED.
    Few months ago I dropped R but now I comming back to it.

    Many thanks for sharing your work.


  2. Pingback: Trend Following – Skewness Signal | Gekko Quant – Quantitative Trading

  3. Hi,
    Great example. Just one question, why do you sum returns (using colSums)? These are simple returns should you be multiplying them to account for compounding? Or am I missing something? Similarly should not you being using geometric chaining in the PerformanceAnalytics?

    Thanks and keep up the good work

    • Hi Husvar,

      Just quickly looking at the comments in my code it says that it goes long on the open and closes the position at the close.The choice of using arithmetic or geometric returns is down to your personal preference. Some people like to put their winnings into their next trade, others dont.

      • hi!
        I have the same question. If you use geometric=FALSE thats mean you need to use log(Cl(price)/Op(price)). Or Im wrong? Can you give your explain, how to use geometric/arithmetic and log/absolute returns.

  4. Sorry for my ignorance, but how do I interpret the “Out of Sample” graphic? What does “Buy&Hold” mean and what the direction (+0 / -0) mean right there? Thanks!

    • Also, I question why your SMA strategy applies to the “Open” (Op) price… is that a good reason to repeat in order strategies? (CCI, MACD,etc)

Leave a Reply

Your email address will not be published. Required fields are marked *