Algotrading with R — Quantstrat
I wrote about this before thinking that I knew the basics of Quantstrat. That was not true. I was probably at “Mt.Stupid” on Dunning-Kruger curve when I posted it.
I will keep posting new things as I learn. Now I want to get to the basics of how to do a simple trading strategy using Quantsrtat. Whats quantstrat?
Description:
quantstrat provides a generic infrastructure to model and backtest signal-based quantitative strategies. It is a high-level abstraction layer (built on xts, FinancialInstrument, blotter, etc.) that allows you to build test strategies in very few lines of code.
Shout out to Peter Carl, Brian Peterson and Jeffrey Ryan for creating such an awesome library!
We need the following before we get started on backtesting our strategy:
- Data
- Instruments/Tools
- Strategy
Data
For this exercise we will be using data from Yahoo. However, if you have other dataset, feel free to upload to the workspace. Ensure that the data is xts format. Without data, we will not be able to do any work!
Instruments/Tools
You can install the library using install.packages("quantstrat")
, but I highly recommend downloading the raw materials from github for blotter and quanstrat, then compiling the package. This allows me to make minor modifications to the package or track the error message. Instruction can be found here.
Strategy
First we need to pick a strategy. For this exercise we will be using Simple Moving Average (SMA) strategy on SPY. Basically, we enter long when fast moving average is crossover slow moving average. Exit long when slow moving average crossover fast moving average. Trading systems by Emilio Tomasini and Urban Jaekle is a great resource to study this strategy.
Basic strategy backtesting workflow for quantstrat is provided below:
Initialization
The code is located in my Github repo. Lets go line by line to setup the strategy.
###Setup library
library("quantstrat")
blotter, PerformanceAnalytics, TTR, foreach, FinancialInstruments, xts, quantmod, Defaults and zoo will be automatically loaded with quantstrat.
quantstart automatically initiates .strategy
and .blotter
environments. We will talk about the importance in the upcoming blogs.
#set the currency and the stock we are interested
currency("USD")
stock("SPY",currency="USD",multiplier=1)
Sys.setenv(TZ="UTC") #setting up the timezone
Set parameters
#Set the parameters
initDate <- "2007-01-02" #Date of initiation
from <- "2007-01-03" #Start date of the data set
to <- "2018-03-18" #End date of the data set
initEq <- 1000 #Initial equity
Time to load the data. getSymbol()
by default imports data from Yahoo. We can change it if we want (FYI). If we dont add the from and to dates, it will import the data from 2007 to current date.
# read in data ---------------------------------------------------
getSymbols("SPY", from = from, to = to)
Now we initialize strategy, portfolio and account.
#-------------Initiate portfolio and account-----------
strategy.st <- "luxor" #Name the strategy
account.st <- "luxor"
portfolio.st <- "luxor"#Initiate portfolio
initPortf(qs.strategy, "SPY", initDate = initDate) #Initiate account
initAcct(qs.strategy, portfolios = qs.strategy, initDate = initDate, initEq = initEq) #Initiate account
initOrders(portfolio = qs.strategy, initDate = initDate) #Store all the events in the strategy
strategy(strategy.st, store = TRUE)
Define strategy
Indicators — Calculate fast and slow moving average for the closing price. Call it nFast and nSlow. The argument name
in the add.indicator
requires the name of the technical trading rule. More of this can be found in the package TTR. The arguments contains all the argument that needs to be supplied to the SMA function. We can use args(SMA)
to find this.
### indicatorsadd.indicator(strategy.st, name = "SMA",
arguments = list(
x = quote(Cl(mktdata)[,1]),
n = nfast
),
label="nFast"
)add.indicator(strategy.st, name="SMA",
arguments = list(
x = quote(Cl(mktdata)[,1]),
n = nslow
),
label="nSlow"
)
Signals — When nFast is greater than nSlow, long = 1. When nFast is lower than nSlow, short = 1. sigComparison, sigFormula, sigThreshold and sigPeak
are options available in add.signal. We can also create our own signals if we like (FYI).
add.signal(strategy.st, name='sigCrossover',
arguments = list(
columns=c("nFast","nSlow"),
relationship="gte"
),
label='long'
)add.signal(strategy.st, name='sigCrossover',
arguments = list(
columns=c("nFast","nSlow"),
relationship="lt"
),
label='short'
)
Rules — When long = 1, enter. When short = 1, exit. args(ruleSignal)
will reveal all the value we need to supply to the arguments. Important arguments:
- orderside — this defines whether we are trading on the short or long side.
- replace — only for exit we set this as TRUE. I still dont understand. My mentor tried to explain, but I still dont understand. So he told me to set this as TRUE only for exit.
### rulesadd.rule(strategy.st, name='ruleSignal',
arguments=list(sigcol='short', sigval=TRUE,
orderside='long' ,
ordertype='market',
orderqty='all',
replace=TRUE
),
type='exit',
label='Exit2SHORT'
)add.rule(strategy.st, name='ruleSignal',
arguments=list(sigcol='long' , sigval=TRUE,
orderside='long' ,
ordertype='market',
orderqty=orderqty,
replace=FALSE
),
type='enter',
label='EnterLONG'
)
Bar by Bar Processing
applyStrategy(strategy.st, portfolio.st)
Update
Update portfolio, account and equity all at once
#update
updatePortf(strategy.st)
updateAcct(strategy.st)
updateEndEq(strategy.st)
Reporting
This is a subject on its own. We will discuss this later. For now we will go with simple PL and Drawdown chart.
Thats all for now. I will go more in the upcoming blogs!