【问题标题】:how to match quote and trade data with data.table (rolling join)如何将报价和交易数据与 data.table 匹配(滚动连接)
【发布时间】:2023-04-10 10:28:02
【问题描述】:

我正在拼命尝试重现经典的Pandas 滚动连接示例,其中quotes 数据与trade 数据合并。

请看这里https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.merge_asof.html

这是data.table格式的数据:

trades <- data.table(time = c('2016-05-25 13:30:00.023',
                              '2016-05-25 13:30:00.038',
                              '2016-05-25 13:30:00.048',
                              '2016-05-25 13:30:00.048',
                              '2016-05-25 13:30:00.048'),
                     ticker = c('MSFT','MSFT','GOOG','GOOG','AAPL'),
                     price = c(51.95,51.95,720.77,720.92,98.0),
                     quantity = c(75,155,100,100,100))
> trades
                      time ticker  price quantity
1: 2016-05-25 13:30:00.023   MSFT  51.95       75
2: 2016-05-25 13:30:00.038   MSFT  51.95      155
3: 2016-05-25 13:30:00.048   GOOG 720.77      100
4: 2016-05-25 13:30:00.048   GOOG 720.92      100
5: 2016-05-25 13:30:00.048   AAPL  98.00      100

和引号

quotes <- data.table(time = c('2016-05-25 13:30:00.023',
                              '2016-05-25 13:30:00.023',
                              '2016-05-25 13:30:00.030',
                              '2016-05-25 13:30:00.041',
                              '2016-05-25 13:30:00.048',
                              '2016-05-25 13:30:00.049',
                              '2016-05-25 13:30:00.072',
                              '2016-05-25 13:30:00.075'),
                     ticker = c('GOOG','MSFT','MSFT','MSFT','GOOG','AAPL','GOOG','MSFT'),
                     bid = c(720.50, 51.95, 51.97, 51.99, 720.5,97.99,720.5,52.01),
                     ask = c(270.93,51.96,51.98,52.00,720.93,98.01,720.88,52.03))
> quotes
                      time ticker    bid    ask
1: 2016-05-25 13:30:00.023   GOOG 720.50 270.93
2: 2016-05-25 13:30:00.023   MSFT  51.95  51.96
3: 2016-05-25 13:30:00.030   MSFT  51.97  51.98
4: 2016-05-25 13:30:00.041   MSFT  51.99  52.00
5: 2016-05-25 13:30:00.048   GOOG 720.50 720.93
6: 2016-05-25 13:30:00.049   AAPL  97.99  98.01
7: 2016-05-25 13:30:00.072   GOOG 720.50 720.88
8: 2016-05-25 13:30:00.075   MSFT  52.01  52.03

我想做的是通过以下方式将交易数据与报价数据合并

  1. 对于每笔交易,尽可能匹配最接近的上一个报价
  2. 匹配的报价必须在 10 毫秒内
  3. 应该发生完全匹配。

输出(与 Pandas 教程中的相同)应该是

                      time ticker  price quantity   bid   ask
1: 2016-05-25 13:30:00.023   MSFT  51.95       75    NA    NA
2: 2016-05-25 13:30:00.038   MSFT  51.95      155 51.97 51.98
3: 2016-05-25 13:30:00.048   GOOG 720.77      100    NA    NA
4: 2016-05-25 13:30:00.048   GOOG 720.92      100    NA    NA
5: 2016-05-25 13:30:00.048   AAPL  98.00      100    NA    NA

确实,您可以看到唯一可能的报价匹配是2016-05-25 13:30:00.038 的第二笔交易,因为关闭(上一个)报价发生在2016-05-25 13:30:00.030,所以这是在 10 毫秒内(而不是完全匹配)。

尽管我进行了试验,但我无法在 data.table 中重现此内容。有任何想法吗? 谢谢!

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    您还可以将this idiom 与滚动连接结合使用, 这与@sindri_baldur 提出的相似但不完全相等:

    library(lubridate)
    library(data.table)
    
    quotes[, time := as.POSIXct(time, format="%Y-%m-%d %H:%M:%OS", tz = "GMT")]
    trades[, time := as.POSIXct(time, format="%Y-%m-%d %H:%M:%OS", tz = "GMT")]
    
    match_inexact <- function(q_time, t_time, bid, ask) {
      exact <- q_time == t_time # exact matches get NA
      bid[exact] <- NA_real_
      ask[exact] <- NA_real_
      list(bid, ask)
    }
    
    trades[, c("bid", "ask") := quotes[.SD,
                                       match_inexact(x.time, i.time, x.bid, x.ask),
                                       on = .(ticker, time),
                                       roll = lubridate::dmilliseconds(10L)]]
    

    需要注意的重要一点: time 是为联接指定的最后一列,因为这是 data.table 将尝试滚动值的列。

    【讨论】:

    • 哇不错。你介意再解释一下发生了什么吗?
    • 在这种情况下,滚动连接表示法(当您指定roll 时激活)意味着:对于用于连接的最后一列,如果没有完全匹配,最多向后查看 10 毫秒。使用match_inexact,我们可以简单地检查时间是否滚动:如果x.time == i.time,没有滚动,则完全匹配。该成语只是通过引用将结果列(每个列在一个列表中)分配给trades
    • 好的,知道了。但是有一点, i.time 指的是什么?左 DT 中的时间变量?
    • 啊,在data.table 文档中,他们通常使用变量x[i, j, by] 来描述事物,其中i 可以是另一个data.table。在连接期间,它们会公开带有 x.i. 前缀的变量,具体取决于取自哪个表的值。所以i.time来自.SD,代表来自trades的数据子集。
    • 知道了。我猜是x。指的是左边的DT?在这种情况下trades?
    【解决方案2】:

    在 10 毫秒窗口内使用最新报价的另一种可能的非 equi 连接方法:

    options(digits.secs=3) #see https://stackoverflow.com/a/43475068/1989480
    library(data.table)
    
    quotes[, time := as.POSIXct(time, format="%Y-%m-%d %H:%M:%OS", tz = "GMT")]
    trades[, time := as.POSIXct(time, format="%Y-%m-%d %H:%M:%OS", tz = "GMT")][,
        c("start", "end") := .(time-0.01, time)]
    
    trades[, c("bid", "ask") :=
            quotes[trades, on=.(ticker, time>=start, time<end), mult="last", .(bid, ask)]
        ][, c("start", "end") := NULL]
    

    输出:

                          time ticker  price quantity   bid   ask
    1: 2016-05-25 13:30:00.023   MSFT  51.95       75    NA    NA
    2: 2016-05-25 13:30:00.038   MSFT  51.95      155 51.97 51.98
    3: 2016-05-25 13:30:00.048   GOOG 720.77      100    NA    NA
    4: 2016-05-25 13:30:00.048   GOOG 720.92      100    NA    NA
    5: 2016-05-25 13:30:00.048   AAPL  98.00      100    NA    NA
    

    【讨论】:

    • 很酷的东西。你介意解释一下你在这里做什么吗? mult 参数是什么?
    • mult arg 允许您处理具有一对多匹配的情况(当mult="last" 使用最后一个观察时,请参阅?data.table 了解更多详细信息)。我认为加入条件很明确?
    • 非常感谢!并假设我想要交易后(并且在 10 毫秒内)的第一个报价,我所需要的只是切换时间
    • iiuc,我认为它是trades[, c("after_start","after_end") := .(time, time+0.01)]; ......[, time&gt;after_start, time&lt;=after_end,如果您使用tradetime 而不仅仅是time,它将有助于您理解
    • 类似 DT[,colToBeUsedInJoin:=existingColumnsWithCalcIfNecessary]
    【解决方案3】:

    这里有一些东西(快速而肮脏)可以完成工作:

    # Format as POSIXct*
    quotes[, time := as.POSIXct(time, format="%Y-%m-%d %H:%M:%OS", tz = "GMT")]
    trades[, time := as.POSIXct(time, format="%Y-%m-%d %H:%M:%OS", tz = "GMT")]
    
    # Match the nearest time (in the right direction) for each ticker and add as column
    trades[quotes, on = .(time > time, ticker), qtime := i.time]
    # Remove if not within time limit (10 millsecs)
    trades[(time - qtime) > 0.01, qtime := NA_real_]
    # Now perform an equi-join after removing timestamp that was too distant
    trades[, c("bid", "ask") := quotes[trades, on = .(time = qtime), .(bid, ask)]]
    trades[, !"qtime"] # drop this temporary column
    
    #                   time ticker  price quantity   bid   ask
    # 1: 2016-05-25 13:30:00   MSFT  51.95       75    NA    NA
    # 2: 2016-05-25 13:30:00   MSFT  51.95      155 51.97 51.98
    # 3: 2016-05-25 13:30:00   GOOG 720.77      100    NA    NA
    # 4: 2016-05-25 13:30:00   GOOG 720.92      100    NA    NA
    # 5: 2016-05-25 13:30:00   AAPL  98.00      100    NA    NA
    

    * POSIXct 向量已构建 在双向量之上,其中的值表示自 1970-01-01 以来的秒数

    从 Alexis 的帖子中学习的是一个稍微干净的版本,它使用了 roll 参数。

    trades[, c("qtime", "bid", "ask") := quotes[.SD, roll = 0.01, on = .(ticker, time), .(x.time, bid, ask)]]
    trades[time == qtime, c("bid", "ask") := NA_real_][, qtime := NULL]
    

    【讨论】:

    • 有趣。谢谢!你能解释一下你在那里做什么吗? qtime := i.time 是做什么的?为什么我们不能用一些roll 参数一次性完成?谢谢!
    • 将尝试添加更多的 cmets。也许有一种方法可以通过一个滚动参数来做到这一点。我是非 equi 连接的初学者。
    • 这里有同样的想法。 time &gt; time 有点模棱两可。如何确保我指的是quotestimetradestime
    • 同意。但在这种情况下,第一次指的是trades。无论如何,我会选择第二个 cleaner 版本。
    猜你喜欢
    • 2021-05-10
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-22
    • 1970-01-01
    相关资源
    最近更新 更多