首页 文章

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

提问于
浏览
8

以下是我正在使用quantstrat进行多时间帧策略的示例 . 这是做多时间框架策略的正确方法还是我做错了?我没有遇到任何其他在quantstrat演示或谷歌搜索中做多时间帧的例子 .

为了保持策略部分简单(这不是某人会交易的策略)并将重点放在多时间框架方面,我将演示一个使用刻度数据和5分钟OHLC数据的简单策略 . 策略逻辑是当蜱数据超过5分钟数据的30周期SMA时买入,并在蜱数据超过相同SMA时关闭仓位 .

例如:如果策略是平坦的,则时间是13:02,并且之前观察到的5分钟数据的30周期SMA是90.55(对于12:55-结束13:00的时段),以及刻度数据从低于90.55到高于它(90.56)的交叉是买入,当蜱数据再次低于它时,它退出该位置 .

为了实现这一点,我需要将tick数据和5分钟,30周期SMA同时放入同一个对象中以进行quantstrat处理 . 我得到了5分钟的OHLC xts并计算出它的30周期SMA . 然后我将它合并到刻度数据xts对象中,这将给我一个包含所有刻度数据的对象,然后每隔5分钟,我将得到最后一个观察到的5分钟,30周期SMA的行 .

如果在13:00有30个周期的SMA值,则为5分12:55-13:00 . 由于下次更新SMA是5分钟后,我需要填充行直到观察到下一个值(在13:05),依此类推 .

这是勾选数据的 head (我没有使用 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分钟数据的 head (每分钟代表前一分钟的数据,例如时间戳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 函数的工作方式,即discussed in this thread . 基本上是第一次的时间戳09:04:00 == OHLC 4分钟的数据从09:00:00 - 09:04:00 . 从09:04:00 - 09:09:00 09:09:00时间戳是下一个完整的5分钟 . 理想情况下,我希望每个时间戳都是5,10,15等,但我还没有弄清楚如何做到这一点 .

将5分钟数据的30 SMA转换为刻度数据

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 列 . SMA30 在11:29:00的值是从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)

那么总结一下这是做多时间框架策略的最佳方法吗?

1 回答

  • 2

    以下是将多个时间帧指示符/信号合并到您的策略中的两种方法 . 两者都只使用quantstrat样本数据开箱即用 .

    两者都遵循相同的策略(并给出相同的结果):该策略在1分钟柱上使用SMA(20),在30分钟柱上使用SMA(10)以产生交易信号 . 当SMA(20,1分钟柱)越过SMA(10,30分钟柱)时,输入多头头寸 . 当SMA(20,1分钟柱)穿过SMA(10,30分钟柱)时,从多头位置退出

    Approach 1: Build price data and indicators on lower time frequencies within a custom function called by add.indicator. (you can't go to higher time frequencies than the raw market data for the symbol).

    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
    

    Approach 2: The idea is that we have already computed daily market data with names in the global namespace of [symbol].d (see below for what I mean). This daily data could be loaded from disk into memory as well, obviously. We use these precomputed data sets on different time frequencies, instead of computing the bar data inside the indicator function (such as is done in indDailyMA above):

    这种方法可以说是更高级和更有效的内存,因为我们不计算聚合(例如,当使用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
    

    您可能还会发现有关此主题的其他参考资料很有用:

    Generating indicators of different periodicity in quantstrat

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

相关问题