I get an error when running my quantmod code:
´Error in .xts(e, .index(e1), .indexCLASS = indexClass(e1), .indexFORMAT = indexFormat(e1), :
index length must match number of observations´
My code (partly from http://rbresearch.wordpress.com/2013/02/19/momentum-in-r-part-4-with-quantstrat/) is:
# qstratRank.R
qstratRank <- function(symbols, init.equity=100000, top.N=1,
max.size=1000, max.levels=1) {
# The qstratRank function uses the quantstrat framework to backtest a
# ranking or relative strength strategy
#
# args
# symbols : character vector of symbols
# init.equity : initial equity
# top.N : trade the top N ranked assets
# max.size : maximum position size
# max.levels : maximum levels to scale in a trade
# max.size and max.levels are passed to addPosLimit
#
# return value
# returns a list: end.eq, returns, book, stats
# remove variables
suppressWarnings(rm("order_book.Rank", pos=.strategy))
suppressWarnings(rm("account.Rank", "portfolio.Rank", pos=.blotter))
suppressWarnings(rm("account.st", "port.st", "stock.str", "stratRank",
"initDate", "initEq", 'start_t', 'end_t'))
# set initial variables
initDate <- "1900-01-01"
initEq <- init.equity
port.st <- "Rank"
account.st <- "Rank"
# trade the top "N" ranked symbols
N <- top.N
# initialize quantstrat objects
initPortf(port.st, symbols=symbols, initDate=initDate)
initAcct(account.st, portfolios=port.st, initDate=initDate,initEq=initEq)
initOrders(portfolio=port.st, initDate=initDate)
# initialize a strategy object
stratRank <- strategy("Rank")
# there are two signals
# the first signal is when Rank is less than or equal to N
# (i.e. trades the #1 ranked symbol if N=1)
stratRank <- add.indicator(strategy=stratRank, name="SMA",
arguments=list(x = quote(Cl(mktdata)), n=50), label="SMA50")
stratRank <- add.signal(stratRank, name="sigComparison",
arguments=list(columns=c("Close", "SMA50"), relationship="gt"), label="Cl.gt.SMA50")
# the second signal is when Rank is greter than or equal to N
# (i.e. trades the #1 ranked symbol if N=1)
stratRank <- add.signal(strategy=stratRank, name="sigThreshold",
arguments=list(threshold=N, column="Rank",
relationship="gt", cross=FALSE),
label="Rank.gt.N")
# add buy rule
stratRank <- add.rule(strategy=stratRank, name='ruleSignal',
arguments = list(sigcol="Cl.gt.SMA50", sigval=TRUE,
orderqty=max.size, ordertype='market',
orderside='long', pricemethod='market',
replace=FALSE, osFUN=osMaxPos),
type='enter', path.dep=TRUE)
stratRank <- add.rule(strategy=stratRank, name='ruleSignal',
arguments = list(sigcol="Rank.lte.N", sigval=TRUE,
orderqty=max.size, ordertype='market',
orderside='long', pricemethod='market',
replace=FALSE, osFUN=osMaxPos),
type='enter', path.dep=TRUE)
# add exit rule
stratRank <- add.rule(strategy = stratRank, name='ruleSignal',
arguments = list(sigcol="Rank.gt.N", sigval=TRUE,
orderqty='all', ordertype='market',
orderside='long', pricemethod='market',
replace=FALSE),
type='exit', path.dep=TRUE)
#set max position size and levels
for(symbol in symbols){ addPosLimit(port.st, symbol, initDate, max.size, max.levels) }
print("setup completed")
# apply the strategy to the portfolio
start_t <- Sys.time()
out <- try(applyStrategy(strategy=stratRank, portfolios=port.st))
end_t <- Sys.time()
print(end_t-start_t)
# update Portfolio
start_t <- Sys.time()
updatePortf(Portfolio=port.st, Dates=paste('::', as.Date(Sys.time()), sep=''))
end_t <- Sys.time()
print("trade blotter portfolio update:")
print(end_t - start_t)
# update account
updateAcct(account.st)
# update ending equity
updateEndEq(account.st)
# get ending equity
eq <- getEndEq(account.st, Sys.Date()) + initEq
# view order book to confirm trades
order.book <- getOrderBook(port.st)
# get trade statistics
stats <- tradeStats(port.st)
# portfolio returns
ret1 <- PortfReturns(port.st)
ret1$total <- rowSums(ret1, na.rm=TRUE)
return(list(end.eq=eq, returns=ret1, book=order.book, stats=stats))
}
I then run:
bt <- qstratRank(symbols=symbols, init.equity=100000, top.N=2,
max.size=1000, max.levels=1)
The issue might be with my ´SMA50´ indicator, and signal and rule.
Any ideas?
Best Regards
Related
I can't figure out how I can backtest a strategy trading ticker X and ticker Y based on a signal from a synthetic asset created from a combination of ticker X and Y.
The data background is a list of XTS series of tickers. Right now I am solving it by trading the synthetic asset, not the individual assets:
initDate = "1990-01-01"
from = "2010-07-22"
to = "2016-12-31"
initeq = 1000000
NBDG <- lXTS[[1]]
UKPSPIR <- lXTS[[2]]
CoIntV <- list(1, -9.90)
Diff <- NBDG - as.numeric(CoIntV[2])*UKPSPIR
colnames(Diff) <- "Close"
strategy.st <- portfolio.st <- account.st <- "test"
rm.strat(strategy.st)
initPortf(portfolio.st, symbols = list("Diff"), initDate=initDate)
initAcct(account.st, portfolios=portfolio.st)
initOrders(portfolio.st)
strategy(strategy.st, store = TRUE)
Diff <- cbind(Diff, BBands(Diff, maType="SMA", n=12, sd=2))
add.signal(strategy=strategy.st, name="sigCrossover",
arguments = list(columns=c("Close", "up"),
relationship="gt"),
label="cl.gt.up")
add.signal(strategy=strategy.st, name="sigCrossover",
arguments = list(columns=c("Close", "dn"),
relationship="lt"),
label="cl.lt.dn")
add.signal(strategy=strategy.st, name="sigCrossover",
arguments = list(columns=c("Close", "mavg"),
relationship="gte"),
label="mid.cross.frombelow")
add.signal(strategy=strategy.st, name="sigCrossover",
arguments = list(columns=c("Close", "mavg"),
relationship="lte"),
label="mid.cross.fromabove")
tmp <- applySignals(strategy = strategy.st, mktdata=Diff)
add.rule(stratBBands,name='ruleSignal',
arguments = list(sigcol="cl.gt.up",
sigval=TRUE,
orderqty=-1,
ordertype='market',
orderside=NULL,
threshold=NULL),
type='enter')
add.rule(stratBBands,name='ruleSignal',
arguments = list(sigcol="cl.lt.dn",
sigval=TRUE,
orderqty=1,
ordertype='market',
orderside=NULL,
threshold=NULL),
type='enter')
add.rule(stratBBands,name='ruleSignal',
arguments = list(sigcol="mid.cross.frombelow",
sigval=TRUE,
orderqty='all',
ordertype='market',
orderside=NULL,
threshold=NULL),
type='exit')
add.rule(stratBBands,name='ruleSignal',
arguments = list(sigcol="mid.cross.fromabove",
sigval=TRUE,
orderqty='all',
ordertype='market',
orderside=NULL,
threshold=NULL),
type='exit')
out <- applyStrategy(strategy=strategy.st,portfolios=portfolio.st)
updatePortf(portfolio.st)
dateRange <- time(getPortfolio(portfolio.st)$summary)[-1]
updateAcct(portfolio.st,dateRange)
updateEndEq(account.st)
I get the following warnings when doing so:
1: In getInstrument(symbol) :
instrument Diff not found, please create it first.
2: In getInstrument(Symbol) :
instrument Diff not found, please create it first.
3: In .updatePosPL(Portfolio = pname, Symbol = as.character(symbol), :
Instrument Diff not found, things may break
but I am getting results out.
Does anyone know anything?
You have not defined your instruments, which quantstrat expects (although your simulation probably still runs OK). Just as the warning says to you.... Define your synthetic instrument before you run the strategy (before rm.strat in your code above, say).
You also should define your currency (not sure if it is GBP, but by default it is USD, which I assume here):
currency(c("USD"))
spread(primary_id = 'Diff', currency = "USD", members = c('NBDG','UKPSPIR'), memberratio = c(1, -9.90))
Run your code with these changes and the warnings will disappear.
(Also, in your code you post, you have arbitrarily changed from strategy.st to stratBBands at add.rules.)
I am new to R and am trying to create a very simple trading strategy (Buy the VIX when it's up by 2%, sell it when it's down by 2%). I am getting an error with the add.signal command: "Error in match.names(column, colnames(data)) :
argument "column" is missing, with no default."
I've searched everywhere and I can't seem to find the fix or understand what R is trying to tell me. Any input would be appreciated.
require(quantstrat)
options("getSymbols.warning4.0"=FALSE) #suppress warnings
rm(list=ls(.blotter), envir=.blotter) #house cleaning, clears blotter
rm(list=ls(.strategy), envir=.strategy)#clears strategy
rm.strat(qs.portfolio)#clear the portfolio environment
rm.strat(qs.strategy)#clear the strategy environment
rm.strat(qs.account)#clear the account environment
currency("USD")
stock("VIX",currency="USD",multiplier=1)
# system settings
initDate <- '2013-12-31'
startDate <- '2014-01-01'
endDate <- '2016-04-07'
initEq <- 100000
Sys.setenv(TZ="UTC") #Timezone
getSymbols('^VIX', from=startDate, to=endDate, index.class="POSIXct", adjust=TRUE)
VIX$lagROC <- lag(round(ROC(Cl(VIX)), 4), n=1, type="discrete")
VIX$lagROC[is.na(VIX$lagROC)] <- 0
# initialize portfolio, account, orders
qs.strategy <- "qsJones"
initPortf(qs.strategy,'VIX')
initAcct(qs.strategy,portfolios=qs.strategy, initEq=initEq)
initOrders(portfolio=qs.strategy)
#create a new strategy object
strategy(qs.strategy,store=TRUE)
strat <- getStrategy(qs.strategy)
thresh1 <- (.02*-1)
thresh2 <- .02
add.indicator(strategy = qs.strategy, name = "ROC",
arguments = list(x = quote(Cl(mktdata)), n=1), label="lagROC")
test <- applyIndicators(qs.strategy, VIX)
add.signal(qs.strategy, name="sigThreshold",
arguments=list(column="lagROC", threshold=thresh1, relationship="lte", cross=FALSE),
label="lt.ROCThresh1")
add.signal(qs.strategy, name="sigThreshold",
arguments=list(column="lagROC", threshold=thresh2, relationship="gte", cross=FALSE),
label="gt.ROCThresh2")
test <- applySignals(qs.strategy, test)
# exit when lagROC < .02
add.rule(qs.strategy, name='ruleSignal',
arguments=list(sigcol="Cl.lt.lagROC", sigval=TRUE, replace=FALSE, orderqty='all',
ordertype='market', orderside='long'),
type='exit', path.dep=TRUE)
# go long when lagROC > .02
add.rule(qs.strategy, name='ruleSignal',
arguments=list(sigcol="Cl.gt.lagROC", sigval=TRUE, replace=FALSE, orderqty=1500,
ordertype='market', orderside='long'),
type='enter', path.dep=TRUE)
applyStrategy(strategy=qs.strategy , portfolios=qs.strategy)
tail(mktdata)
mktdata["2014"]
getTxns(Portfolio=qs.strategy, Symbol="VIX")
Your add.signal calls create two columns: "lt.ROCThresh1" and "gt.ROCThresh2", while your add.rule calls reference columns "Cl.lt.lagROC" and "Cl.gt.lagROC" (which don't exist).
You need to either:
Change the label= argument in your add.signal calls from "lt.ROCThresh1" and "gt.ROCThresh2" to "Cl.lt.lagROC" and "Cl.gt.lagROC", respectively, or
Change the sigcol= argument in your add.rule calls from "Cl.lt.lagROC" and "Cl.gt.lagROC" to "lt.ROCThresh1" and "gt.ROCThresh2", respectively.
Goal: produce a stop limit using time stamp (aka not a trailing stop). Most of the coding is stolen from Ilya Kipnis' design for calculating position size based on ATR. The link to the code is in comments, below, so it could be reproduced.
I'm fairly certain that the function produces the desired effect, but I don't know what type of information the order.price requires. It seems I need to indicate that this is a function, not a number.
I'm running the following function in a chain order, to calculate order.price.
stopATR <- function(atrMod="") {
atrString <- paste0("atr",atrMod)
atrCol <- grep(atrString, colnames(mktdata))
atrTimeStamp <- mktdata[timestamp, atrCol]
atrStop <- atrTimeStamp * pctATR*100
atrString <- paste0("EMA.currentPrice")
priceCol <- grep(atrString, colnames(mktdata))
currentPrice <- mktdata[timestamp, priceCol]
out <- currentPrice-atrStop
colnames(out) <- "atrStopLoss"
return(out)
}
#rules
add.rule(strategy.st, name="ruleSignal",
arguments=list(sigcol="buyTrigger", sigval=TRUE, ordertype="market",
orderside="long", replace=FALSE, prefer="Open",
osFUN=osDollarATR, tradeSize=tradeSize,
pctATR=pctATR, atrMod="X"),
type="enter", path.dep=TRUE,
label="newEntry")
add.rule(strategy.st, name="ruleSignal",
arguments=list(sigcol="buyTrigger", sigval=TRUE, ordertype="stoplimit",
orderside="long", replace=FALSE,
orderqty='all',
order.price=stopATR,
orderset="orders"),
type="chain",
parent="newEntry",
label="stopLossLong",
path.dep=TRUE)
The error is:
Error in as.numeric(orderprice) :
cannot coerce type 'closure' to vector of type 'double'
I wrote the following function and run it through add.indicator. Then I just refer to the column in mktdata to use it as a stoplimit.
stopOut <- function (x, n, maType, HLC, pctATR) {
EMA <- EMA(x, n, maType=maType)
ATR <- ATR(HLC, n=n, maType=maType)
ATR <- ATR$atr
atrStop <- EMA - (ATR*100*pctATR)
return(atrStop)
}
add.indicator(strategy.st, name="stopOut",
arguments=list(x=quote(HLC(mktdata)), n=period, wilder=TRUE, HLC=quote(HLC(mktdata)), pctATR=pctATR),
label="stopLimit")
add.rule(strategy.st, name="ruleSignal",
arguments=list(sigcol="buyTrigger",
sigval=TRUE,
ordertype="stoplimit",
orderside="long",
replace=FALSE,
orderqty='all',
order.price=quote(mktdata$EMA.stopLimit[timestamp]),
orderset="orders"),
type="chain",
parent="newEntry",
label="takeProfitLong",
path.dep=TRUE)
I am trying to backtest a trading strategy with "quantstrat" package.
My strategy is composed by 4 indicators, 3 different EMAs and 1 lagged EMA.
I want to go long when: EMA1 > EMA2 & EMA1 > EMA3 & EMA1_lag < EMA1
I want to exit and go flat when: EMA1 < EMA3
It's pretty simple but I am not able to write it into quantstrat environment.
Here's a data integrity check function used in both examples:
# Data integrity check
checkBlotterUpdate <- function(port.st,account.st,verbose=TRUE)
{
ok <- TRUE
p <- getPortfolio(port.st)
a <- getAccount(account.st)
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,endEq-initEq)) ) {
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)
}
Here is the blotter code that does what I want:
# Working Strategy
# it works well only with one portfolio
library(quantstrat)
suppressWarnings({
try(rm(list=ls(FinancialInstrument:::.instrument),
pos=FinancialInstrument:::.instrument), silent=TRUE)
try(rm(list=c("account.bGiulio","portfolio.bGiulio","order_book"),
pos=.blotter), silent=TRUE)
try(rm(list=c("b.strategy","myTheme","SPY",".getSymbols")), silent=TRUE)
})
#### all currency instruments must be defined
#### before instruments of other types can be defined
# Initialize a currency and a stock instrument
currency("USD")
stock("SPY",currency="USD",multiplier=1)
#Fetch historic data
# system settings
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2014-06-30'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, index.class="POSIXct", adjust=T)
# convert data to weekly
SPY=to.weekly(SPY, indexAt='endof', drop.time=FALSE)
SPY$EMA_1<-EMA(na.locf(Cl(SPY)),10) # 10 o 3
SPY$EMA_2<-EMA(na.locf(Cl(SPY)),25) # 50 o 10
SPY$EMA_3<-EMA(na.locf(Cl(SPY)),30) # 200 o 50
SPY$EMA_1_lag<-lag(EMA(na.locf(Cl(SPY)),10),1) # 200 o 50
# inizialization on both
b.strategy <- "bGiulio"
initPortf(b.strategy, 'SPY', initDate=initDate)
initAcct(b.strategy, portfolios=b.strategy, initDate=initDate, initEq=initEq)
initOrders(portfolio=b.strategy,initDate=initDate)
# trading algo
for( i in 1:nrow(SPY) )
{
# update values for this date
CurrentDate <- time(SPY)[i]
equity = getEndEq(b.strategy, CurrentDate)
ClosePrice <- as.numeric(Cl(SPY[i,]))
Posn <- getPosQty(b.strategy, Symbol='SPY', Date=CurrentDate)
UnitSize = as.numeric(trunc(equity/ClosePrice))
EMA1 <- as.numeric(SPY[i,'EMA_1'])
EMA2 <- as.numeric(SPY[i,'EMA_2'])
EMA3 <- as.numeric(SPY[i,'EMA_3'])
EMA1_lag <- as.numeric(SPY[i,'EMA_1_lag'])
# change market position if necessary
if( !is.na(EMA1) & # if the moving average has begun
!is.na(EMA2) & # if the moving average has begun
!is.na(EMA3) &
!is.na(EMA1_lag) ) # if the moving average has begun
{
if( Posn == 0 ) { # No position, test to go Long
if( EMA1 > EMA2 & EMA1 > EMA3 & EMA1_lag<EMA1) {
# enter long position
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = UnitSize , TxnFees=0)
}
} else { # Have a position, so check exit
if( EMA1 < EMA3) {
# exit position
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = -Posn , TxnFees=0)
} else {
if( i==nrow(SPY) ) # exit on last day
addTxn(b.strategy, Symbol='SPY', TxnDate=CurrentDate,
TxnPrice=ClosePrice, TxnQty = -Posn , TxnFees=0)
}
}
}
updatePortf(b.strategy,Dates=CurrentDate)
updateAcct(b.strategy,Dates=CurrentDate)
updateEndEq(b.strategy,CurrentDate)
} # End dates loop
# transactions
#getTxns(Portfolio=b.strategy, Symbol="SPY")
checkBlotterUpdate(b.strategy,b.strategy)
## [1] TRUE
tstats <- t(tradeStats(b.strategy))
perTradeStats(b.strategy)
library(lattice)
a <- getAccount(b.strategy)
xyplot(a$summary,type="h",col=4)
equity <- a$summary$End.Eq
plot(equity,main="Giulio Strategy Equity Curve")
ret <- Return.calculate(equity,method="log")
charts.PerformanceSummary(ret, colorset = bluefocus,
main="Giulio Strategy Performance")
I tried to replicate the above strategy with quantstrat (using add.indicator, add.signal, add.rule), but the results are definitely different. Here the second code with quantstrat:
# Here the code that does not work
library(quantstrat)
#Initialize a currency and a stock instrument
currency("USD")
stock("SPY",currency="USD",multiplier=1)
# system settings
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2014-06-30'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, index.class="POSIXct", adjust=T)
SPY <- to.weekly(SPY, indexAt='endof', drop.time=FALSE)
SPY$EMA1<-EMA(na.locf(Cl(SPY)),10) # 10 o 3
SPY$EMA2<-EMA(na.locf(Cl(SPY)),25) # 50 o 10
SPY$EMA3<-EMA(na.locf(Cl(SPY)),30) # 200 o 50
SPY$EMA1_lag<-lag(EMA(na.locf(Cl(SPY)),10)) # 200 o 50
# initialize portfolio/account
qs.strategy <- "qsGiulio"
rm.strat(qs.strategy) # remove strategy etc. if this is a re-run
initPortf(qs.strategy,'SPY', initDate=initDate)
initAcct(qs.strategy,portfolios=qs.strategy, initDate=initDate, initEq=initEq)
# initialize orders container
initOrders(portfolio=qs.strategy,initDate=initDate)
# instantiate a new strategy object
strategy(qs.strategy,store=TRUE)
strat <-getStrategy(qs.strategy)
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=10), label="EMA1")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=25), label="EMA2")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(na.locf(Cl(mktdata))), n=30), label="EMA3")
add.indicator(strategy = qs.strategy, name = "EMA",
arguments = list(x = quote(lag(na.locf(Cl(mktdata)))), n=10), label="EMA1_lag")
# entry signals
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA2"),relationship="gt"),
label="EMA1.gt.EMA2")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA3"),relationship="gt"),
label="EMA1.gt.EMA3")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA1_lag"),relationship="gt"),
label="EMA1.gt.EMA1_lag")
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag"),
label="longEntry")
# exit signals
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA1","EMA3"),relationship="lt"),
label="EMA1.lt.EMA3")
# RULES
# go long when 3 condition
add.rule(qs.strategy, name='ruleSignal',
arguments =
list(sigcol="longEntry", sigval=TRUE, orderqty=900,
ordertype='market', orderside='long'),
type='enter')
# exit when 1 condition
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="EMA1.lt.EMA3", sigval=TRUE, orderqty='all',
ordertype='market', orderside='long'),
type='exit')
applyStrategy(strategy=qs.strategy , portfolios=qs.strategy)
# transactions
#getTxns(Portfolio=qs.strategy, Symbol="SPY")
checkBlotterUpdate(b.strategy,b.strategy)
## [1] TRUE
# update portfolio/account
updatePortf(qs.strategy)
updateAcct(qs.strategy)
updateEndEq(qs.strategy)
tstats <- t(tradeStats(qs.strategy))
perTradeStats(qs.strategy)
library(lattice)
a <- getAccount(qs.strategy)
xyplot(a$summary,type="h",col=4)
equity <- a$summary$End.Eq
plot(equity,main="Giulio Strategy Equity Curve")
ret <- Return.calculate(equity,method="log")
charts.PerformanceSummary(ret, colorset = bluefocus,
main="Giulio Strategy Performance")
Could anyone help me to understand why the second code doesn't give identical results? I think my mistakes are within the add.indicator, add.signal, add.rule setup, but I am not able to figure out it precisely.
The quanstrat-based code will not provide identical results for several reasons. One is that your columns are not correct in your first 3 add.signal calls. All the columns need to have an "EMA." prefix:
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA2"),relationship="gt"),
label="EMA1.gt.EMA2")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA3"),relationship="gt"),
label="EMA1.gt.EMA3")
add.signal(qs.strategy,name="sigComparison",
arguments = list(columns=c("EMA.EMA1","EMA.EMA1_lag"),relationship="gt"),
label="EMA1.gt.EMA1_lag")
Another issue, and likely the biggest cause of differences, is the next signal:
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag"),
label="longEntry")
That creates a signal for every observation where the formula is true, not just the observations where the formula crosses from false to true. You only want the observations where the formula crosses, so you should use:
add.signal(qs.strategy, name = "sigFormula",
arguments = list(formula="EMA1.gt.EMA2 & EMA1.gt.EMA3 & EMA1.gt.EMA1_lag",
cross = TRUE),
label="longEntry")
Another source of differences is that you always use ~100% of your available equity for your opening long transaction in the blotter version, but you always buy 900 shares in the quantstrat version. You can do something similar in quantstrat by using a custom order sizing function (see osNoOp and osMaxPos for examples of how to write a custom order sizing function).
I'm still playing around with Guy Yollins quantstrat example. In this example he buys 1000 shares of the SPY when it crosses its 10 day MA. Since we define an initial equity, is it possible to always buy for the whole portfolio amount and not just 900 shares? 'all' didn't work for the enter, just the exit..
if (!exists('.blotter')) .blotter <- new.env()
if (!exists('.strategy')) .strategy <- new.env()
if (!exists('.instrument')) .instrument <- new.env()
currency("USD")
stock("SPY",currency="USD",multiplier=1)
ls(envir=FinancialInstrument:::.instrument)
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2013-07-31'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, adjust=T)
SPY=to.monthly(SPY, indexAt='endof')
SPY$SMA10m <- SMA(Cl(SPY), 10)
# inz portfolio, account
qs.strategy <- "qsFaber"
rm.strat(qs.strategy) # remove strategy etc. if this is a re-run
initPortf(qs.strategy,'SPY', initDate=initDate)
initAcct(qs.strategy,portfolios=qs.strategy, initDate=initDate, initEq=initEq)
initOrders(portfolio=qs.strategy,initDate=initDate)
# instantiate a new strategy object
strategy(qs.strategy,store=TRUE)
add.indicator(strategy = qs.strategy, name = "SMA",
arguments = list(x = quote(Cl(mktdata)), n=10), label="SMA10")
add.signal(qs.strategy,name="sigCrossover",
arguments = list(columns=c("Close","SMA10"),relationship="gt"),
label="Cl.gt.SMA")
add.signal(qs.strategy,name="sigCrossover",
arguments = list(columns=c("Close","SMA10"),relationship="lt"),
label="Cl.lt.SMA")
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="Cl.gt.SMA", sigval=TRUE, orderqty=900,
ordertype='market', orderside='long', pricemethod='market'),
type='enter', path.dep=TRUE)
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="Cl.lt.SMA", sigval=TRUE, orderqty='all',
ordertype='market', orderside='long', pricemethod='market'),
type='exit', path.dep=TRUE)
out <- applyStrategy(strategy=qs.strategy , portfolios=qs.strategy)
updatePortf(qs.strategy)
updateAcct(qs.strategy)
updateEndEq(qs.strategy)
myTheme<-chart_theme()
myTheme$col$dn.col<-'lightblue'
myTheme$col$dn.border <- 'lightgray'
myTheme$col$up.border <- 'lightgray'
# plot performance
chart.Posn(qs.strategy, Symbol = 'SPY', Dates = '1998::',theme=myTheme)
plot(add_SMA(n=10,col=4, on=1, lwd=2))
You cannot use orderqty="all" on entries because "all" refers to the current position size (i.e., when you want to exit the entire position).
It's possible to purchase an amount equal to the total available portfolio equity, but you have to define a custom order sizing function. And that function would necessarily have to mark the book (using updatePortf) in order to determine the amount of available equity.
Here is a toy example that achieves what you want.
You need to introduce an order sizing function.
Take a look at the arguments to the function ruleSignal (e.g. formals(ruleSignal) and ?ruleSignal).
You'll see there is an argument osFUN, which is where you can write your custom function that will determine how you order size.
You modify the appropriate parameters in add.rule to introduce ordersizing (on entry trades).
osFUN_all_eq <- function (data, timestamp, orderqty, ordertype, orderside, equity, portfolio, symbol, ruletype, ..., initEq) {
datePos <- format(timestamp,"%Y-%m-%d")
updatePortf(Portfolio = portfolio, Symbol = symbol, Dates = paste0(start(data), "/", datePos))
trading_pl <- sum(.getPortfolio(portfolio)$summary$Net.Trading.PL)
# The total equity in the strategy for this symbol (and this symbol only in isolation always, as this is how quantstrat by default works with applyStrategy)
equity <- initEq + trading_pl
ClosePrice <- getPrice(data, prefer = "Close")[datePos]
UnitSize <- as.numeric(trunc(equity / ClosePrice))
UnitSize <- osMaxPos(data, timestamp, UnitSize, ordertype, orderside, portfolio, symbol, ruletype, digits=0)
UnitSize
}
library(quantstrat)
currency("USD")
stock("SPY",currency="USD",multiplier=1)
initDate <- '1997-12-31'
startDate <- '1998-01-01'
endDate <- '2013-07-31'
initEq <- 1e6
Sys.setenv(TZ="UTC")
getSymbols('SPY', from=startDate, to=endDate, adjust=T)
SPY=to.monthly(SPY, indexAt='endof')
SPY$SMA10m <- SMA(Cl(SPY), 10)
qs.strategy <- "qsFaber"
rm.strat(qs.strategy) # remove strategy etc. if this is a re-run
initPortf(qs.strategy,'SPY', initDate=initDate)
initAcct(qs.strategy,portfolios=qs.strategy, initDate=initDate, initEq=initEq)
initOrders(portfolio=qs.strategy,initDate=initDate)
# instantiate a new strategy object
strategy(qs.strategy,store=TRUE)
# Specify the max quantity you could hold in the SPY instrument. Here we simply assume 1e5 units. You could reduce this number to limit the exposure
max_qty_traded <- 1e5
addPosLimit(qs.strategy, "SPY", timestamp = startDate, maxpos = max_qty_traded)
add.indicator(strategy = qs.strategy, name = "SMA",
arguments = list(x = quote(Cl(mktdata)), n=10), label="SMA10")
add.signal(qs.strategy,name="sigCrossover",
arguments = list(columns=c("Close","SMA10"),relationship="gt"),
label="Cl.gt.SMA")
add.signal(qs.strategy,name="sigCrossover",
arguments = list(columns=c("Close","SMA10"),relationship="lt"),
label="Cl.lt.SMA")
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="Cl.gt.SMA",
sigval=TRUE,
orderqty = 1, # the acutal orderqty size becomes redundant when supplying a function to the argument `osFUN`
osFUN = osFUN_all_eq,
ordertype='market', orderside='long', pricemethod='market'),
type='enter', path.dep=TRUE)
add.rule(qs.strategy, name='ruleSignal',
arguments = list(sigcol="Cl.lt.SMA",
sigval=TRUE,
orderqty='all', # flatten all open long positions
ordertype='market',
orderside='long',
pricemethod='market'),
type='exit',
path.dep=TRUE)
# supply initEq parameter and its value, which pass through to `osFUN`
out <- applyStrategy(strategy=qs.strategy , portfolios=qs.strategy, initEq=initEq)
updatePortf(qs.strategy)
updateAcct(qs.strategy)
updateEndEq(qs.strategy)
myTheme<-chart_theme()
myTheme$col$dn.col<-'lightblue'
myTheme$col$dn.border <- 'lightgray'
myTheme$col$up.border <- 'lightgray'
# plot performance
chart.Posn(qs.strategy, Symbol = 'SPY', Dates = '1998::',theme=myTheme)
plot(add_SMA(n=10,col=4, on=1, lwd=2))
There are a few things you should consider in an order sizing function:
1) If you already have a position open, do you want to permit "stacking"/pyramidying of positions on a particular side? For example, if you want to just have one position on, which you don't contribute further to, you could include getPosQty(qs.strategy, "SPY", timestamp) in your osFUN and return 0 if the current position held is not 0.
2) Do you want a max trade size? This can be handled using addPosLimit() as was done in this example above.
I know this question was posted long ago, but check out package "IKTrading," and the order sizing function "osMaxDollar." Here's a blog post on the topic; when you use this order sizing function you can set the dollar value of each trade and the total position.
https://quantstrattrader.wordpress.com/2014/08/29/comparing-atr-order-sizing-to-max-dollar-order-sizing/