1. 概述
对数据库进行合理的分区可以显著降低系统响应延迟,提高数据吞吐量。合理的分区设计需要综合考虑数据分布的特征、查询和计算的典型场景、数据更新的频率等方方面面。
本文为入门级教程,针对几种常见的金融数据给出了较为通用的分区方案和示例脚本,以帮助初次体验 DolphinDB 的用户快速完成建库建表。
本文扩展阅读:
2. 数据存储方案总览
存储方案包括金融数据存储方案和因子库存储方案两大部分。
2.1 金融数据存储方案
针对不同金融数据,给出以下的分区存储方案:
2.2 因子库窄表存储方案
因子挖掘是量化交易的必备环节之一。随着量化交易和AI模型训练规模的发展,量化投资团队在投研环节势必需要处理大量因子数据。因子的存储也是一个关键问题,本文推荐使用窄表存储因子库。
(窄表存储模型)
(宽表存储模型)
相较于宽表,窄表存储有以下优势:
-
更加高效地删除过期失效因子
- 宽表的方案是删除全表的某一列,相比窄表方案效率低下
-
更加高效地添加新因子
- 宽表的方案是先增加列,然后更新新增列的值,相比窄表方案效率低下
-
更加高效地按照因子名更新指定因子的值
- 宽表的方案是 update 全表的某一列,相比窄表效率低下
-
单次多因子面板数据的查询效率和宽表近似
- DolphinDB 通过对 pivot by 方法的并行优化,保证了窄表存储方案的多因子数据查询效率
针对不同频率的因子数据,给出以下的分区存储方案,均采用 TSDB 存储引擎:
3. 金融数据存储方案实践
基于上一章的分区方案,本章将提供对应的 DolphinDB 脚本。
3.1 股票数据
3.1.1 Levle-2 行情数据
股票 Levle-2 行情数据包含 Levle-2 快照数据、逐笔委托数据、逐笔成交数据,由于在分布式数据库中,如果多个分区的数据表要连接(join)通常十分耗时,因为涉及到的分区可能在不同的节点上,需要在不同节点之间复制数据。为解决这个问题,DolphinDB 推出了共存储位置的分区机制,确保同一个分布式数据库里所有表在相同分区的数据存储在相同的节点上。这样的安排,保证了这些表在连接的时候非常高效。因此在本方案中,把 Levle-2 快照数据、逐笔委托数据、逐笔成交数据这些分区方案一致的数据表存入同一个数据库中。
上交所和深交所两个交易所的数据在字段结构上略有不同,用户在建立库表时可以考虑是否将两个交易所的数据进行合并,如果选择存为一张表,则表中的字段是两个交易所数据字段的并集,并新增字段 Market 用于标识数据来自哪个交易所。level-1 的分区方案采用 level-2 的即可。
如果您希望将通联数据导入 DolphinDB 数据库,可以参考如下教程,使用封装好的模块即可完成建库建表和 level-2 行情数据导入:
下面将根据是否将沪深交易所分开存储介绍不同建库建表的分区规则。
3.1.1.1 Levle-2快照(沪深分表)
深交所
sql
create database "dfs://split_SZ_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 25])
engine='TSDB'
create table "dfs://split_SZ_TB"."split_SZ_snapshotTB"(
TradeTime TIMESTAMP[comment="时间列", compress="delta"]
MDStreamID SYMBOL
SecurityID SYMBOL
SecurityIDSource SYMBOL
TradingPhaseCode SYMBOL
PreCloPrice DOUBLE
NumTrades LONG
TotalVolumeTrade LONG
TotalValueTrade DOUBLE
LastPrice DOUBLE
OpenPrice DOUBLE
HighPrice DOUBLE
LowPrice DOUBLE
DifPrice1 DOUBLE
DifPrice2 DOUBLE
PE1 DOUBLE
PE2 DOUBLE
PreCloseIOPV DOUBLE
IOPV DOUBLE
TotalBidQty LONG
WeightedAvgBidPx DOUBLE
TotalOfferQty LONG
WeightedAvgOfferPx DOUBLE
UpLimitPx DOUBLE
DownLimitPx DOUBLE
OpenInt INT
OptPremiumRatio DOUBLE
OfferPrice DOUBLE[]
BidPrice DOUBLE[]
OfferOrderQty LONG[]
BidOrderQty LONG[]
BidNumOrders INT[]
OfferNumOrders INT[]
LocalTime TIME
SeqNo INT
OfferOrders LONG[]
BidOrders LONG[]
)
partitioned by TradeTime, SecurityID,
sortColumns=[`SecurityID,`TradeTime],
keepDuplicates=ALL
Levle-2行情数据使用"时间维度按天+股票维度 HASH25 "的分区规则,使用存储引擎 TSDB ,使用"股票代码+交易时间"作为排序列。下面对上述脚本进行说明:
- TSDB 引擎只支持创建分布式数据库,库名前必须添加"dfs://";
- 在此处建立分区表时对某些列使用了指定压缩算法,对TradeTime列使用了"delta"压缩算法,其余列则默认使用"lz4"压缩算法;
- 建表时指定 OfferPrice 为 DOUBLE[] 类型, 表示将 10 档卖价数据存储在一个字段中,该字段类型为 DOUBLE 类型的数组向量(arrray vector),更详细的 array vector 介绍见:DolphinDB 中有关 array vector 的最佳实践指南 ;
- sortColumns 参数用于将写入的数据按照指定字段进行排序,系统默认 sortColumns (指定多列时) 最后一列为时间列,其余列字段作为排序的索引列,称作 sort key。频繁查询的字段适合设置为 sortColumns,且建议优先把查询频率高的字段作为 sortColumns 中位置靠前的列;
- keepDuplicates 参数表示在每个分区内如何处理所有 sortColumns 之值皆相同的数据,在两个函数中该参数的值均为"ALL"表示保留所有数据。
上交所
sql
create database "dfs://split_SH_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 25])
engine='TSDB'
create table "dfs://split_SH_TB"."split_SH_snapshotTB"(
TradeTime TIMESTAMP[comment="时间列", compress="delta"]
SecurityID SYMBOL
ImageStatus INT
PreCloPrice DOUBLE
OpenPrice DOUBLE
HighPrice DOUBLE
LowPrice DOUBLE
LastPrice DOUBLE
ClosePrice DOUBLE
TradingPhaseCode SYMBOL
NumTrades LONG
TotalVolumeTrade LONG
TotalValueTrade DOUBLE
TotalBidQty LONG
WeightedAvgBidPx DOUBLE
AltWAvgBidPri DOUBLE
TotalOfferQty LONG
WeightedAvgOfferPx DOUBLE
AltWAvgAskPri DOUBLE
ETFBuyNumber INT
ETFBuyAmount LONG
ETFBuyMoney DOUBLE
ETFSellNumber INT
ETFSellAmount LONG
ETFSellMoney DOUBLE
YieldToMatu DOUBLE
TotWarExNum DOUBLE
UpLimitPx DOUBLE
DownLimitPx DOUBLE
WithdrawBuyNumber INT
WithdrawBuyAmount LONG
WithdrawBuyMoney DOUBLE
WithdrawSellNumber INT
WithdrawSellAmount LONG
WithdrawSellMoney DOUBLE
TotalBidNumber INT
TotalOfferNumber INT
MaxBidDur INT
MaxSellDur INT
BidNum INT
SellNum INT
IOPV DOUBLE
OfferPrice DOUBLE[]
BidPrice DOUBLE[]
OfferOrderQty LONG[]
BidOrderQty LONG[]
BidNumOrders INT[]
OfferNumOrders INT[]
LocalTime TIME
SeqNo INT
OfferOrders LONG[]
BidOrders LONG[]
)
partitioned by TradeTime, SecurityID,
sortColumns=[`SecurityID,`TradeTime],
keepDuplicates=ALL
上交所的数据格式与深交所略有不同,但分区方案一致。
3.1.1.2 逐笔委托(沪深分表)
深交所
sql
create database "dfs://split_SZ_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 25])
engine='TSDB'
create table "dfs://split_SZ_TB"."split_SZ_entrustTB"(
ChannelNo INT
ApplSeqNum LONG
MDStreamID SYMBOL
SecurityID SYMBOL
SecurityIDSource SYMBOL
Price DOUBLE
OrderQty LONG
Side SYMBOL
TradeTime TIMESTAMP[comment="时间列", compress="delta"]
OrderType SYMBOL
LocalTime TIME
SeqNo INT
)
partitioned by TradeTime, SecurityID,
sortColumns=[`SecurityID,`TradeTime],
keepDuplicates=ALL
用户可以使用上述代码以创建存储深交所逐笔委托数据的数据库表,分区规则采用了"时间维度按日 + 股票代码HASH25 "的组合分区方式存储,使用存储引擎 TSDB, 使用 "股票代码+交易时间" 作为排序列。
上交所
sql
create database "dfs://split_SH_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 25])
engine='TSDB'
create table "dfs://split_SH_TB"."split_SH_entrustTB"(
DataStatus INT
ApplSeqNum LONG
ChannelNo INT
SecurityID SYMBOL
TradeTime TIMESTAMP[comment="时间列", compress="delta"]
OrderType SYMBOL
OrderNO INT
Price DOUBLE
OrderQty LONG
Side SYMBOL
BizIndex INT
LocalTime TIME
SeqNo INT
)
partitioned by TradeTime, SecurityID,
sortColumns=[`SecurityID,`TradeTime],
keepDuplicates=ALL
上交所的数据格式与深交所略有不同,但分区方案一致。
3.1.1.3 逐笔成交(沪深分表)
深交所
sql
create database "dfs://split_SZ_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 25])
engine='TSDB'
create table "dfs://split_SZ_TB"."split_SZ_tradeTB"(
ChannelNo INT
ApplSeqNum LONG
MDStreamID SYMBOL
BidApplSeqNum LONG
OfferApplSeqNum LONG
SecurityID SYMBOL
SecurityIDSource SYMBOL
TradePrice DOUBLE
TradeQty INT
ExecType SYMBOL
TradeTime TIMESTAMP[comment="时间列", compress="delta"]
LocalTime TIME
SeqNo INT
OrderKind SYMBOL
)
partitioned by TradeTime, SecurityID,
sortColumns=[`SecurityID,`TradeTime],
keepDuplicates=ALL
用户可以使用上述代码以创建存储深交所逐笔委托数据的数据库表,分区规则采用了"时间维度按日 + 股票代码HASH25 "的组合分区方式存储,使用存储引擎 TSDB ,使用"股票代码+交易时间"作为排序列。
上交所
sql
create database "dfs://split_SH_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 25])
engine='TSDB'
create table "dfs://split_SH_TB"."split_SH_tradeTB"(
DataStatus INT
ApplSeqNum INT
ChannelNo INT
SecurityID SYMBOL
TradeTime TIMESTAMP[comment="时间列", compress="delta"]
TradePrice DOUBLE
TradeQty LONG
TradeMoney DOUBLE
BidApplSeqNum LONG
OfferApplSeqNum LONG
TradeBSFlag SYMBOL
BizIndex LONG
LocalTime TIME
SeqNo INT
)
partitioned by TradeTime, SecurityID,
sortColumns=[`SecurityID,`TradeTime],
keepDuplicates=ALL
上交所的数据格式与深交所略有不同,但分区方案一致。
3.1.1.4 Levle-2快照(沪深合并)
sql
create database "dfs://merge_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 50])
engine='TSDB'
create table "dfs://merge_TB"."merge_snapshotTB"(
Market SYMBOL
TradeTime TIMESTAMP[comment="时间列", compress="delta"],
MDStreamID SYMBOL
SecurityID SYMBOL
SecurityIDSource SYMBOL
TradingPhaseCode SYMBOL
ImageStatus INT
PreCloPrice DOUBLE
NumTrades LONG
TotalVolumeTrade LONG
TotalValueTrade DOUBLE
LastPrice DOUBLE
OpenPrice DOUBLE
HighPrice DOUBLE
LowPrice DOUBLE
ClosePrice DOUBLE
DifPrice1 DOUBLE
DifPrice2 DOUBLE
PE1 DOUBLE
PE2 DOUBLE
PreCloseIOPV DOUBLE
IOPV DOUBLE
TotalBidQty LONG
WeightedAvgBidPx DOUBLE
AltWAvgBidPri DOUBLE
TotalOfferQty LONG
WeightedAvgOfferPx DOUBLE
AltWAvgAskPri DOUBLE
UpLimitPx DOUBLE
DownLimitPx DOUBLE
OpenInt INT
OptPremiumRatio DOUBLE
OfferPrice DOUBLE[]
BidPrice DOUBLE[]
OfferOrderQty LONG[]
BidOrderQty LONG[]
BidNumOrders INT[]
OfferNumOrders INT[]
ETFBuyNumber INT
ETFBuyAmount LONG
ETFBuyMoney DOUBLE
ETFSellNumber INT
ETFSellAmount LONG
ETFSellMoney DOUBLE
YieldToMatu DOUBLE
TotWarExNum DOUBLE
WithdrawBuyNumber INT
WithdrawBuyAmount LONG
WithdrawBuyMoney DOUBLE
WithdrawSellNumber INT
WithdrawSellAmount LONG
WithdrawSellMoney DOUBLE
TotalBidNumber INT
TotalOfferNumber INT
MaxBidDur INT
MaxSellDur INT
BidNum INT
SellNum INT
LocalTime TIME
SeqNo INT
OfferOrders LONG[]
BidOrders LONG[]
)
partitioned by TradeTime, SecurityID,
sortColumns=[`Market,`SecurityID,`TradeTime],
keepDuplicates=ALL
用户可以使用上述代码建立同时存储沪深Levle-2行情数据的数据库表,使用"时间维度按天+股票维度 HASH50 "的分区规则,使用存储引擎 TSDB, 使用 "交易所类型+股票代码+交易时间" 作为排序列。与沪深分开存储的建库建表的分区规则类似,仅在HASH数量和排序列方面有所区别。
3.1.1.5 逐笔委托(沪深合并)
sql
create database "dfs://merge_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 50])
engine='TSDB'
create table "dfs://merge_TB"."merge_entrustTB"(
ChannelNo INT
ApplSeqNum LONG
MDStreamID SYMBOL
SecurityID SYMBOL
SecurityIDSource SYMBOL
Price DOUBLE
OrderQty LONG
Side SYMBOL
TradeTime TIMESTAMP[comment="时间列", compress="delta"]
OrderType SYMBOL
LocalTime TIME
SeqNo LONG
OrderNO LONG
DataStatus INT
BizIndex LONG
Market SYMBOL
)
partitioned by TradeTime, SecurityID,
sortColumns=[`Market,`SecurityID,`TradeTime],
keepDuplicates=ALL
用户可以使用上述代码建立同时存储沪深Levle-2行情数据的数据库表,使用"时间维度按天+股票维度 HASH50 "的分区规则,使用存储引擎 TSDB ,使用 "交易所类型+股票代码+交易时间" 作为排序列。与沪深分开存储的建库建表的分区规则类似,仅在HASH数量和排序列方面有所区别。
3.1.1.6 逐笔成交(沪深合并)
sql
create database "dfs://merge_TB"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 50])
engine='TSDB'
create table "dfs://merge_TB"."merge_tradeTB"(
ChannelNo INT
ApplSeqNum LONG
MDStreamID SYMBOL
BidApplSeqNum LONG
OfferApplSeqNum LONG
SecurityID SYMBOL
SecurityIDSource SYMBOL
TradePrice DOUBLE
TradeQty LONG
ExecType SYMBOL
TradeTime TIMESTAMP[comment="时间列", compress="delta"]
LocalTime TIME
SeqNo LONG
DataStatus INT
TradeMoney DOUBLE
TradeBSFlag SYMBOL
BizIndex LONG
OrderKind SYMBOL
Market SYMBOL
)
partitioned by TradeTime, SecurityID,
sortColumns=[`Market,`SecurityID,`TradeTime],
keepDuplicates=ALL
用户可以使用上述代码建立同时存储沪深Levle-2行情数据的数据库表,使用"时间维度按天+股票维度 HASH50 "的分区规则,使用存储引擎 TSDB ,使用 "交易所类型+股票代码+交易时间" 作为排序列。与沪深分开存储的建库建表的分区规则类似,仅在HASH数量和排序列方面有所区别。
3.1.2 股票日K数据
sql
create database "dfs://k_day_level"
partitioned by RANGE(2000.01M + (0..30)*12)
engine='OLAP'
create table "dfs://k_day_level"."k_day"(
securityid SYMBOL
tradetime TIMESTAMP
open DOUBLE
close DOUBLE
high DOUBLE
low DOUBLE
vol INT
val DOUBLE
vwap DOUBLE
)
partitioned by tradetime
用户可以使用上述代码建立同时存储日K的数据库表,使用"时间维度按年 "的分区规则,使用存储引擎 OLAP。每个分区内都包含了这一年所有股票的日K数据。
3.1.3 股票分钟 K 数据
sql
create database "dfs://k_minute_level"
partitioned by VALUE(2020.01.01..2021.01.01)
engine='OLAP'
create table "dfs://k_minute_level"."k_minute"(
securityid SYMBOL
tradetime TIMESTAMP
open DOUBLE
close DOUBLE
high DOUBLE
low DOUBLE
vol INT
val DOUBLE
vwap DOUBLE
)
partitioned by tradetime
用户可以使用上述代码建立同时存储日K的数据库表,使用"时间维度按天 "的分区规则,存储引擎使用 OLAP。每个分区内都包含了当天所有股票的分钟K数据。
3.2 期权数据
3.2.1 期权快照数据
sql
create database "dfs://ctp"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 20])
engine='TSDB'
create table "dfs://ctp"."options"(
TradingDay DATE[comment="时间列", compress="delta"]
ExchangeID SYMBOL
LastPrice DOUBLE
PreSettlementPrice DOUBLE
PreClosePrice DOUBLE
PreOpenInterest DOUBLE
OpenPrice DOUBLE
HighestPrice DOUBLE
LowestPrice DOUBLE
Volume INT
Turnover DOUBLE
OpenInterest DOUBLE
ClosePrice DOUBLE
SettlementPrice DOUBLE
UpperLimitPrice DOUBLE
LowerLimitPrice DOUBLE
PreDelta DOUBLE
CurrDelta DOUBLE
UpdateTime SECOND
UpdateMillisec INT
BidPrice DOUBLE[]
BidVolume INT[]
AskPrice DOUBLE[]
AskVolume INT[]
AveragePrice DOUBLE
ActionDay DATE
InstrumentID SYMBOL
ExchangeInstID STRING
BandingUpperPrice DOUBLE
BandingLowerPrice DOUBLE
tradeTime TIME
receivedTime NANOTIMESTAMP
perPenetrationTime LONG
)
partitioned by TradingDay, InstrumentID,
sortColumns=[`InstrumentID,`ReceivedTime],
keepDuplicates=ALL
用户可以使用上述代码以创建存储期权快照数据的数据库表,分区规则采用了"时间维度按天 + 期权代码维度 HASH20 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "期权代码+接收时间" 作为排序列。经测试期货快照数据采用这种方式分区存储,综合性能最佳。
3.3 期货数据
3.3.1 期货快照数据
sql
create database "dfs://ctp"
partitioned by VALUE(2020.01.01..2021.01.01), HASH([SYMBOL, 10])
engine='TSDB'
create table "dfs://ctp"."futures"(
TradingDay DATE[comment="时间列", compress="delta"]
ExchangeID SYMBOL
LastPrice DOUBLE
PreSettlementPrice DOUBLE
PreClosePrice DOUBLE
PreOpenInterest DOUBLE
OpenPrice DOUBLE
HighestPrice DOUBLE
LowestPrice DOUBLE
Volume INT
Turnover DOUBLE
OpenInterest DOUBLE
ClosePrice DOUBLE
SettlementPrice DOUBLE
UpperLimitPrice DOUBLE
LowerLimitPrice DOUBLE
PreDelta DOUBLE
CurrDelta DOUBLE
UpdateTime SECOND
UpdateMillisec INT
BidPrice DOUBLE[]
BidVolume INT[]
AskPrice DOUBLE[]
AskVolume INT[]
AveragePrice DOUBLE
ActionDay DATE
InstrumentID SYMBOL
ExchangeInstID STRING
BandingUpperPrice DOUBLE
BandingLowerPrice DOUBLE
tradeTime TIME
receivedTime NANOTIMESTAMP
perPenetrationTime LONG
)
partitioned by TradingDay, InstrumentID,
sortColumns=[`InstrumentID,`ReceivedTime],
keepDuplicates=ALL
用户可以使用上述代码以创建存储期货快照数据的数据库表,分区规则采用了"时间维度按天 + 期货代码维度 HASH10 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "期货代码+接收时间" 作为排序列。经测试期货快照数据采用这种方式分区存储,综合性能最佳。
3.3.2 期货日 K 数据
由于期货K数据与股票K数据类似,因此可以使用股票K数据的表格结构作为期货K数据的表格结构
sql
create database "dfs://ctp_k_day_level"
partitioned by RANGE(2000.01M + (0..30)*12)
engine='OLAP'
create table "dfs://ctp_k_day_level"."ctp_k_day"(
securityid SYMBOL
tradetime TIMESTAMP
open DOUBLE
close DOUBLE
high DOUBLE
low DOUBLE
vol INT
val DOUBLE
vwap DOUBLE
)
partitioned by tradetime
用户可以使用上述代码以创建存储期货日K数据的数据库表,与股票日K数据的分区规则相同,使用"时间维度按年 "的分区规则,存储引擎使用 OLAP。在一个分区中包含了这一年所有期货的日K数据。
3.3.3 期货分钟 K 数据
sql
create database "dfs://ctp_k_minute_level"
partitioned by VALUE(2020.01.01..2021.01.01)
engine='OLAP'
create table "dfs://ctp_k_minute_level"."ctp_k_minute"(
securityid SYMBOL
tradetime TIMESTAMP
open DOUBLE
close DOUBLE
high DOUBLE
low DOUBLE
vol INT
val DOUBLE
vwap DOUBLE
)
partitioned by tradetime
用户可以使用上述代码以创建存储期货分钟K数据的数据库表,与股票分钟K数据的分区规则相同,使用"时间维度按天 "的分区规则,存储引擎使用 OLAP。每个分区内都包含了当天所有期货的分钟K数据。
3.4 银行间债券
3.4.1 X-Bond 报价
vbnet
create database "dfs://XBondDepth"
partitioned by VALUE(2023.01.01..2023.12.31)
engine='TSDB'
create table "dfs://XBondDepth"."XBondDepthTable"(
bondCodeVal SYMBOL
createTime TIMESTAMP
marketDepth LONG
mdBookType LONG
messageId LONG
messageSource STRING
msgSeqNum LONG
msgType STRING
bidmdEntryPrice DOUBLE[]
offermdEntryPrice DOUBLE[]
bidmdEntrySize LONG[]
offermdEntrySize LONG[]
bidsettlType LONG[]
offersettlType LONG[]
bidyield DOUBLE[]
offeryield DOUBLE[]
bid1yieldType STRING
offer1yieldType STRING
bid2yieldType STRING
offer2yieldType STRING
bid3yieldType STRING
offer3yieldType STRING
bid4yieldType STRING
offer4yieldType STRING
bid5yieldType STRING
offer5yieldType STRING
bid6yieldType STRING
offer6yieldType STRING
securityID SYMBOL
senderCompID STRING
senderSubID STRING
sendingTime TIMESTAMP
)
partitioned by createTime,
sortColumns=[`securityID, `createTime]
用户可以使用上述代码以创建存储X-Bond报价数据的数据库表,使用"时间维度按天 "的分区规则,使用存储引擎 TSDB ,使用 "债券代码+创建时间" 作为排序列。每个分区内都包含了当天所有 X-Bond 报价数据。
3.4.2 X-Bond 成交
vbnet
create database "dfs://XBondTrade"
partitioned by VALUE(2023.01.01..2023.12.31)
engine='TSDB'
create table "dfs://XBondTrade"."XBondTradetable"(
createTime TIMESTAMP
securityID SYMBOL
beforeClosingPrice DOUBLE
beforeClosingYield DOUBLE
beforeWeightedAveragePrice DOUBLE
beforeWeightedAverageYield DOUBLE
fillSide LONG
highestPrice DOUBLE
highestYield DOUBLE
lowestPrice DOUBLE
lowestYield DOUBLE
marketIndicator LONG
mdSubType LONG
mdType LONG
messageId LONG
messageSource STRING
msgSeqNum LONG
msgType STRING
openingValence DOUBLE
openingYield DOUBLE
priceRiseFallAmplitude DOUBLE
senderCompID STRING
sendingTime TIMESTAMP
settlType LONG
symbol STRING
tradeMethod LONG
transactTime TIMESTAMP
transactionNumber LONG
uniqueOutputKey LONG
upToDatePrice DOUBLE
upToDateYield DOUBLE
weightedAveragePrice DOUBLE
weightedAverageYield DOUBLE
yieldRiseFall DOUBLE
)
partitioned by createTime,
sortColumns=[`securityID, `createTime]
用户可以使用上述代码以创建存储X-Bond成交数据的数据库表,使用"时间维度按天 "的分区规则,使用存储引擎 TSDB ,使用 "债券代码+创建时间" 作为排序列。每个分区内都包含了当天所有 X-Bond 成交数据。
3.4.3 ESP 报价
less
create database "dfs://ESPDepth"
partitioned by VALUE(2023.01.01..2023.12.31)
engine='TSDB'
create table "dfs://ESPDepth"."ESPDepthtable"(
createTime TIMESTAMP
bondCodeVal SYMBOL
marketDepth LONG
marketIndicator LONG
mdBookType LONG
mdSubBookType LONG
messageId LONG
messageSource STRING
msgSeqNum LONG
msgType STRING
askclearingMethod LONG[]
bidclearingMethod LONG[]
askdeliveryType LONG[]
biddeliveryType LONG[]
askinitAccountNumSixCode LONG[]
bidinitAccountNumSixCode LONG[]
asklastPx DOUBLE[]
bidlastPx DOUBLE[]
askmdEntryDate DATE[]
bidmdEntryDate DATE[]
askmdEntrySize LONG[]
bidmdEntrySize LONG[]
askmdEntryTime TIME[]
bidmdEntryTime TIME[]
askmdQuoteType LONG[]
bidmdQuoteType LONG[]
askquoteEntryID LONG[]
bidquoteEntryID LONG[]
asksettlType LONG[]
bidsettlType LONG[]
askyield DOUBLE[]
bidyield DOUBLE[]
ask1initPartyTradeCode STRING
bid1initPartyTradeCode STRING
ask2initPartyTradeCode STRING
bid2initPartyTradeCode STRING
ask3initPartyTradeCode STRING
bid3initPartyTradeCode STRING
ask4initPartyTradeCode STRING
bid4initPartyTradeCode STRING
ask5initPartyTradeCode STRING
bid5initPartyTradeCode STRING
ask6initPartyTradeCode STRING
bid6initPartyTradeCode STRING
ask7initPartyTradeCode STRING
bid7initPartyTradeCode STRING
ask8initPartyTradeCode STRING
bid8initPartyTradeCode STRING
ask9initPartyTradeCode STRING
bid9initPartyTradeCode STRING
ask10initPartyTradeCode STRING
bid10initPartyTradeCode STRING
securityID SYMBOL
securityType STRING
senderCompID STRING
senderSubID STRING
sendingTime TIMESTAMP
symbol STRING
)
partitioned by createTime,
sortColumns=[`securityID, `createTime]
用户可以使用上述代码以创建存储ESP报价数据的数据库表,使用"时间维度按天 "的分区规则,使用存储引擎 TSDB ,使用 "债券代码+创建时间" 作为排序列。每个分区内都包含了当天所有 ESP 报价数据。
3.4.4 ESP 成交
vbnet
create database "dfs://ESPTrade"
partitioned by VALUE(2023.01.01..2023.12.31)
engine='TSDB'
create table "dfs://ESPTrade"."ESPTradetable"(
createTime TIMESTAMP
securityID SYMBOL
execId STRING
execType STRING
lastQty LONG
marketIndicator LONG
messageSource STRING
msgSeqNum LONG
msgType STRING
price DOUBLE
senderCompID STRING
stipulationType STRING
stipulationValue DOUBLE
symbol STRING
tradeDate DATE
tradeMethod LONG
tradeTime TIME
tradeType LONG
transactTime TIMESTAMP
)
partitioned by createTime,
sortColumns=[`securityID, `createTime]
用户可以使用上述代码以创建存储ESP成交数据的数据库表,使用"时间维度按天 "的分区规则,使用存储引擎 TSDB ,使用 "债券代码+创建时间" 作为排序列。每个分区内都包含了当天所有 ESP 成交数据。
3.4.5 QB 报价
sql
create database "dfs://QB_QUOTE"
partitioned by VALUE(2023.10.01..2023.10.31)
engine='TSDB'
create table "dfs://QB_QUOTE"."qbTable"(
SENDINGTIME TIMESTAMP
CONTRIBUTORID SYMBOL
MARKETDATATIME TIMESTAMP
SECURITYID SYMBOL
BONDNAME SYMBOL
DISPLAYLISTEDMARKET SYMBOL
BIDQUOTESTATUS INT
BIDYIELD DOUBLE
BIDPX DOUBLE
BIDPRICETYPE INT
BIDPRICE DOUBLE
BIDDIRTYPRICE DOUBLE
BIDVOLUME INT
BIDPRICEDESC STRING
ASKQUOTESTATUS INT
ASKYIELD DOUBLE
OFFERPX DOUBLE
ASKPRICETYPE INT
ASKPRICE DOUBLE
ASKDIRTYPRICE DOUBLE
ASKVOLUME INT
ASKPRICEDESC STRING
)
partitioned by MARKETDATATIME,
sortColumns=[`SECURITYID,`MARKETDATATIME]
用户可以使用上述代码以创建存储QB报价数据的数据库表,使用"时间维度按天 "的分区规则,使用存储引擎 TSDB ,使用 "债券代码+市场时间" 作为排序列。每个分区内都包含了当天所有 QB 报价数据。
3.4.6 QB 成交
sql
create database "dfs://QB_TRADE"
partitioned by VALUE(2023.10.01..2023.10.31)
engine='TSDB'
create table "dfs://QB_TRADE"."lastTradeTable"(
SECURITYID SYMBOL
BONDNAME SYMBOL
SENDINGTIME TIMESTAMP
CONTRIBUTORID SYMBOL
MARKETDATATIME TIMESTAMP
MODIFYTIME SECOND
DISPLAYLISTEDMARKET SYMBOL
EXECID STRING
DEALSTATUS INT
TRADEMETHOD INT
YIELD DOUBLE
TRADEPX DOUBLE
PRICETYPE INT
TRADEPRICE DOUBLE
DIRTYPRICE DOUBLE
SETTLSPEED STRING
)
partitioned by MARKETDATATIME,
sortColumns=[`SECURITYID,`MARKETDATATIME]
用户可以使用上述代码以创建存储QB交易数据的数据库表,使用"时间维度按天 "的分区规则,使用存储引擎 TSDB ,使用 "债券代码+市场时间" 作为排序列。每个分区内都包含了当天所有 QB 交易数据。
4 因子库窄表存储方案实践
4.1 日频因子库
sql
create database "dfs://dayFactorDB"
partitioned by RANGE(date(datetimeAdd(1980.01M,0..80*12,'M'))), VALUE(`f1`f2),
engine='TSDB'
create table "dfs://dayFactorDB"."dayFactorTB"(
tradetime DATE[comment="时间列", compress="delta"],
securityid SYMBOL,
value DOUBLE,
factorname SYMBOL
)
partitioned by tradetime, factorname,
sortColumns=[`securityid, `tradetime],
keepDuplicates=ALL,
sortKeyMappingFunction=[hashBucket{, 500}]
用户可以使用上述代码以创建存储日频因子库的数据库表,分区规则采用了"时间维度按年 + 因子名 "的组合分区方式存储,使用存储引擎TSDB ,使用 "股票代码+交易时间" 作为排序列。经测试日频因子数据采用这种方式分区存储,综合性能最佳。
对于分区内的分组排序存储来说,DolphinDB 中的 TSDB 存储引擎提供排序键设置,每一个分区的数据写在一个或多个level file中。每一个level file内部的数据按照指定的列进行排序且创建块索引。排序列中除了最后一列的其他列通常为在点查中过滤条件会用到的列,其唯一值组合称为 SortKeys。为保证性能最优,每个分区的 SortKeys 建议不超过1000个。 如 SortKeys 较多可通过设置 sortKeyMappingFunction 对 SortKeys 降维。经测试日频因子数据采用"securityid+tradetime "的方式进行排序,sortKeyMapping 设置为500时,综合性能最佳。
4.2 半小时频因子库
sql
create database "dfs://halfhourFactorDB"
partitioned by RANGE(date(datetimeAdd(1980.01M,0..80*12,'M'))), VALUE(`f1`f2),
engine='TSDB'
create table "dfs://halfhourFactorDB"."halfhourFactorTB"(
tradetime TIMESTAMP[comment="时间列", compress="delta"],
securityid SYMBOL,
value DOUBLE,
factorname SYMBOL,
)
partitioned by tradetime, factorname,
sortColumns=[`securityid, `tradetime],
keepDuplicates=ALL,
sortKeyMappingFunction=[hashBucket{, 500}]
用户可以使用上述代码以创建存储半小时频因子库的数据库表,分区规则采用了"时间维度按年 + 因子名 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "股票代码+交易时间" 作为排序列。经测试半小时频因子数据采用这种方式分区存储,综合性能最佳。
4.3 十分钟频因子库
sql
create database "dfs://tenMinutesFactorDB"
partitioned by VALUE(2023.01M..2023.06M),
VALUE(`f1`f2),
engine='TSDB'
create table "dfs://tenMinutesFactorDB"."tenMinutesFactorTB"(
tradetime TIMESTAMP[comment="时间列", compress="delta"],
securityid SYMBOL,
value DOUBLE,
factorname SYMBOL
)
partitioned by tradetime, factorname,
sortColumns=[`securityid, `tradetime],
keepDuplicates=ALL,
sortKeyMappingFunction=[hashBucket{, 500}]
用户可以使用上述代码以创建存储十分钟频因子库的数据库表,分区规则采用了"时间维度按月 + 因子名 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "股票代码+交易时间" 作为排序列。经测试十分钟频因子数据采用这种方式分区存储,综合性能最佳。
4.4 分钟频因子库
sql
create database "dfs://minuteFactorDB"
partitioned by VALUE(2012.01.01..2021.12.31), VALUE(`f1`f2),
engine='TSDB'
create table "dfs://minuteFactorDB"."minuteFactorTB"(
tradetime TIMESTAMP[comment="时间列", compress="delta"],
securityid SYMBOL,
value DOUBLE,
factorname SYMBOL
)
partitioned by tradetime, factorname,
sortColumns=[`securityid, `tradetime],
keepDuplicates=ALL,
sortKeyMappingFunction=[hashBucket{, 500}]
用户可以使用上述代码以创建存储分钟频因子库的数据库表,分区规则采用了"时间维度按日 + 因子名 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "股票代码+交易时间" 作为排序列。经测试分钟频因子数据采用这种方式分区存储,综合性能最佳。
4.5 秒钟频因子库
less
create database "dfs://secondFactorDB"
partitioned by VALUE(datehour(2022.01.01T00:00:00)..datehour(2022.01.31T00:00:00)), VALUE(`f1`f2),
engine='TSDB'
create table "dfs://secondFactorDB"."secondFactorTB"(
tradetime TIMESTAMP[comment="时间列", compress="delta"],
securityid SYMBOL,
value DOUBLE,
factorname SYMBOL
)
partitioned by tradetime, factorname,
sortColumns=[`securityid, `tradetime],
keepDuplicates=ALL,
sortKeyMappingFunction=[hashBucket{, 500}]
用户可以使用上述代码以创建存储秒钟频因子库的数据库表,分区规则采用了"时间维度按小时 + 因子名 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "股票代码+交易时间" 作为排序列。经测试秒钟频因子数据采用这种方式分区存储,综合性能最佳。
4.6 Level-2 快照频因子库
sql
create database "dfs://level2FactorDB" partitioned by VALUE(2022.01.01..2022.12.31), VALUE(["f1", "f2"]),
engine='TSDB'
create table "dfs://level2FactorDB"."level2FactorTB"(
tradetime TIMESTAMP[comment="时间列", compress="delta"],
securityid SYMBOL,
value DOUBLE,
factorname SYMBOL
)
partitioned by tradetime, factorname,
sortColumns=[`securityid, `tradetime],
keepDuplicates=ALL,
sortKeyMappingFunction=[hashBucket{, 500}]
用户可以使用上述代码以创建存储Level-2快照频因子库的数据库表,分区规则采用了"时间维度按日 + 因子名 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "股票代码+交易时间" 作为排序列。经测试Level-2快照频因子数据采用这种方式分区存储,综合性能最佳。
4.7 逐笔频因子库
sql
create database "dfs://tickFactorDB" partitioned by VALUE(2022.01.01..2022.12.31), VALUE(["f1", "f2"]), HASH([SYMBOL,10])
engine='TSDB'
create table "dfs://tickFactorDB"."tickFactorTB"(
tradetime TIMESTAMP[comment="时间列", compress="delta"],
securityid SYMBOL,
value DOUBLE,
factorname SYMBOL
)
partitioned by tradetime, factorname,securityid,
sortColumns=[`securityid, `tradetime],
keepDuplicates=ALL
用户可以使用上述代码以创建存储逐笔频因子库的数据库表,分区规则采用了"时间维度按日 + 因子名维度 + 股票维度 HASH10 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "股票代码+交易时间" 作为排序列。经测试逐笔频因子数据采用这种方式分区存储,综合性能最佳。与上述因子的存储方案不同的地方在于没有对sortKey进行降维,因为1天数据股票代码维度HASH 10,每个最小分区内是500个股票,不需要再进行降维。
4.8 期货500ms频因子库
sql
create database "dfs://futuresFactorDB"
partitioned by VALUE(2022.01.01..2023.01.01), VALUE(["f1", "f2"]),
engine='TSDB'
create table "dfs://futuresFactorDB"."futuresFactorTB"(
tradetime TIMESTAMP[comment="时间列", compress="delta"],
securityid SYMBOL,
value DOUBLE,
factorname SYMBOL
)
partitioned by tradetime, factorname,
sortColumns=[`securityid, `tradetime],
keepDuplicates=ALL,
sortKeyMappingFunction=[hashBucket{, 500}]
用户可以使用上述代码以创建存储L期货500ms频因子库的数据库表,分区规则采用了"时间维度按日 + 因子名 "的组合分区方式存储,使用存储引擎 TSDB ,使用 "期货代码+交易时间" 作为排序列。经测试期货500ms频因子数据采用这种方式分区存储,综合性能最佳。
5. 结语
针对常见的金融数据,我们通过最佳实践,给出上述建库建表的推荐方案,用户可以根据实际业务场景,进行适当调整和修改。
6. 常见问题与解答(FAQ)
6.1 创建数据库表时报错:已经存在重名库表
执行创建数据库表的代码后,报出如下错误:
csharp
create database partitioned by VALUE(["f1","f2"]), engine=TSDB => It is not allowed to overwrite an existing database.
解决方法: 建议使用其他名字或者删掉该数据库。
删除指定数据库的代码如下:
scss
dropDatabase("dfs://dayFactorDB")
检查指定数据库是否存在的代码如下:
scss
existsDatabase("dfs://dayFactorDB")