使用quantstrat进行多时间框架策略的正确方法是什么?

7
这是我正在使用 quantstrat 开发的一个多时间框架策略示例。这是正确的多时间框架策略实现方式吗?我在 quantstrat 演示中没有找到任何其他多时间框架示例,也没有通过谷歌搜索找到。为了保持策略简单(这不是一个人们会交易的策略),并保持对多时间框架方面的关注,我将演示一个简单的策略,该策略使用 Tick 数据和 5 分钟 OHLC 数据。策略逻辑是在 Tick 数据穿过 5 分钟数据的 30 周期 SMA 时买入,并在 Tick 数据再次穿过同一 SMA 时平仓。
例如:如果策略处于空头状态,时间是 13:02,先前观察到的 5 分钟数据的 30 周期 SMA 是 90.55(12:55-13:00 的周期),并且 Tick 数据从 90.55 下方穿过到上方(90.56)则进行买入,当 Tick 数据再次下穿时退出仓位。
为了实现这一点,我需要将 Tick 数据和 5 分钟、30 周期 SMA 合并到同一个对象中,以便 quantstrat 进行处理。我获取 5 分钟的 OHLC xts 并计算其 30 周期 SMA。然后将其合并到 Tick 数据 xts 对象中,这将给我一个包含所有 Tick 数据的对象,然后每 5 分钟我将获得最后观察到的 5 分钟、30 周期 SMA 的行。
如果在 13:00 有一个 30 周期 SMA 值,则为 5 分钟 12:55-13:00。由于 SMA 的下一个更新时间是 5 分钟后,因此需要向下填充行,直到观察到下一个值(在 13:05)等等。
以下是 Tick 数据的头部(我拥有的 Tick 数据不包括毫秒,但我已使用 make.index.unique(clemtick) 使行唯一):
head(clemtick)
                    Price Volume
2013-01-15 09:00:00 93.90      1
2013-01-15 09:00:00 93.89      1
2013-01-15 09:00:00 93.89      1
2013-01-15 09:00:00 93.88      2
2013-01-15 09:00:00 93.89      1
2013-01-15 09:00:00 93.89      2

以下是1分钟数据的“头部”(每分钟代表前一分钟的数据,例如时间戳09:01:00 == 从09:00:00到09:01:00的数据):

head(clemin)
                     Open  High   Low Close Volume
2013-01-15 09:01:00 93.90 94.04 93.87 93.97   1631
2013-01-15 09:02:00 93.97 93.98 93.90 93.91    522
2013-01-15 09:03:00 93.91 93.97 93.90 93.96    248
2013-01-15 09:04:00 93.95 93.98 93.93 93.95    138
2013-01-15 09:05:00 93.95 93.96 93.91 93.92    143
2013-01-15 09:06:00 93.93 93.97 93.91 93.91    729

将1分钟的数据转换为5分钟的数据:

cle5min <- to.minutes5(clemin)
                    clemin.Open clemin.High clemin.Low clemin.Close clemin.Volume
2013-01-15 09:04:00       93.90       94.04      93.87        93.95          2539
2013-01-15 09:09:00       93.95       93.97      93.81        93.89          2356
2013-01-15 09:14:00       93.90       94.05      93.86        93.89          4050
2013-01-15 09:19:00       93.90       94.03      93.84        94.00          2351
2013-01-15 09:24:00       93.99       94.21      93.97        94.18          3261
2013-01-15 09:29:00       94.18       94.26      94.18        94.19          1361

你会注意到第一个OHLC是09:04:00,这是因为to.minutes5函数的工作方式,这在这个线程中讨论过。基本上,第一个时间戳09:04:00 == OHLC 4分钟的数据从09:00:00 - 09:04:00。09:09:00时间戳是从09:04:00 - 09:09:00的下一个完整的5分钟。理想情况下,我希望每个时间戳都是5、10、15等,但我还没有找出如何做到这一点。
将5分钟数据的30 SMA加入到tick数据中。
clemtick$sma30 <- SMA(cle5min$clemin.Close, 30)

这将创建一个带有SMA的新列。SMA需要30个周期来计算第一个值,而且SMA仅会出现在每5分钟的时间戳(11:29:00、11:34:00、11:39等)中。它看起来像:

clemtick["2013-01-15 11:28:59::2013-01-15 11:29:00"]
                    Price Volume    SMA30
2013-01-15 11:28:59 93.87      1       NA
2013-01-15 11:28:59 93.87      1       NA
2013-01-15 11:28:59 93.88      1       NA
2013-01-15 11:29:00 93.87      1 93.92633
2013-01-15 11:29:00 93.87      1       NA
2013-01-15 11:29:00 93.88      1       NA
2013-01-15 11:29:00 93.88      1       NA

现在我需要用重复值填充SMA30列。11:29:00时的SMA30值是从11:24:00到11:29:00的OHLC。下一次更新此值将在11:34:00之后,因此我需要向下填充行,直到下一个值,因为这是策略在逐行处理时将引用的内容。

clemtick  <- na.locf(clemtick)

现在,如果我再次查询该对象,
clemtick["2013-01-15 11:33:58::2013-01-15 11:34:01"]
                    Price Volume    SMA30
2013-01-15 11:33:58 93.84      1 93.92633
2013-01-15 11:34:00 93.84      1 93.92267
2013-01-15 11:34:00 93.85      1 93.92267
2013-01-15 11:34:01 93.84      1 93.92267

现在我们有了最终的对象,接下来运行策略:

require(quantstrat)

options("getSymbols.warning4.0"=FALSE)
rm(list=ls(.blotter), envir=.blotter)
Sys.setenv(TZ="UTC")

symbols  <- "clemtick"
currency('USD')
stock(symbols, currency="USD", multiplier=1)

account.st  <- 0
strategy.st  <- portfolio.st <- account.st  <- "multi"
rm.strat(portfolio.st)
rm.strat(strategy.st)

initDate <- "1980-01-01"
tradeSize  <- 1000
initEq  <- tradeSize*length(symbols)
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.signal(strategy.st, name="sigCrossover",
  arguments=list(columns=c("Price", "sma30"), relationship="gt"),
  label="golong") 

add.signal(strategy.st, name="sigCrossover",
  arguments=list(columns=c("Price", "sma30"), relationship="lt"),
  label="exitlong")

#enter rule
add.rule(strategy.st, name="ruleSignal",
  arguments=list(sigcol="golong",
                 sigval=TRUE,
                 ordertype="market",
                 orderside="long",
                 replace=TRUE,
                 prefer="Price",
                 orderqty=1),
  type="enter", path.dep=TRUE, label="long")

#exit rule
add.rule(strategy.st, name = "ruleSignal",
  arguments=list(sigcol="exitlong",
                 sigval=TRUE,
                 ordertype="market",
                 orderside="long",
                 replace=TRUE,
                 prefer="Price",
                 orderqty=-1),
  type="exit", path.dep=TRUE, label="exitlong")

#apply strategy
t1 <- Sys.time()
out2 <- applyStrategy(strategy=strategy.st, portfolios=portfolio.st, debug=TRUE)
t2 <- Sys.time()
print(t2-t1)
head(mktdata)
nrow(mktdata)

所以总结一下,这是实施多时间框架策略的最佳方式吗?

肯定有一些人在做多时间框架策略吧? - GeV 126
是的,但我们正在进行交易,而不是测试。 :) 我会尽快在接下来的几天内回复。 - Joshua Ulrich
1个回答

0
这里有两种方法可以将多时间框架指标/信号纳入您的策略中。两者都可以直接使用quantstrat示例数据。
两者遵循相同的策略(并提供相同的结果):该策略使用1分钟K线上的SMA(20)和30分钟K线上的SMA(10)生成交易信号。当SMA(20,1分钟K线)上穿SMA(10,30分钟K线)时,进入多头头寸。当SMA(20,1分钟K线)下穿SMA(10,30分钟K线)时,退出多头头寸。
方法1:在由add.indicator调用的自定义函数中构建较低时间频率的价格数据和指标。(不能超过符号的原始市场数据的较高时间频率)。
from <- "2002-10-20"
to <- "2002-10-24"

symbols <- "GBPUSD"
# Load 1 minute data stored in the quantstrat package
getSymbols.FI(Symbols = symbols,
              dir=system.file('extdata',package='quantstrat'),
              from=from, 
              to=to
)

currency(c('GBP', 'USD'))
exchange_rate('GBPUSD', tick_size=0.0001)

strategy.st <- "multiFrame"
portfolio.st <- "multiFrame"
account.st <- "multiFrame"

initEq <- 50000

rm.strat(strategy.st)
initPortf(portfolio.st, symbols = symbols)
initAcct(account.st, portfolios = portfolio.st, initEq = initEq)
initOrders(portfolio.st)
strategy(strategy.st, store = TRUE)

# Create an SMA on 20 1 minute bars:
add.indicator(strategy.st, name = "SMA", 
              arguments = list(x = quote(Cl(mktdata)),
                                n = 20), 
              label = "MA20")

# Define the function that add.indicator will use to create an SMA(10) on 30 minute bars:
ind30minMA <- function(x, n30min = 10) {

  if (!is.OHLC(x)) 
    stop("Must pass in OHLC data")
  x.h <- to.period(x[, 1:4], period = "minutes", k = 30, indexAt = "endof") 
  #^ Ensure that the timestamp on the lower frequency data is at the END of the bar/candle, to avoid look forward bias.

  # If you need to know what symbol you are currently processing:
  # symbol <- parent.frame(n = 2)$symbol
  sma.h <- SMA(Cl(x.h), n = n30min)
  r <- merge(sma.h, xts(, index(x)), fill= na.locf) 
  #^ Carry forward the last value, no lookforward bias introduced

  r <- r[index(x)]
  # if you don't return the same # of rows in the argument x, then quantstrat won't work correctly. So let's check the data is OK after the merge above:
  stopifnot(NROW(r) == NROW(x))
  r
}

add.indicator(strategy.st, name = "ind30minMA", 
              arguments = list(x = quote(mktdata),
                               n30min = 10), 
              label = "MA30minbar")

add.signal(strategy.st, name = "sigCrossover", 
              arguments = list(columns = c("SMA.MA20", "SMA.MA30minbar"),
                               relationship = "gt"),
              label = "FastCrossUp")

add.signal(strategy.st, name = "sigCrossover", 
           arguments = list(columns = c("SMA.MA20", "SMA.MA30minbar"),
                            relationship = "lt"),
           label = "FastCrossDn")

add.rule(strategy.st,name='ruleSignal', 
         arguments = list(sigcol="FastCrossUp",
                          sigval=TRUE, 
                          orderqty= 100, 
                          ordertype='market', 
                          orderside='long', 
                          threshold=NULL),
         type='enter',
         label='enterL',
         storefun=FALSE
)

add.rule(strategy.st,name='ruleSignal',
         arguments = list(sigcol="FastCrossDn",
                          sigval=TRUE,
                          orderqty='all',
                          ordertype='market',
                          orderside='long',
                          threshold=NULL,
                          orderset='default',
                          replace = TRUE),
         type='exit',
         label='exitL'
)


applyStrategy(strategy.st, portfolio.st)


tail(mktdata)
# Open   High    Low  Close Volume SMA.MA20 SMA.MA30minbar FastCrossUp FastCrossDn
# 2002-10-24 17:54:00 1.5552 1.5552 1.5552 1.5552      0 1.555115        1.55467          NA          NA
# 2002-10-24 17:55:00 1.5552 1.5552 1.5551 1.5551      0 1.555120        1.55467          NA          NA
# 2002-10-24 17:56:00 1.5551 1.5551 1.5551 1.5551      0 1.555125        1.55467          NA          NA
# 2002-10-24 17:57:00 1.5551 1.5551 1.5551 1.5551      0 1.555130        1.55467          NA          NA
# 2002-10-24 17:58:00 1.5551 1.5551 1.5551 1.5551      0 1.555130        1.55467          NA          NA
# 2002-10-24 17:59:00 1.5551 1.5551 1.5551 1.5551      0 1.555135        1.55478          NA          NA

tx <- getTxns(portfolio.st, "GBPUSD")
# Record total PL earned.  This number should be identical to the result from the second approach listed below:
sum(tx$Net.Txn.Realized.PL)
# -0.03

方法2:我们的想法是,我们已经使用全局命名空间中的名称计算了每日市场数据[symbol].d(请参见下面我所说的内容)。这些每日数据也可以从磁盘加载到内存中。我们在不同的时间频率上使用这些预先计算的数据集,而不是在指标函数内计算条形数据(例如在上面的indDailyMA中执行的操作):

这种方法在内存方面可能更先进和高效,因为我们没有计算聚合(例如在处理tick数据时可能会计算成本昂贵)。

library(quantstrat)

from <- "2002-10-20"
to <- "2002-10-24"

symbols <- "GBPUSD"
# Load 1 minute data stored in the quantstrat package
getSymbols.FI(Symbols = symbols,
              dir=system.file('extdata',package='quantstrat'),
              from=from, 
              to=to
)

currency(c('GBP', 'USD'))
exchange_rate('GBPUSD', tick_size=0.0001)

strategy.st <- "multiFrame"
portfolio.st <- "multiFrame"
account.st <- "multiFrame"

# Parameters:

initEq <- 50000



rm.strat(strategy.st)
initPortf(portfolio.st, symbols = symbols)
initAcct(account.st, portfolios = portfolio.st, initEq = initEq)
initOrders(portfolio.st)
strategy(strategy.st, store = TRUE)


GBPUSD <- GBPUSD[, colnames(GBPUSD) != "Volume"]

# Before running the backtest, create the lower frequency market data
GBPUSD.30m <- to.period(OHLC(GBPUSD), period = "minutes", k = 30, indexAt = "endof", name = "GBPUSD") 

GBPUSD.1m.idx <- index(GBPUSD)

NROW(GBPUSD)
# 5276

# Add the lower frequency data indicators to the higher frequency data that will be processed in quantstrat.  Fill forward the lower frequency moving average

GBPUSD <- merge(GBPUSD, setNames(SMA(Cl(GBPUSD.30m), n = 10), "SMA.MA30minbar"))
GBPUSD$SMA.MA30minbar <- na.locf(GBPUSD$SMA.MA30minbar)

# Note: Short hand for the above will the fill argument, which can be helpful in special cases where NAs only exist in the new data to be added:
# GBPUSD <- merge(GBPUSD, setNames(SMA(Cl(GBPUSD.30m), n = 10), "SMA.MA30minbar"),  fill = na.locf)

NROW(GBPUSD)
# 5276

# After doing this merge, sometimes extra rows will appear beyond what GBPUSD (based on the original 1 min bar data) 
GBPUSD <- GBPUSD[GBPUSD.1m.idx, ]

# Now GBPUSD, which will be the raw data used in applyStrategy, already contains the 30 min bar indicators.

add.indicator(strategy.st, name = "SMA", 
              arguments = list(x = quote(Cl(mktdata)),
                               n = 20), 
              label = "MA20")



add.signal(strategy.st, name = "sigCrossover", 
           arguments = list(columns = c("SMA.MA20", "SMA.MA30minbar"),
                            relationship = "gt"),
           label = "FastCrossUp")

add.signal(strategy.st, name = "sigCrossover", 
           arguments = list(columns = c("SMA.MA20", "SMA.MA30minbar"),
                            relationship = "lt"),
           label = "FastCrossDn")

add.rule(strategy.st,name='ruleSignal', 
         arguments = list(sigcol="FastCrossUp",
                          sigval=TRUE, 
                          orderqty= 100, 
                          ordertype='market', 
                          orderside='long', 
                          threshold=NULL),
         type='enter',
         label='enterL',
         storefun=FALSE
)

add.rule(strategy.st,name='ruleSignal',
         arguments = list(sigcol="FastCrossDn",
                          sigval=TRUE,
                          orderqty='all',
                          ordertype='market',
                          orderside='long',
                          threshold=NULL,
                          orderset='sysMACD',
                          replace = TRUE),
         type='exit',
         label='exitL'
)


applyStrategy(strategy.st, portfolio.st)


tail(mktdata)
# Open   High    Low  Close SMA.MA30minbar SMA.MA20 FastCrossUp FastCrossDn
# 2002-10-24 17:54:00 1.5552 1.5552 1.5552 1.5552        1.55467 1.555115          NA          NA
# 2002-10-24 17:55:00 1.5552 1.5552 1.5551 1.5551        1.55467 1.555120          NA          NA
# 2002-10-24 17:56:00 1.5551 1.5551 1.5551 1.5551        1.55467 1.555125          NA          NA
# 2002-10-24 17:57:00 1.5551 1.5551 1.5551 1.5551        1.55467 1.555130          NA          NA
# 2002-10-24 17:58:00 1.5551 1.5551 1.5551 1.5551        1.55467 1.555130          NA          NA
# 2002-10-24 17:59:00 1.5551 1.5551 1.5551 1.5551        1.55478 1.555135          NA          NA

tx <- getTxns(portfolio.st, "GBPUSD")
sum(tx$Net.Txn.Realized.PL)
# -0.03

# Same result as the first approach, as we would expect

您还可以找到以下与此主题相关的其他参考资料:

在quantstrat中生成不同周期的指标

http://r.789695.n4.nabble.com/R-Quantstrat-package-question-td3772989.html


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接