在进行行情 tick 数据存储时,哪种数据结构查找起来更快?
小 T 导读:如果我们要做行情 tick 数据的存储,怎样的数据结构查找起来才会比较快?在加入 TDengine 之前,本文作者丁博在弘源泰平量化投资做量化工程师,曾经遇到过这一类存储行情 tick 数据的问题,本文会就此问题进行详细的技术解读。
01
内存存储方案
1
两级 map 方案
using namespace std;int main(){unordered_map<TThostFtdcInstrumentIDType, map<long, CThostFtdcDepthMarketDataField>*> tickData;}
2
map + array
-
交易策略通常会依赖标准化的行情计算交易信号,收行情和标准化并作一步会更节省时间。 -
可以直接用数组下标索引对应时间的行情,查找的时间复杂度为 O(1)。
using namespace std;int main(){unordered_map<TThostFtdcInstrumentIDType, array<CThostFtdcDepthMarketDataField, 28800>> tickData;}
02
持久化存储方案
1
下载 TDengine Database Server
2
安装并启动
sudo dpkg -i TDengine-server-2.4.0.7-Linux-x64.deb
sudo rpm -ivh TDengine-server-2.4.0.7-Linux-x64.rpm
3
建行情表
-
进入 taos 命令行
bo@RDBB:~$ taosWelcome to the TDengine shell from Linux, Client Version:2.4.0.12Copyright (c) 2020 by TAOS Data, Inc. All rights reserved.
-
执行下面的语句
create database marketdata;use marketdata;create stable tick( ts timestamp, updatetime binary(9), updatemillisec int, askprice1 double, bidprice1 double, askvolume1 int, bidvolume1 int) tags (exchangeid binary(9));
-
查看表结构
taos> desc tick;Field | Type | Length | Note |=================================================================================ts | TIMESTAMP | 8 | |updatetime | BINARY | 9 | |updatemillisec | INT | 4 | |askprice1 | DOUBLE | 8 | |bidprice1 | DOUBLE | 8 | |askvolume1 | INT | 4 | |bidvolume1 | INT | 4 | |exchangeid | BINARY | 9 | TAG |Query OK, 8 row(s) in set (0.000378s)
4
写入行情
using namespace std;void insertTickData(TAOS* taos, CThostFtdcDepthMarketDataField &tick) {stringstream sql;// 会自动创建子表tick.InstrumentIDsql << "insert into " << tick.InstrumentID << " using tick tags("<< tick.ExchangeID << ") values(now, '" << tick.UpdateTime << "', "<< tick.UpdateMillisec << "," << tick.AskPrice1 << "," << tick.BidPrice1<< "," << tick.AskVolume1 << "," << tick.BidVolume1 << ")";TAOS_RES *res = taos_query(taos, sql.str().c_str());if (res == nullptr || taos_errno(res) != 0) {cerr << "insertTitckData failed," << taos_errno(res) << ", " << taos_errstr(res) << endl;}}int main(){TAOS *taos = taos_connect("localhost", "root", "taosdata", "marketdata", 6030);// 构造测试数据CThostFtdcDepthMarketDataField tick;strcpy_s(tick.InstrumentID, "IH2209");strcpy_s(tick.UpdateTime, "14:10:32");strcpy_s(tick.ExchangeID, "DEC");tick.UpdateMillisec = 500;tick.AskPrice1 = 123.8;tick.BidPrice1 = 123.4;tick.AskVolume1 = 10;tick.BidVolume1 = 9;// 写入测试数据insertTickData(taos, tick);taos_close(taos);}
5
查询最新的行情
using namespace std;CThostFtdcDepthMarketDataField* getLastTick(TAOS* taos, const char* instrumentID) {string sql("select last(*) from ");sql += instrumentID;TAOS_RES* res = taos_query(taos, sql.c_str());if (res == nullptr || taos_errno(res) != 0) {cerr << "getLastTick failed," << taos_errno(res) << ", " << taos_errstr(res) << endl;return nullptr;}TAOS_ROW row = taos_fetch_row(res);if (row == nullptr) {return nullptr;}CThostFtdcDepthMarketDataField* tick = new CThostFtdcDepthMarketDataField();//int64_t ts = *((int64_t*)row[0]);memcpy(tick->UpdateTime, row[1], 9);tick->UpdateMillisec = *(int*)row[2];tick->AskPrice1 = *((double *)row[3]);tick->BidPrice1 = *((double*)row[4]);taos_free_result(res);return tick;}int main() {TAOS* taos = taos_connect("localhost", "root", "taosdata", "marketdata", 6030);CThostFtdcDepthMarketDataField* tick = getLastTick(taos, "IH2209");cout << "askPrice1=" << tick->AskPrice1 << " bidPrice1=" << tick->BidPrice1 << endl;delete tick;taos_close(taos);}
03
从实际业务出发的实践经验分享
依赖多,稳定性较差:PMS作为多品种的投后分析服务, 需要使用到各种日线数据、当天实时行情数据、当天分钟数据等,在数据获取方面需要依赖Http以及Postgres、LevelDB等数据库。过于多的数据获取链路会导致平台可靠性降低,同时依赖于其他各个服务,导致查询问题过于复杂。
-
性能不能满足需求:PMS作为多品种投后分析,在算法分析层面需要大量的行情获取,而且对行情获取的性能也有较大的要求,当前所有行情会占据大量分析的性能。
本文分享自微信公众号 - TDengine(taosdata_news)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
