简体   繁体   English

从 R 转换为 quantstrat 设置以进行交易策略回测

[英]Convert from R to quantstrat setup for trading strategy backtesting

I am trying to backtest a trading strategy with "quantstrat" package.我正在尝试使用“quantstrat”包对交易策略进行回测。 My strategy is composed by 4 indicators, 3 different EMAs and 1 lagged EMA.我的策略由 4 个指标、3 个不同的 EMA 和 1 个滞后 EMA 组成。

I want to go long when: EMA1 > EMA2 & EMA1 > EMA3 & EMA1_lag < EMA1 I want to exit and go flat when: EMA1 < EMA3我想在以下时间做多:EMA1 > EMA2 & EMA1 > EMA3 & EMA1_lag < EMA1 我想在以下时间平仓:EMA1 < EMA3

It's pretty simple but I am not able to write it into quantstrat environment.这很简单,但我无法将其写入 quantstrat 环境。

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.我尝试用 quantstrat 复制上述策略(使用add.indicatoradd.signaladd.rule ),但结果肯定不同。 Here the second code with quantstrat:这是带有 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.我认为我的错误在add.indicatoradd.signaladd.rule设置中,但我无法准确地弄清楚。

The quanstrat-based code will not provide identical results for several reasons.由于多种原因,基于 quastrat 的代码不会提供相同的结果。 One is that your columns are not correct in your first 3 add.signal calls.一个是您的前 3 个add.signal调用中的列不正确。 All the columns need to have an "EMA."所有列都需要有一个"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.另一个差异来源是,您总是使用大约 100% 的可用股票来进行记账版的多头交易,但您总是在 quantstrat 版本中购买 900 股。 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).您可以在 quantstrat 中使用自定义定单调整函数执行类似操作(有关如何编写自定义定单调整函数的示例,请参阅osNoOposMaxPos )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM