在时差内匹配观测值的最快方法

时间:2018-10-26 14:32:58

标签: r for-loop dplyr plyr

我正在计算具有特定时间差(例如60秒)的交易之间的价格差。我需要用几项资产和几笔交易来完成此任务。但是,如果没有永恒的for循环,我无法找到一种方法。

让我们创建一些随机价格:

library(birk)
library(tictoc)
library(dplyr)

initial.date <- as.POSIXct('2018-10-27 10:00:00',tz='GMT')
last.date <- as.POSIXct('2018-10-28 17:00:00',tz='GMT')

PriorityDateTime=seq.POSIXt(from=initial.date,to = last.date,by = '30 sec')
TradePrice=seq(from=1, to=length(PriorityDateTime),by = 1)

ndf<- data.frame(PriorityDateTime,TradePrice)
ndf$InstrumentSymbol <- rep_len(x = c('asset1','asset2'),length.out = length(ndf$PriorityDateTime))
ndf$id <- seq(1:length(x = ndf$InstrumentSymbol))

我的主要功能如下: 对于每笔交易(在TradePrice列),我需要找到间隔为60秒的最近交易。

calc.spread <- function(df,c=60){
  n<-length(df$PriorityDateTime)
  difft <- dspread <- spread <- rep(0,n)
  TimeF <- as.POSIXct(NA)
  for (k in 1:n){
    diffs <- as.POSIXct(df$PriorityDateTime) - as.POSIXct(df$PriorityDateTime[k])
    idx <- which.closest(diffs,x=c)  
    TimeF[k]<- as.POSIXct(df$PriorityDateTime[idx])
    difft[k] <- difftime(time1 = TimeF[k],time2 = df$PriorityDateTime[k], units = 'sec')
    dspread[k] <- abs(df$TradePrice[k] - df$TradePrice[idx])
    spread[k] <- 2*abs(log(df$TradePrice[k]) - log(df$TradePrice[idx]))

  }

  df <- data.frame(spread,dspread,difft,TimeF,PriorityDateTime=df$PriorityDateTime,id=df$id)
}

函数which.closest只是which.min(abs(vec-x))的包装。当我有一个包含多个资产的数据框时,我运行:

c=60
spreads <- ndf %>% group_by(InstrumentSymbol) %>% do(calc.spread(.,c=c))

问题是我需要为300万行数据帧运行此命令。我在论坛上进行了搜索,但找不到更快运行此代码的方法。 dpply比使用dplyr慢一点。

有什么建议吗?

2 个答案:

答案 0 :(得分:0)

您可能会犯一个错误,即您不是在寻找所描述的60秒内的最小差异,而是在寻找过去或将来尽可能接近60秒的交易:

idx <- which.closest(diffs,x=c)

使用此交易发生在1秒钟前,而交易发生在60秒钟之内将被丢弃,我认为这不是您想要的。您可能希望60秒内所有交易的最低价差可以通过以下方式完成:

res$idx[i] <<-  which.min(pricediff)[1]

请参见下面的代码:

library(lubridate)
library(dplyr)
ndf$datetime <- ymd_hms(ndf$PriorityDateTime)
res <- ndf %>% data.frame(stringsAsFactors = F)
res$dspread <- res$idx <- res$spread <- NA
sapply(1:nrow(res),function(i){
  within60 <- abs(difftime(ndf$datetime[i],ndf$datetime,"secs"))<=60
  samesymbol <- res$InstrumentSymbol[i]==res$InstrumentSymbol
  isdifferenttrade <- 1:nrow(res)!=i 
  pricediff <- ifelse(within60&samesymbol&isdifferenttrade,abs(res$TradePrice[i]-res$TradePrice), Inf)

  res$dspread[i] <<-  min(pricediff)
  res$idx[i] <<-  which.min(pricediff)[1] #in case several elements have same price 
  res$spread[i] <<-  2*abs(log(res$TradePrice[i])-log(res$TradePrice[res$idx[i]]))
} )
head(res)

我使用的是apply,它类似于for循环(甚至比它慢)。如果这对于您的真实数据而言更快,那是因为我以较少的步骤进行操作。

让我知道,否则您可以在for循环中尝试相同的操作,否则我们将不得不尝试使用我不太熟悉的data.table。当然,这些通常很耗时,因为您需要根据每行数据定义条件。

     PriorityDateTime TradePrice InstrumentSymbol id            datetime    spread idx
1 2018-10-27 10:00:00          1           asset1  1 2018-10-27 10:00:00 2.1972246   3
2 2018-10-27 10:00:30          2           asset2  2 2018-10-27 10:00:30 1.3862944   4
3 2018-10-27 10:01:00          3           asset1  3 2018-10-27 10:01:00 2.1972246   1
4 2018-10-27 10:01:30          4           asset2  4 2018-10-27 10:01:30 1.3862944   2
5 2018-10-27 10:02:00          5           asset1  5 2018-10-27 10:02:00 1.0216512   3
6 2018-10-27 10:02:30          6           asset2  6 2018-10-27 10:02:30 0.8109302   4
  dspread
1       2
2       2
3       2
4       2
5       2
6       2

答案 1 :(得分:0)

我对先前的回答非常不满意,我向here寻求帮助,结果发现data.table中至少有一种方法显然更快。还提出了与dplyr相关的问题here

s <- Sys.time()
initial.date <- as.POSIXct('2018-10-27 10:00:00',tz='GMT')
last.date <- as.POSIXct('2018-12-28 17:00:00',tz='GMT')
PriorityDateTime=seq.POSIXt(from=initial.date,to = last.date,by = '30 sec');length(PriorityDateTime)
TradePrice=seq(from=1, to=length(PriorityDateTime),by = 1)
ndf<- data.frame(PriorityDateTime,TradePrice)
ndf$InstrumentSymbol <- rep_len(x = c('asset1','asset2'),length.out = length(ndf$PriorityDateTime))
ndf$id <- seq(1:length(x = ndf$InstrumentSymbol))
ndf$datetime <- ymd_hms(ndf$PriorityDateTime)
res <- ndf %>% data.table()
res2 <- setDT(res)
res2 <- res2[, `:=` (min_60 = datetime - 60, plus_60 = datetime + 60, idx = .I)][
  res2,  on = .(InstrumentSymbol = InstrumentSymbol, datetime >= min_60, datetime <= plus_60), allow.cartesian = TRUE][
    idx != i.idx, .SD[which.min(abs(i.TradePrice - TradePrice))], by = id][
      , .(id, minpricewithin60 = i.TradePrice, index.minpricewithin60 = i.idx)][
        res, on = .(id)][, `:=` (min_60 = NULL, plus_60 = NULL, idx = NULL)]
res2[]
e <- Sys.time()
> e-s
Time difference of 1.23701 mins

然后,您可以将calc.spread函数直接应用于minpricewithin60列。