Самый быстрый способ сопоставления наблюдений с разницей во времени - PullRequest
0 голосов
/ 26 октября 2018

Я рассчитываю разницу в ценах между сделками, которые имеют определенную разницу во времени (скажем, 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 - это просто оболочка для которого .min (abs (vec - x)). Поскольку у меня есть фрейм данных с несколькими активами, я запускаю:

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

Проблема в том, что мне нужно запустить это для 3 миллионов фреймов данных строки. Я искал на форуме, но не мог найти способ запустить этот код быстрее. Ddply немного медленнее, чем dplyr.

Есть ли какие-либо предложения?

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Будучи весьма неудовлетворенным моим собственным предыдущим ответом, я попросил здесь о помощи и оказалось, что в data.table есть по крайней мере один способ, который явно быстрее.Также задан вопрос, связанный с dplyr здесь

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.

0 голосов
/ 28 октября 2018

Возможно, вы допустили ошибку в том смысле, что вы не ищете минимальную разницу в пределах разницы в 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
...