Technical Trading Strategy Analysis & Backtesting – Intro to Quantstrat

   Given historical price/volume data for any exchange traded ticker symbol, widely available on the web, either free or via paid subscription,  we saw earlier how the TTR package can spew out any number of common technical indicators used often in developing and implementing trading strategies.

   Indeed there are many sites on the web that provide space and resources to examine trading strategies.  Quantconnect has a free signup and if one is somewhat familiar with C## although there is a Python interface as well can start using their library of user submitted algorithms. Personally, I had some difficulty in installing and running their desktop version.  The site has a very active forum with lots of examples, quite a few just concentrating on technical analysis.  In a similar vein Quantopian provides many of the same features for setting up strategies and backtesting – this time in a Python setting.  Not to be outdone, TradingView is another similar site with a free component, but here one has to learn what is known as the “pine” script.  But there are some impressive algorithms with fantastic sounding names such as this  RSI Alligator which uses RSI and the Williams indicator together (with due attribution) – actually quite simple to understand.

    All of the above are quite excellent places to visit and hone one’s skills.  However, adding a capability to do some further in-depth independent analysis on a personal desktop in a language such as R definitely increases one’s confidence. 

  In order to test a strategy or set of strategies it is essential to have some software that can do all the backtesting and derive key performance metrics that can help one gauge success or failure possibilities.  Enter R package quantstrat,  a package specifically designed to do just this.

   There are many good references available. This is good for a start.   Another little more deeper intro is a presentation from an R conference.   There is however, a lack of detailed technical documentation but several sites have some good examples that cover a variety of situations.  A reasonably complete one is here,  followed by one whichs explore some of the more interesting features such as stoploss/stoptrailing type of trades, as well as testing various different sets of indicator or other parameters (from Guy Yollin at the Univ of Washington) are provided.  The quantstrat package itself comes with many sample strategies and programs.

    At this time, quantstrat is not available from a CRAN site but must be installed from github quite easily by doing the following:

install.packages("devtools")
require(devtools)
install_github("braverock/blotter") # dependency
install_github("braverock/quantstrat")

Alternately, package can be installed from a zip file obtained from here.   Or it can also be downloaded from here as a zip file and installed thereafter.  Installing in R Studio Console is quite simple.    Open tab Tools; then Install Packages and then  choose Package Archive File from “Install From” dropdown  menu of Install.  Enter  Install from zip and follow directions to redirect to the folder where zip is downloaded,select it and all done.

For all of our backtest programs going forward, we will follow this architecture in terms of setting up the code:

   Next a template will be provided to perform backtest runs.  A very simple strategy that just looks at a moving average, the EMA in this case.  Only long positions will be shown for now.  Entry is whenever the Close price moves above the n-period moving average ( we can pick any number for n or leave it at default value).  Exit from trade is when Close goes below this average.

    Blocks of reusable code are presented below which we can resuse for more complex strategies going forward.  They should be kept in separate files and sourced into the template code. The example below is a very simple example and the references cited have many more. 

########################################################################
####
#### Quanstrat Custom Script
#### “Z:\WorkSpace\R\Project0\Quantstrat\QS_CLEANSTART.r”
####
########################################################################

 

#### Clean up environments
### and start afresh
### Note we are using the same names
### for every scenario test
########################################
rm(list=c(“.blotter”, “.strategy”))
.blotter <<- new.env()
.strategy <<- new.env()

 

## Clean up previous

 

require(quantstrat)
require(quantmod)
setDefaults(“getSymbols.tiingo”, api.key = “…. your key here …..”)

 

### standard objects in the sub-environments
suppressWarnings(rm(“order_book.QS_MAINDRIVER”,pos=.strategy))
suppressWarnings(rm(“account.QS_MAINDRIVER”,“portfolio.QS_MAINDRIVER”,pos=.blotter))

 

## variables used in backtest program
suppressWarnings(rm(“acctName”,“portfName”, “stratName”,
“Symbols”, “startDate”, “startEquity”, “outQS”,
‘start_t’, ‘end_t’))

 

########################################
# Set the currency and the timezone

 

##Sys.setenv(TZ = “UTC”)
currency(‘USD’)

 

########################################
########################################################################
####
#### Quanstrat Custom Functions
#### “Z:\WorkSpace\R\Project0\Quantstrat\QS_CUSTFUNCS.r”
####
########################################################################
osMaxDollar <- function(data, timestamp, orderqty, ordertype, orderside,
portfolio, symbol, prefer=“Close”, tradeSize,
maxSize, integerQty=TRUE,
) {
pos <- getPosQty(portfolio, symbol, timestamp)
if(prefer==“Close”) {
price <- as.numeric(Cl(mktdata[timestamp,]))
} else {
price <- as.numeric(Op(mktdata[timestamp,]))
}
posVal <- pos*price
if (orderside==“short”) {
dollarsToTransact <- max(tradeSize, maxSizeposVal)
#If our position is profitable, we don’t want to cover needlessly.
if(dollarsToTransact > 0) {dollarsToTransact=0}
} else {
dollarsToTransact <- min(tradeSize, maxSizeposVal)
#If our position is profitable, we don’t want to sell needlessly.
if(dollarsToTransact < 0) {dollarsToTransact=0}
}
qty <- dollarsToTransact/price
if(integerQty) {
qty <- trunc(qty)
}
return(qty)
}
##########################################################################
## get price history data
### check if we already have it – else get
### Note location may need to be changed to suit
### uses “Z:/WorkSpace/R/SavedObjects/mktdata_%SYMBOL%.rsavedata” here!
### make sure tiingo api key is set
##########################################################################
getPriceData <- function(Symbols = Symbols) {
for (symbol in Symbols ) {
savedMktdata = gsub(“%SYMBOL%”, symbol, “Z:/WorkSpace/R/SavedObjects/mktdata_%SYMBOL%.rsavedata”)
## get price data and ensure start dates align
skip<<-0 ## need global because doesn’t get set in error block
if ( file.exists(savedMktdata ) ) {
dat = readRDS(savedMktdata)
} else {
## dont have data – lets get it from tiingo as much as we can
## need to trap if not found
tryCatch (
dat <- getSymbols(symbol,from=“1980-01-01” ,index.class=c(‘POSIXt’,‘POSIXct’), src=‘tiingo’, auto.assign = FALSE)
, error= function(err) {
print (sprintf(” Tiingo Price data for %s not found”, symbol) )
skip <<- 1
}
)
if ( skip == 0 ) saveRDS(dat , file = savedMktdata)
}
## now just get period we need for backtest
if ( skip == 1 ) next
dat = dat[paste(startDate,“::”, sep=“”)]
assign(symbol, dat, envir = .GlobalEnv)
}
}
 
##########################################################################
## Calling functions
### trade statistics
##########################################################################
printRunStats<- function(portfName) {
tStats <- tradeStats(Portfolios = portfName, use=“trades”, inclZeroDays=FALSE)
tStats[,4:ncol(tStats)] <- round(tStats[,4:ncol(tStats)], 2)
print(data.frame(t(tStats[,c(1,2)])))
(aggPF <- sum(tStats$Gross.Profits)/-sum(tStats$Gross.Losses))
(aggCorrect <- mean(tStats$Percent.Positive))
(numTrades <- sum(tStats$Num.Trades))
(meanAvgWLR <- mean(tStats$Avg.WinLoss.Ratio[tStats$Avg.WinLoss.Ratio < Inf], na.rm=TRUE))
print(sprintf (” Aggregate Profit Ratio is %f “ , aggPF ) )
print(sprintf (” Percentage Positive Trades is %f “, aggCorrect) )
print(sprintf (” Total Number trades is %d “,numTrades) )
print(sprintf (” Mean Win-Loss Ratio is %f “,meanAvgWLR) )
## View(getOrderBook(portfName)[[portfName]]$DVY ) # << note ticker is hard coded here!
}
#### save strategy output as .RData file
### passid in:
### saveLocation, results object
## saveDir = “z:/Opensource/RStudio/SavedRObjects/”
saveStratObject <- function(saveDir, stratName = stratName, results = results, id = NULL) {
if (!is.null(id)) id = paste(id, “_”, sep=“”)
results_file <- paste(saveDir, stratName, “_”, id, gsub(“-“, “”, Sys.Date()), “.RData”, sep = “”)
#save(list = “results”, file = results_file)
save.image(results_file)
}
# ls(.blotter)[grep(“account”, ls(.blotter))]
## [1] “account.Acct_MACD_TS”
## if always doing clean run then can just grep for account
### this assumes clean run every time
### and that blotter will only have 1 one account… object
getEndEquityFromBlotter <- function( period=“years” ) {
stEndEq = .blotter[[ls(.blotter)[grep(“account”, ls(.blotter))]]]$summary$End.Eq
stEndEq$End.Eq[endpoints(stEndEq, on=period)]
}
showOrderHist <- function(portfName, symbol, nbr=0, allSigs = NULL, matchSig = NULL ) {
obk = getOrderBook(portfName)[[portfName]][[ symbol ]]
print( sprintf(” No of rows in orderbook for Symbol %s is %d”, symbol, nrow(obk)) )
orderCols = c(“Order.Qty”, “Order.Price”, “Order.Type”, “Order.Side”, “Order.Threshold”, “Order.Status”, “Rule”)
obk = obk[,orderCols]
## to remove timestamp
index(obk) <- as.Date(unlist(lapply(strsplit(as.character(index(obk)),” “),function(x) x[1])))
if (is.null(matchSig) ) {
print( sprintf(” All orderbook items shown below”) )
if ( nbr == 0) tail(obk)
else {
print ( sprintf( ” Showing last %d number of orders” , nbr) )
nbr = ifelse(nbr > nrow(obk), 1, nrow(obk) nbr + 1 )
obk[nbr: nrow(obk)]
}
} else {
out = NULL
for ( ix in allSigs) {
if (ix$label %in% matchSig) {
sigVals = ix$signal
index(sigVals) = as.Date(unlist(lapply(strsplit(as.character(index(sigVals)),” “),function(x) x[1])))
print( sprintf(” Matching Orderbook items for symbol %s Triggered by Signal %s” , symbol, ix$label) )
stk = paste0(symbol, “.signal”)
sigVals = sigVals[which(sigVals[,stk] == 1), stk]
if (is.null(out)) out = sigVals else out = rbind(out, sigVals)
}
}
obk[ index(out) ]
 
}
}
 
### orig version of checkSignals NO!!!
### this does NOT work because of the .strategy environment – wrong args for subseeting an environment
##checkSignals <- function(stratName = stratName, env = caller_env() ) {
## print( unlist( lapply( .strategy[[stratName]]$signals, function(x) x$label) ) )
## res = lapply( .strategy[[stratName]]$signals, function(x) applyIndicatorSignals(stratName, portfName, sigcol= x$label) )
## }
### this works but needs all args to be passed in !!!!!
checkSignals <- function(envObj, stratName, portfName ) {
print( unlist( lapply( envObj$signals, function(x) x$label) ) )
#lapply( envObj$signals, function(x) applyIndicatorSignals(stratName, portfName, sigcol= x$label) )
lapply( envObj$signals, function(x) return(list(label=x$label, signal =applyIndicatorSignals(stratName, portfName, sigcol= x$label) ) ) )
}
############# Check Blotter Update
checkBlotterUpdate <- function(portfName,acctName,verbose=TRUE)
{
ok <- TRUE
p <- getPortfolio(portfName)
a <- getAccount(acctName)
syms <- names(p$symbols)
port.tot <- sum(sapply(syms,FUN = function(x) eval(parse(
text=paste(“sum(p$symbols”,x,“posPL.USD$Net.Trading.PL)”,sep=“$”)))))
port.sum.tot <- sum(p$summary$Net.Trading.PL)
if( !isTRUE(all.equal(port.tot,port.sum.tot)) ) {
ok <- FALSE
if( verbose )
print(“portfolio P&L doesn’t match sum of symbols P&L”)
}
initEq <- as.numeric(first(a$summary$End.Eq))
endEq <- as.numeric(last(a$summary$End.Eq))
if( !isTRUE(all.equal(port.tot,endEqinitEq)) ) {
ok <- FALSE
if( verbose )
print(“portfolio P&L doesn’t match account P&L”)
}
if( sum(duplicated(index(p$summary))) ) {
ok <- FALSE
if( verbose )
print(“duplicate timestamps in portfolio summary”)
}
if( sum(duplicated(index(a$summary))) ) {
ok <- FALSE
if( verbose )
print(“duplicate timestamps in account summary”)
}
return(ok)
}
########################################################################
####
#### Quanstrat Custom Script
#### “Z:\WorkSpace\R\Project0\Quantstrat\QS_CLEANSTART.r”
####
########################################################################


####################################
### Quantstrat CONSTANTS
### Can use this to set all the default values
### For commonly used indicators etc.,
### example only
####################################

ctlTxnFee=0 # keep zero for testing initially
ctlThreshold = 0.0005 # as percentage so tmult must be set TRUE in rules
ctlOrderqty = 100

##

fastMA = 12
slowMA = 26
signalMA = 9


## for bbands test
maType=‘SMA’
n = 20
sdp = 2


#########
 

   The template code for the actual run is shown below. For this example we will be using all of the sector ETFs as our test symbols for the trades.  Also every trade incurs a transaction fee of $5.

#########################################################
#
########## Template to Run the Quantstrat Backtests #############
#
##########################################################

########################################
### History of changes
## Publish Version 05 Feb 2019
########################################


#### clean up environments – start afresh

source(“Z:/WorkSpace/R/Project0/Quantstrat/QS_CLEANSTART.r”)
source(“Z:/WorkSpace/R/Project0/Quantstrat/QS_CUSTFUNCS.r”)
#########################################
### What symbols are we testing one or many
### Note ticker symbols ‘T’ (ATT) and ‘F’ for Fors will give problems in getSymbols
#########################################

oldtz<-Sys.getenv(‘TZ’)
if(oldtz==) {
      Sys.setenv(TZ=“GMT”)
}

Symbols = c(“XLF”, “XLP”, “XLE”, “XLY”, “XLV”, “XLI”, “XLB”, “XLK”, “XLU”)
stock(Symbols,currency=‘USD’,multiplier=1)
### for testing can use just a few of the above

###################################
## parameters for run
## StartDate; startEquity; tradeSize
## Strategy/Portfolio/Account Name to use for Run
###################################

startDate=‘2010-01-01’
startEquity=1000000
tradeSize = startEquity / length(Symbols)

QstRunId=‘QS_MAINDRIVER’

stratName<-portfName<-acctName<-QstRunId

########################################
### swicthes for debugging / logging – set to FALSE
#######################################
LOG_SW=FALSE
DBUG_SW = TRUE
bypass_pkg=FALSE ## <<– apply ind/signals/ separately for debugging & avoind going to package code

###################################
#### scenario specific variables – depends on indicators used
#### Indicators have specific variable names
#### Can override defaults by providing overrides here or
#### in indicator setup
###################################
stdDev = 2.4 # how many standard deviations, traditionally 2
maPeriod = 20 # how many periods for the moving average, traditionally 20

### set the maximum position size for any symbol
### will be used when processing orders & trades
ordPosLimit = 1000

###################################
##
## STRATEGY / BACKTEST PROCESSING
##
###################################

#################################################
## get historic price data for symbols
source(“Z:/WorkSpace/R/Project0/Quantstrat/QS_GETDATA.r”)

#################################################
## Initialize portfolio, account & orderbook
initPortf(portfName,symbols=Symbols, startDate=startDate)
initAcct(acctName,portfolios=portfName, startDate=startDate, initEq = startEquity )
initOrders(portfolio=portfName,startDate=startDate)

### set position constarints for symbols
for (symbol in Symbols)
addPosLimit(portfName, symbol, startDate, ordPosLimit , 1 ) #set max pos
# define and store the strategy with name=stratName
strategy(stratName, store=TRUE)
##################################################
## Bring in the source control files for various elements
## general constants & parameters
source(“Z:/WorkSpace/R/Project0/Quantstrat/QS_CONST.r”)

##################################################
## setup indicators for scenario tested
## set the override period / for default “n=…” can be omitted
##################################################
#add.indicator(stratName, name = “EMA”, arguments = list(x = quote(Cl(mktdata)), n=maPeriod), label=”EMA20″)
add.indicator(stratName, name = “EMA”, arguments = list(x = quote(Cl(mktdata)) ), label=“EMA20”)
##################################################
## setup signals for scenario tested
##################################################
add.signal(stratName, name=“sigCrossover”,arguments = list(columns=c(“Close”,“EMA.EMA20”),relationship=“gt”),label=“EntryCond”)
add.signal(stratName, name=“sigCrossover”,arguments = list(columns=c(“Close”,“EMA.EMA20”),relationship=“lt”),label=“ExitCond”)

##################################################
## setup rules for scenario tested
##################################################
## condition for entry into trade
add.rule(stratName, name=‘ruleSignal’,
arguments = list(sigcol=“EntryCond”,
sigval=TRUE,
orderqty=ordPosLimit,
ordertype=‘market’,
orderside=‘long’,
tradeSize = tradeSize,
pricemethod=‘market’,
TxnFees=-5), type=‘enter’, path.dep=TRUE)
# The second is to sell when the price crosses below the SMA
add.rule(stratName, name=‘ruleSignal’,
arguments = list(sigcol=“ExitCond”,
sigval=TRUE,
orderqty=‘all’,
ordertype=‘market’,
orderside=‘long’,
pricemethod=‘market’,
TxnFees=-5), type=‘exit’, path.dep=TRUE)

#########################################
### Now apply the strategy
#########################################
start_t<-Sys.time()

out_QS<-applyStrategy(strategy=stratName,
portfolios=portfName,
symbols = Symbols,
debug=DBUG_SW,verbose=LOG_SW)

end_t<-Sys.time()
print(end_tstart_t)
#################################################
## update protfolio/account etc.,
#################################################
updatePortf(Portfolio=portfName,Dates=paste(‘::’,as.Date(Sys.time()),sep=))
updateAcct(acctName)
updateEndEq(acctName)

##################################################
## show run stats function
### printRunStats is in “Z:/WorkSpace/R/Project0/Quantstrat/QS_CUSTFUNCS.r”)
## Provide summary
##################################################

printRunStats(portfName)

### reset timezone back to existing
Sys.setenv(TZ=oldtz)

#####################################################################
######### End of Quanstrat Template Model #######################
#####################################################################


 

   Results can be printed with the printRunStats custom command and a flavor of the metrics collected are shown below.  We will explore these in greater detail along with visual charts.

   For now, it can be noted that the performance of this strategy is actually quite poor as can be seen from the numbers.  The end equity numbers are less than impressive over the period of 8 years or so.  On top of all that, a large number of trades appear to have been triggered thereby lessening the net profit available. 

printRunStats(portfName)
XLB XLE XLF XLI XLK XLP XLU XLV XLY
Num.Txns 477.00 341.00 445.00 435.00 423.00 433.00 489.00 425.00 433.00
Num.Trades 237.00 171.00 220.00 217.00 211.00 214.00 244.00 209.00 215.00
Net.Trading.PL 3290.50 104.80 1667.10 23713.60 23799.00 1335.00 455.30 17558.50 28766.30
Avg.Trade.PL 23.95 10.58 17.69 119.30 122.82 3.88 8.15 94.18 143.87
Med.Trade.PL 20.00 130.00 10.00 80.00 30.00 80.00 70.00 30.00 80.00
Largest.Winner 3755.00 5565.00 3205.00 8315.00 5055.00 3385.00 2475.00 6395.00 13215.00
Largest.Loser 3655.00 4085.00 2145.00 2970.00 4815.00 2675.00 2955.00 3615.00 4825.00
Gross.Profits 80508.00 105340.80 43354.40 103960.00 89316.50 57531.10 61237.50 97601.00 129070.00
Gross.Losses 74832.50 103531.00 39462.30 78071.40 63402.50 56701.10 59247.80 77917.50 98138.70
Std.Dev.Trade.PL 931.39 1635.24 569.63 1382.70 1121.75 778.05 707.61 1224.18 1695.56
Percent.Positive 52.32 46.20 50.91 45.16 51.66 43.46 44.26 48.80 46.51
Percent.Negative 47.68 53.80 49.09 54.84 48.34 56.54 55.74 51.20 53.49
Profit.Factor 1.08 1.02 1.10 1.33 1.41 1.01 1.03 1.25 1.32
Avg.Win.Trade 649.26 1333.43 387.09 1060.82 819.42 618.61 567.01 956.87 1290.70
Med.Win.Trade 385.00 1000.00 190.00 495.00 400.00 380.00 320.00 575.00 660.00
Avg.Losing.Trade 662.23 1125.34 365.39 656.06 621.59 468.60 435.65 728.20 853.38
Med.Losing.Trade 570.00 885.00 310.00 460.00 435.00 350.00 310.00 540.00 730.00
Avg.Daily.PL 17.25 0.53 4.11 81.45 110.90 8.06 3.65 88.70 116.07
Med.Daily.PL 15.00 160.00 0.00 85.00 25.00 75.00 75.00 10.00 85.00
Std.Dev.Daily.PL 929.13 1638.11 553.13 1297.42 1117.96 767.73 707.56 1215.37 1661.70
Ann.Sharpe 0.29 0.01 0.12 1.00 1.57 0.17 0.08 1.16 1.11
Max.Drawdown 9490.00 21630.00 7820.00 10550.00 12600.00 13752.10 8639.20 11760.00 15800.00
Profit.To.Max.Draw 0.35 0.00 0.21 2.25 1.89 0.10 0.05 1.49 1.82
Avg.WinLoss.Ratio 0.98 1.18 1.06 1.62 1.32 1.32 1.30 1.31 1.51
Med.WinLoss.Ratio 0.68 1.13 0.61 1.08 0.92 1.09 1.03 1.06 0.90
Max.Equity 9270.50 15644.80 6847.10 24763.60 28049.00 10167.10 7523.90 25478.50 29826.30
Min.Equity 4525.00 5985.20 3667.50 1488.50 255.00 3585.00 1902.10 3585.00 1345.00
End.Equity 3290.50 104.80 1667.10 23713.60 23799.00 1335.00 455.30 17558.50 28766.30
[1] ” Aggregate Profit Ratio is 1.179048 “
[1] ” Percentage Positive Trades is 47.697778 “
[1] ” Total Number trades is 1938 “
[1] ” Mean Win-Loss Ratio is 1.288889 “
 
### using the custom getEndEquity.. function gives this:
> getEndEquityFromBlotter()
                     End.Eq
1949-12-31 16:00:00 1000000
2010-12-30 16:00:00 1017286
2011-12-29 16:00:00  991349
2012-12-30 16:00:00  996388
2013-12-30 16:00:00 1049124
2014-12-30 16:00:00 1079138
2015-12-30 16:00:00 1035770
2016-12-29 16:00:00 1050601
2017-12-28 16:00:00 1095840
2018-12-30 16:00:00 1069110
2019-02-03 16:00:00 1097110

Now we will run the same strategy but this time using our previously selected dividend stocks.  We can see that for these stocks the strategy was definitely better than for the ETF case.  Reminder note – none of strategies on this site is being recommended – we are just providing ourselves the capacity to analyze these as any professional money manager or trader would do with equivalent tools and software.

Running the same strategy but with all our previously selected dividend stocks

 

Symbols = c(“MO” ,  “UVV”,  “MMP”,  “GTY”, “ORI”,  “CINF” ,“BWLA”, “VVC”, “ATO”,  “RPM”,  “IFF”,  “LMT”,

            “RTN”,  “NOC” , “ODC” , “CPB”,  “R”,    “MMP” , “GTY” , “THFF”, “ABT”, “MMM”,  “MXIM”, “ITW” , “MSFT” )

 

##########################################

####   Corresponding trade statistics for these 20+ symbols

##########################################

 

                          MO        UVV        MMP       GTY       ORI      CINF      BWLA

Num.Txns              376.00     452.00     429.00    435.00    404.00    461.00    359.00

Num.Trades            187.00     222.00     214.00    217.00    197.00    231.00    157.00

Net.Trading.PL      26830.00  30220.00   23156.20   1065.00   1900.00  13787.50 33920.60

Avg.Trade.PL          153.53    125.95     118.23     14.93     19.90     69.66   204.62

Med.Trade.PL           50.00    260.00    160.00    60.00    20.00    40.00   210.00

Largest.Winner       6505.00   17395.00    9155.00   4335.00   3345.00   7105.00   1045.00

Largest.Loser       5575.00  12105.00   5205.00  5675.00  1655.00  4655.00  1805.00

Gross.Profits       93565.00  128370.00  147560.00  55580.00  34810.00 102862.50  13373.40

Gross.Losses       64855.00 156330.00 122258.80 52340.00 30890.00 86770.00 45499.00

Std.Dev.Trade.PL     1296.32    2324.53    2001.65    826.19    538.95   1337.45    436.86

Percent.Positive       51.87      38.74      43.93     46.54     47.21     48.05     28.03

Percent.Negative       48.13      61.26      56.07     53.46     52.79     51.95     71.97

Profit.Factor           1.44       0.82       1.21      1.06      1.13      1.19      0.29

Avg.Win.Trade         964.59    1492.67    1569.79    550.30    374.30    926.69    303.94

Med.Win.Trade         570.00     935.00     785.00    340.00    220.00    430.00    207.90

Avg.Losing.Trade     720.61   1149.49   1018.82   451.21   297.02   723.08   402.65

Med.Losing.Trade     465.00    655.00    735.00   330.00   210.00   495.00   350.00

Avg.Daily.PL          147.71    128.72     108.09      4.22     14.41     56.05   191.88

Med.Daily.PL           40.00    255.00    165.00    65.00    15.00    55.00   155.00

Std.Dev.Daily.PL     1292.90    2303.84    2000.53    821.95    532.22   1333.93    398.98

Ann.Sharpe              1.81      0.89       0.86      0.08      0.43      0.67     7.63

Max.Drawdown       11180.00  36720.00  32110.00 10910.00  5590.00 23740.00 35970.20

Profit.To.Max.Draw      2.40      0.82       0.72      0.10      0.34      0.58     0.94

Avg.WinLoss.Ratio       1.34       1.30       1.54      1.22      1.26      1.28      0.75

Med.WinLoss.Ratio       1.23       1.43       1.07      1.03      1.05      0.87      0.59

Max.Equity          33705.00    2355.00   42926.20   7395.00   5915.00  35227.50      0.00

Min.Equity          1140.00  34365.00   6975.00  3515.00  1505.00  3877.50 35970.20

End.Equity          26830.00  30220.00   23156.20   1065.00   1900.00  13787.50 33920.60

[1] ” Aggregate Profit Ratio is 1.030733 “

[1] ” Percentage Positive Trades is 43.481429 “

[1] ” Total Number trades is 1425 “

[1] ” Mean Win-Loss Ratio is 1.241429 “

                         VVC       ATO       RPM        IFF        LMT        RTN        NOC

Num.Txns              457.00    451.00    385.00     433.00     441.00     453.00     419.00

Num.Trades            227.00    226.00    193.00     216.00     221.00     227.00     209.00

Net.Trading.PL      27425.00  10495.00  19935.00   21115.00   50735.00   46695.00   89655.00

Avg.Trade.PL          130.88     56.42    113.26     107.78     239.55     215.68     439.00

Med.Trade.PL          20.00     20.00   100.00     70.00     50.00    160.00     170.00

Largest.Winner       5375.00   7525.00  11735.00   13035.00   17605.00   11925.00   19785.00

Largest.Loser       3835.00  4975.00  4435.00   7255.00  15695.00   8795.00  17985.00

Gross.Profits       90760.00 107580.00  98420.00  201230.00  359950.00  236440.00  320390.00

Gross.Losses       61050.00 94830.00 76560.00 177950.00 307010.00 187480.00 228640.00

Std.Dev.Trade.PL     1103.32   1448.35   1638.31    2530.48    4858.68    2912.73    4438.65

Percent.Positive       48.02     50.44     43.01      48.15      48.87      44.93      54.07

Percent.Negative       51.98     49.56     56.99      51.85      51.13      55.07      45.93

Profit.Factor           1.49      1.13      1.29       1.13       1.17       1.26       1.40

Avg.Win.Trade         832.66    943.68   1185.78    1934.90    3332.87    2318.04    2835.31

Med.Win.Trade         440.00    525.00    550.00    1060.00    1625.00    1250.00    1170.00

Avg.Losing.Trade     517.37   846.70   696.00   1588.84   2716.90   1499.84   2381.67

Med.Losing.Trade     325.00   560.00   460.00   1175.00   1580.00   1070.00   1545.00

Avg.Daily.PL          125.26     49.58    108.59     103.98     149.45     165.62     323.90

Med.Daily.PL          25.00     15.00   110.00     65.00     55.00    180.00     155.00

Std.Dev.Daily.PL     1100.92   1451.32   1642.59    2530.37    4701.81    2838.78    4152.24

Ann.Sharpe              1.81      0.54      1.05       0.65       0.50       0.93       1.24

Max.Drawdown        7810.00 22620.00 13580.00  28920.00  82530.00  44510.00  87700.00

Profit.To.Max.Draw      3.51      0.46      1.47       0.73       0.61       1.05       1.02

Avg.WinLoss.Ratio       1.61      1.11      1.70       1.22       1.23       1.55       1.19

Med.WinLoss.Ratio       1.35      0.94      1.20       0.90       1.03       1.17       0.76

Max.Equity          29125.00  30235.00  23755.00   47835.00  113965.00   80805.00  146375.00

Min.Equity          4445.00  7875.00  6545.00   5365.00   3255.00  17145.00    525.00

End.Equity          27425.00  10495.00  19935.00   21115.00   50735.00   46695.00   89655.00

[1] ” Aggregate Profit Ratio is 1.248121 “

[1] ” Percentage Positive Trades is 48.212857 “

[1] ” Total Number trades is 1519 “

[1] ” Mean Win-Loss Ratio is 1.372857 “

                         ODC       CPB          R        MMP       GTY       THFF       ABT

Num.Txns              464.00    458.00     421.00     429.00    435.00     511.00    403.00

Num.Trades            226.00    228.00     209.00     214.00    217.00     255.00    199.00

Net.Trading.PL      12660.00  13290.00  14845.00   23156.20   1065.00  15895.00   4755.00

Avg.Trade.PL           66.28     68.33     60.96     118.23     14.93     52.31     34.02

Med.Trade.PL         140.00    70.00    180.00    160.00    60.00    180.00    60.00

Largest.Winner       6285.00  10845.00   12745.00    9155.00   4335.00    8295.00   5045.00

Largest.Loser       3515.00  4075.00   9565.00   5205.00  5675.00   2805.00  5495.00

Gross.Profits      102810.00  91620.00  195500.00  147560.00  55580.00   89320.00  95910.00

Gross.Losses       87830.00 76040.00 208240.00 122258.80 52340.00 102660.00 89140.00

Std.Dev.Trade.PL     1310.75   1336.75    2873.63    2001.65    826.19    1120.47   1350.01

Percent.Positive       41.15     45.18      46.41      43.93     46.54      39.22     47.24

Percent.Negative       58.85     54.82      53.59      56.07     53.46      60.78     52.76

Profit.Factor           1.17      1.20       0.94       1.21      1.06       0.87      1.08

Avg.Win.Trade        1105.48    889.51    2015.46    1569.79    550.30     893.20   1020.32

Med.Win.Trade         630.00    420.00    1000.00     785.00    340.00     495.00    685.00

Avg.Losing.Trade     660.38   608.32   1859.29   1018.82   451.21    662.32   848.95

Med.Losing.Trade     550.00   390.00   1440.00    735.00   330.00    540.00   620.00

Avg.Daily.PL           59.57     63.03     65.67     108.09      4.22     57.16     28.68

Med.Daily.PL         125.00    75.00    180.00    165.00    65.00    185.00    35.00

Std.Dev.Daily.PL     1293.65   1333.82    2866.76    2000.53    821.95    1120.47   1343.25

Ann.Sharpe              0.73      0.75      0.36       0.86      0.08      0.81      0.34

Max.Drawdown       17940.00 17250.00  56690.00  32110.00 10910.00  19790.00 16820.00

Profit.To.Max.Draw      0.71      0.77      0.26       0.72      0.10      0.80      0.28

Avg.WinLoss.Ratio       1.67      1.46       1.08       1.54      1.22       1.35      1.20

Med.WinLoss.Ratio       1.15      1.08       0.69       1.07      1.03       0.92      1.10

Max.Equity          18225.00  28105.00   37325.00   42926.20   7395.00    2985.00  18035.00

Min.Equity          7815.00  9605.00  19365.00   6975.00  3515.00  16805.00  8025.00

End.Equity          12660.00  13290.00  14845.00   23156.20   1065.00  15895.00   4755.00

[1] ” Aggregate Profit Ratio is 1.053880 “

[1] ” Percentage Positive Trades is 44.238571 “

[1] ” Total Number trades is 1548 “

[1] ” Mean Win-Loss Ratio is 1.360000 “




#######################

########   Ending Equity Yearly Progress

#######################

getEndEquityFromBlotter()

                       End.Eq

19491231 16:00:00 1000000.0

20101230 16:00:00 1034262.3

20111229 16:00:00  944420.0

20121230 16:00:00  992778.6

20131230 16:00:00 1196897.9

20141230 16:00:00 1300055.3

20151230 16:00:00 1267206.6

20161229 16:00:00 1404506.1

20171228 16:00:00 1572141.1

20181230 16:00:00 1366022.4

20190124 16:00:00 1431727.4           

 

 

We can also plot out rolling cumulative performance by symbols and compare it to a Buy & Hold.  Code and plots are shown below

#############
######## Plot the performance of the Simple Cross Startegy
#############
getPeriodReturnsFromBlotter <- function(envObj, period=“months” ) {
xret = lapply(envObj$symbols, function(x) {
val = x$posPL$Pos.Value
val = ifelse (val > 0, val, NA )
val = na.locf(val) # na.rm=TRUE is default
Return.calculate(val[endpoints(val, on=period)], method=“discrete”)
}
)
combRet = do.call(cbind, xret)
colnames(combRet) = Symbols
}
combRet = getPeriodReturnsFromBlotter(.blotter[[paste0(“portfolio.”,portfNameOpt)]])
colnames(combRet) = Symbols
charts.PerformanceSummary(combRet, main=” Triple Cross Strategy”)
#############
######## Lets compare it to a Buy & Hold Startegy
#############
## na.locf replace with most recent non-NA before it
prc = do.call( cbind, sapply(Symbols, function(x) list(Cl(get(x))) ))
head(prc)
mRet = Return.calculate(prc[endpoints(prc, on=“months”)], method=“discrete”)
charts.PerformanceSummary(mRet, main=“Buy & Hold Strategy”)

 

Thanx for reading

4 Comments on “Technical Trading Strategy Analysis & Backtesting – Intro to Quantstrat”

  1. [url=https://mosmedspravki.ru/spravka-kek/]Справка КЭК в Москве, получить заключение КЭК – Витбиомед+ [/url]
    Tegs: Справка Манту для ребёнка в Москве – Витбиомед+ https://mosmedspravki.ru/spravka-mantu-dlya-rebyonka/

    [u]Справка в свободной форме от врача медицинская – Витбиомед+ [/u]
    [i]Справка для ребенка в Москве | Витбиомед+ [/i]
    [b]Справку для учебы 086у купить медицинскую в Москве | Витбиомед+ [/b]

  2. 👉 $5,000 FREE EXCHANGE BONUSES BELOW 📈 👉 PlaseFuture FREE $3,000 BONUS + 0% Maker Fees 📈 + PROMOCODE FOR NEWS USERS OF THE EXCHANGE 👉 [M0345IHZFN] — 0.01 BTC 👉 site: https://buycrypto.in.net Our site is a secure platform that makes it easy to buy, sell, and store cryptocurrency like Bitcoin, Ethereum, and More. We are available in over 30 countries worldwide.

Comments are closed.