干货丨DolphinDB高频数据处理技巧:数据透视的应用

反射苔原
• 阅读 1737

行列转换(pivot)是一个常见的整理数据的需求,又称为转置或者透视。

高频数据通常以下图的格式保存:每一行为一个股票在某个时刻的信息。

干货丨DolphinDB高频数据处理技巧:数据透视的应用

我们进行数据处理时,考虑到后续的向量化操作,有时会希望数据或者中间结果将原始数据转置,即每行代表不同的时刻,而每列代表一只股票。在DolphinDB中可通过pivot by语句对原始数据或分组聚合结果进行行列转置。若与向量化操作搭配使用,在高频数据处理和计算中,行列转换不仅可简化策略代码,还能提高代码效率。具体请看下面的两个例子。

  1. 计算股票收益的两两相关性

在配对交易(pair trading)及风险对冲(hedging)时,经常需要计算给定一篮子股票之间的两两相关性。这种复杂的计算在传统的数据库中无法执行,而使用一般的统计软件不仅需要数据迁移,还需要繁琐的代码。下面我们使用DolphinDB来计算股票收益的两两相关性。

首先,载入美股股票高频交易数据库:

quotes = loadTable("dfs://TAQ", "quotes")

接下来,选择2009年8月4日中500只报价变动最频繁的股票:

dateValue=2009.08.04
num=500
syms = (exec count(*) from quotes where date = dateValue, time between 09:30:00 : 15:59:59, 0<bid, bid<ofr, ofr<bid*1.1 group by Symbol order by count desc).Symbol[0:num]

下面我们利用pivot by将高频数据降维成为分钟级数据,并且改变原始数据的结构,生成一个分钟级股票价格矩阵:每一列是一只股票;每一行是一分钟。

priceMatrix = exec avg(bid + ofr)/2.0 as price from quotes where date = dateValue, Symbol in syms, 0<bid, bid<ofr, ofr<bid*1.1, time between 09:30:00 : 15:59:59 pivot by time.minute() as minute, Symbol

DolphinDB的语言非常灵活。在这里,pivot by不仅将数据转换为透视表,同时也可以搭配聚合函数使用,具有"group by"的功能。

利用高阶函数each将价格矩阵转换为收益率矩阵:

retMatrix = each(def(x):ratios(x)-1, priceMatrix)

利用高阶函数pcross计算这500只股票之间收益的两两相关性:

corrMatrix = pcross(corr, retMatrix)

选取与每只股票相关性最高的10只股票:

mostCorrelated = select * from table(corrMatrix).rename!(`sym`corrSym`corr) context by sym having rank(corr,false) between 1:10

选取与SPY相关性最高的10只股票:

select * from mostCorrelated where sym='SPY' order by corr desc

quotes总共有2,693亿条数据,2009年8月4日这天一共有近1.9亿条数据,执行上面的计算,耗时仅需1,952毫秒

  1. 计算股票组合的价值

在进行指数套利交易回测时,需要计算给定股票组合的价值。当数据量极大时,回测时采用一般数据分析系统,对系统内存及速度的要求极高。而DolphinDB database 从底层进行优化,对硬件的要求不高。

在本例中,为了简化起见,假定某个指数只由两只股票组成:AAPL与FB,时间戳精度为纳秒,指数成分权重存在weights字典中。

Symbol=take(`AAPL, 6) join take(`FB, 5)
Time=2019.02.27T09:45:01.000000000+[146, 278, 412, 445, 496, 789, 212, 556, 598, 712, 989]
Price=173.27 173.26 173.24 173.25 173.26 173.27 161.51 161.50 161.49 161.50 161.51
quotes=table(Symbol, Time, Price)
weights=dict(`AAPL`FB, 0.6 0.4)
ETF = select Symbol, Time, Price*weights[Symbol] as weightedPrice from quotes
select last(weightedPrice) from ETF pivot by Time, Symbol;

运行结果如下:

Time                          AAPL    FB
----------------------------- ------- ------
2019.02.27T09:45:01.000000146 103.962
2019.02.27T09:45:01.000000212         64.604
2019.02.27T09:45:01.000000278 103.956
2019.02.27T09:45:01.000000412 103.944
2019.02.27T09:45:01.000000445 103.95
2019.02.27T09:45:01.000000496 103.956
2019.02.27T09:45:01.000000556         64.6
2019.02.27T09:45:01.000000598         64.596
2019.02.27T09:45:01.000000712         64.6
2019.02.27T09:45:01.000000789 103.962
2019.02.27T09:45:01.000000989         64.604

由于时间戳精度为纳秒,基本上所有交易的时间戳均不一致。如果回测时的数据行数极多(几亿或几十亿行)且指数成分股数量也较多(如S&P500指数的500只成分股),使用传统分析系统,要计算任一时刻的指数价值,需要将原始数据表的3列(时间,股票代码,价格)转换为同等长度但是宽度为指数成分股数量+1的数据表,向前补充空值(forward fill NULLs),进而计算每行的指数成分股对指数价格的贡献之和。这种做法,会产生比原始数据表大很多倍的中间过程数据表,很有可能会导致系统内存不足。同时,计算速度也很慢。

使用DolphinDB的pivot by语句,只需要一行代码,即可实现上述的所有步骤,代码简洁,而且无需产生中间过程数据表,有效避免内存不足的问题,同时极大提升计算速度。

select rowSum(ffill(last(weightedPrice))) from ETF pivot by Time, Symbol;

结果如下:

Time                          rowSum
----------------------------- -------
2019.02.27T09:45:01.000000146 103.962
2019.02.27T09:45:01.000000212 168.566
2019.02.27T09:45:01.000000278 168.56
2019.02.27T09:45:01.000000412 168.548
2019.02.27T09:45:01.000000445 168.554
2019.02.27T09:45:01.000000496 168.56
2019.02.27T09:45:01.000000556 168.556
2019.02.27T09:45:01.000000598 168.552
2019.02.27T09:45:01.000000712 168.556
2019.02.27T09:45:01.000000789 168.562
2019.02.27T09:45:01.000000989 168.566

DolphinDB下载:DolphinDB

使用过程中有任何问题,欢迎加入智臾科技:DolphinDB技术交流群,内含二维码

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Peter20 Peter20
4年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
4个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
反射苔原
反射苔原
Lv1
道理只会告诉你对错但未必能给你幸福.
文章
4
粉丝
0
获赞
0