Convert from R to quantstrat setup for trading strategy backtesting

丶灬走出姿态 提交于 2019-12-30 05:34:05

问题


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.


回答1:


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).



来源:https://stackoverflow.com/questions/32169176/convert-from-r-to-quantstrat-setup-for-trading-strategy-backtesting

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!