quantstrat:尽管信号值似乎有效,但策略不采取立场?

Posted

技术标签:

【中文标题】quantstrat:尽管信号值似乎有效,但策略不采取立场?【英文标题】:quantstrat: Strategy not taking positions despite signal values appearing to be valid? 【发布时间】:2017-04-14 06:40:36 【问题描述】:

我提出了一个新问题,因为 original post 已经太长了,而我得出的解决方案几乎否定了该帖子。

以下是我找到的解决方法,用于添加与您的数据不同的周期性指标。该解决方案基于this post on the R-SIG-Finance mailing list by Brian Peterson。

代码有两个主要问题。

1) 尽管生成了信号,但该策略不持有任何头寸(运行该策略后,mktdata 中存在相应的列)

2) 奇怪的是,如果indMerge() 没有将 SMA 列显式重命名为"SPY.SMA" 并使用add.signal(columns = c("SPY.Close", SPY.SMA")),则会引发以下错误(也就是说我无法在add.signal 中传递columns = c("Close", "SMA")

Warning message:
In match.names(columns, colnames(data)) :
  all columns not located in Close SMA for SPY.Open SPY.High SPY.Low SPY.Close 
SPY.Volume SPY.Adjusted SMA Cl.gt.SMA

由于我发现解决此错误的唯一方法是下面的代码,因此下面的解决方案在具有多个符号的投资组合中实际上是无用的。无论如何,这是代码:

require(quantstrat)
require(quantmod)
require(FinancialInstrument)

symbols = "SPY"

initDate="2000-01-01"
from="2003-01-01"
to="2016-12-31"
options(width=70)

options("getSymbols.warning4.0"=FALSE)

#set account currency and system timezone
currency('USD')
stock("SPY",currency="USD",multiplier=1)
Sys.setenv(TZ="UTC")

#trade sizing and initial equity settings
tradeSize <- 1e6
initEq <- tradeSize*length(symbols)

#Brians code 
#>> http://r.789695.n4.nabble.com/R-Quantstrat-package-question-td3772989.html
indMerge <- function(x, period, k, SMAlength, maType)
  mktdata <- getSymbols(x, auto.assign = FALSE)

  xW = to.period(mktdata, period = period, k = k, indexAt = "startof")
  smaW = wSMA = SMA(Cl(xW), n = SMAlength, maType = maType)

  x <- cbind(mktdata, smaW[paste(first(index(mktdata)) ,
                                 last(index(mktdata)) , sep='/')])
  colnames(x)[ncol(x)] = paste("SPY", ".", "SMA", sep = "")
  x <- na.locf(x)
  x
 

#get the data
getSymbols(symbols, from=from, to=to, src="yahoo", adjust=TRUE)

#apply the weekly SMA to the data without the use of add.indicator
SPY = indMerge(x = symbols, period = "weeks", k = 1, SMAlength = 14, maType = "SMA")

#set up the portfolio, account and strategy
strategy.st <- portfolio.st <- account.st <- "mtf.strat"
rm.strat(strategy.st)

initPortf(portfolio.st, symbols=symbols, initDate=initDate, currency='USD')
initAcct(account.st, portfolios=portfolio.st, initDate=initDate, 
              currency='USD',initEq=initEq)
initOrders(portfolio.st, initDate=initDate)

strategy(strategy.st, store=TRUE)

#add signals
add.signal(strategy.st, name = "sigComparison", arguments = list(columns = 
              c("SPY.Close", "SPY.SMA"), relationship = "gt"), label = "Cl.gt.SMA")
add.signal(strategy.st, name = "sigComparison", arguments = list(columns = 
              c("SPY.Close", "SPY.SMA"), relationship = "lt"), label = "Cl.lt.SMA")

#test = applySignals(strategy.st, mktdata = SPY)

#add.rules
add.rule(strategy.st, name = "ruleSignal", arguments = list(sigCol = "Cl.gt.SMA", 
              sigval = 1, orderqty = 900, ordertype = "market", orderside = "long"), 
              type = "enter")

add.rule(strategy.st, name = "ruleSignal", arguments = list(sigCol = "Cl.lt.SMA", 
              sigval = 1, orderqty = "all", ordertype = "market", orderside = "long"), 
              type = "exit")


strat = getStrategy(strategy.st)
summary(strat)

#apply the strategy and get the transactions
applyStrategy(strategy = strategy.st, portfolios = portfolio.st)
getTxns(Portfolio = portfolio.st, Symbol = "SPY")

【问题讨论】:

【参考方案1】:

这是您的代码的一个工作示例(不止一种仪器,只是 2017 年的数据,使示例更易于测试),我希望通过调整解决您的所有问题:

require(quantstrat)
require(quantmod)
require(FinancialInstrument)

symbols = c("SPY", "GOOG")

initDate="2000-01-01"
from="2017-01-01"
to=Sys.Date()
options(width=70)

options("getSymbols.warning4.0"=FALSE)

#set account currency and system timezone
currency('USD')
stock("SPY",currency="USD",multiplier=1)
stock("GOOG", currency = "USD")
Sys.setenv(TZ="UTC")

tradeSize <- 1e6
initEq <- tradeSize*length(symbols)

getSymbols(symbols, from=from, to=to, src="yahoo", adjust=TRUE)

rm.strat(strategy.st)

initPortf(portfolio.st, symbols=symbols, initDate=initDate, currency='USD')
initAcct(account.st, portfolios=portfolio.st, initDate=initDate, 
         currency='USD',initEq=initEq)
initOrders(portfolio.st, initDate=initDate)

strategy(strategy.st, store=TRUE)



#SPY = indMerge(x = symbols, period = "weeks", k = 1, SMAlength = 14, maType = "SMA")
#Brians code 
#>> http://r.789695.n4.nabble.com/R-Quantstrat-package-question-td3772989.html
AddWeeklyMA <- function(x, period, k, SMAlength, maType)
  # Don't use getSymbols here. It is redundant.  Use x, which is the daily OHLC for the symbol you've selected
  #mktdata <- getSymbols(x, auto.assign = FALSE)

  # YOU MUST USE indexAt as "endof" instead of "startof" otherwise you introduce look ahead bias in your weekly indicator signal. (Sees the future in the backtest)  Bad!!!
  xW <- to.period(x, period = period, k = k, indexAt = "endof")
  wSMA <- SMA(Cl(xW), n = SMAlength, maType = maType)

  y <- merge(wSMA, xts(, order.by = index(x)), fill = na.locf) 
  x <- y[index(x)] # just to be safe, in case any extra timestamps where generated in the merge above to make y.
  # Note that the column name of x is simply "SMA" rather than "SPY.SMA" etc.  You can of course relabel the column name here if you wish.
  x
 

add.indicator(strategy = strategy.st, 
              name = "AddWeeklyMA", 
              arguments = list(x = quote(mktdata),
                               period = "weeks",
                               k = 1,
                               SMAlength = 14,
                               maType = "SMA"),
              label = "weeklyMA")


#add signals.
# one way to handle column names is simply rename the OHLC columns, removing the name of the symbol.  e.g. .Open .High .Low .Close, then pass columns = c(".Close", "SMA.weeklyMA") which will work for multiple symbols.
#add.signal(strategy.st, name = "sigCrossover", arguments = list(columns = 
#                                                                   c("SPY.Close", "SMA.weeklyMA"), relationship = "gt"), label = "Cl.gt.SMA")

sigCrossoverWrapper <- function(x, label, col1 = "Close", col2 = "weeklyMA", relationship) 
  Col1 <- grep(pattern = col1, colnames(x), value = TRUE)[1]
  Col2 <- grep(pattern = col2, colnames(x), value = TRUE)[1]  
  # Basic checks.  If we can't find a column name like col1, col2, throw an error. also use the [1] index, particular for col1 values like "Close", in case multiple column labels are returned that all contain "Close".  The assumption is that the OHLC data are always the first for columns in the symbol data object.
  stopifnot(length(Col1) == 1)
  stopifnot(length(Col2) == 1)
  columns <- c(Col1, Col2)
  r <- sigCrossover(data = x, label = label, columns = columns, relationship = relationship)
  r


add.signal(strategy.st, name = "sigCrossoverWrapper", arguments = list(x = quote(mktdata), 
                                                                       col1 = "Close", 
                                                                       col2 = "weeklyMA", 
                                                                       relationship = "gt"), 
           label = "Cl.gt.SMA")

add.signal(strategy.st, name = "sigCrossoverWrapper", arguments = list(x = quote(mktdata), 
                                                                       col1 = "Close", 
                                                                       col2 = "weeklyMA", 
                                                                       relationship = "lt"), 
           label = "Cl.lt.SMA")

add.rule(strategy.st, name = "ruleSignal", arguments = list(sigcol = "Cl.gt.SMA", 
                                                            sigval = 1, 
                                                            orderqty = 900, 
                                                            ordertype = "market", 
                                                            orderside = "long"), 
         label = "enterL", # Add label names for rules.
         type = "enter")

add.rule(strategy.st, name = "ruleSignal", arguments = list(sigcol = "Cl.lt.SMA", 
                                                            sigval = 1, 
                                                            orderqty = "all", 
                                                            ordertype = "market", 
                                                            orderside = "long"), 
         label = "exitL", # Add label names for rules.
         type = "exit")


applyStrategy(strategy = strategy.st, portfolios = portfolio.st)

# [1] "2017-07-06 00:00:00 GOOG 900 @ 906.690002"
# [1] "2017-07-07 00:00:00 GOOG -900 @ 918.590027"
# [1] "2017-07-10 00:00:00 GOOG 900 @ 928.799988"
# [1] "2017-07-28 00:00:00 GOOG -900 @ 941.530029"
# [1] "2017-09-14 00:00:00 GOOG 900 @ 925.109985"
# [1] "2017-09-15 00:00:00 GOOG -900 @ 920.289978"
# [1] "2017-09-28 00:00:00 GOOG 900 @ 949.5"
# [1] "2017-04-18 00:00:00 SPY 900 @ 231.585741736316"
# [1] "2017-08-21 00:00:00 SPY -900 @ 241.70049982835"
# [1] "2017-08-23 00:00:00 SPY 900 @ 243.352306359548"

for (sym in symbols) 
  print(paste0("txns for ", sym, ":"))
  print(getTxns(Portfolio = portfolio.st, Symbol = sym)  )

您的代码有几个问题:

没有触发任何规则,因为您在 add.rules 中使用了 sigCol 而不是 sigcol。这基本上是一个错误,意味着没有任何规则被测试。 (要亲自查看,请将 browser() 粘贴到您的 ruleSignal 版本中并通过调试器)。

sigComparison 更改为sigCrossover。当relationship = gt 时,对于一个列的所有值大于另一列的所有值,Sig 比较返回1/TRUE。我想你想在收盘价和 MA 的交叉点上入场。

为规则提供唯一标签是一种很好的做法。

以上代码将您的代码概括为多个符号。这样做的一部分涉及概括您的自定义指标,我将其命名为 AddWeeklyMA

关于您希望能够传递“通用”列名称columns = c("Close", "SMA") 的评论,获得所需效果的一种方法是像我对sigCrossoverWrapper 所做的那样创建一个包装函数。您还可以通过简单地重命名每个符号对象内的相关列来避免想要针对 [symbol].Close 形式的列名进行测试的问题(即,将代码名称放在 OHLC 列名中,正如我在上面提到的代码)。

【讨论】:

以上是关于quantstrat:尽管信号值似乎有效,但策略不采取立场?的主要内容,如果未能解决你的问题,请参考以下文章

Quantitative Trading with R:两个简单的策略

R quantstrat 代码中的 While 循环 - 如何使其更快?

增量值有效,但不适用于附加的 HTML [重复]

尽管路径似乎正确,但文件不存在

尽管时区有效,但设置默认时区不起作用

如何在 quantstrat 中使用 sigPeak() 函数