quantstrat: Стратегия не открывает позиции, несмотря на то, что значения сигналов кажутся действительными?

Я открыл новый вопрос, поскольку исходный пост был уже слишком длинным, и решение, к которому я пришел в значительной степени отрицает этот пост.

Ниже приведен обходной путь, который я нашел для добавления индикаторов с периодичностью, отличной от периодичности ваших данных. Решение было основано на этом сообщении на сайте R-SIG. - Список рассылки по финансам Брайана Петерсона.

Есть две основные проблемы с кодом.

1) Стратегия не открывает никаких позиций, несмотря на генерируемые сигналы (соответствующие столбцы существуют в mktdata после запуска стратегии)

2) Как ни странно, если indMerge() явно не переименовывает столбец SMA в "SPY.SMA" и используется add.signal(columns = c("SPY.Close", SPY.SMA")), возникает следующая ошибка (то есть я не могу передать columns = c("Close", "SMA") в add.signal:

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

person user3180258    schedule 30.11.2016    source источник


Ответы (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)  )
}

У вас было несколько проблем с кодом:

Никакие правила не срабатывали, потому что вы использовали sigCol вместо sigcol в add.rules. В основном это ошибка, и это означает, что ни одно из правил не тестировалось. (Чтобы убедиться в этом лично, воткните browser() в свою версию ruleSignal и пройдитесь отладчиком).

Измените sigComparison на sigCrossover. Сравнение Sig возвращает 1/TRUE для всех значений одного столбца больше, чем значение другого столбца, когда relationship = gt. Я думаю, вы хотите войти в пересечение Close и MA.

Рекомендуется давать уникальные метки для правил.

Приведенный выше код обобщает ваш код на несколько символов. Частью этого является обобщение вашего пользовательского индикатора, который я назвал AddWeeklyMA.

Что касается вашего комментария о желании иметь возможность передавать «общие» имена столбцов, columns = c("Close", "SMA"), один из способов получить желаемый эффект - создать функцию-оболочку, как я сделал с sigCrossoverWrapper. Вы также можете избежать проблемы, связанной с желанием протестировать имя столбца в форме [symbol].Close, просто переименовав рассматриваемый столбец внутри каждого объекта символа (т.е. удалив имя тикера в именах столбцов OHLC, как я упоминал выше в коде ).

person FXQuantTrader    schedule 08.11.2017