21天驯化AI打工仔 - 如何存储10亿个Symbol?
现在,我们需要设计一种通用的数据交换格式(Standard Quotes Exchange Protocol, SQEP)。这种格式的工作原理是:由数据生产者(因为只有生产者才了解原始数据的具体格式)将数据转换为这种标准格式,然后再将其推送到Redis中供消费者使用。
前言¶
第一天,我们讨论了如何从Tushare获取OHLC(开盘价、最高价、最低价、收盘价)数据和调整因子(adj_factor)。当时我们存储的数据结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
现在,我们需要设计一种通用的数据交换格式(Standard Quotes Exchange Protocol, SQEP)。这种格式的工作原理是:由数据生产者(因为只有生产者才了解原始数据的具体格式)将数据转换为这种标准格式,然后再将其推送到Redis中供消费者使用。
1. SQEP-BAR-DAY 日线场景下的数据交换格式¶
SQEP-BAR-DAY 是标准行情交换协议(Standard Quotes Exchange Protocol)中用于日线数据的格式规范。该格式设计用于在不同系统组件间高效传输和处理股票日线数据,确保数据的一致性和互操作性。
1.1. 字段定义¶
SQEP-BAR-DAY 包含以下标准字段:
字段名 | 数据类型 | 说明 |
---|---|---|
symbol | str/int | 股票代码。推荐使用整型编码以提高性能 |
frame | datetime.date | 交易日期 |
open | float64 | 开盘价 |
high | float64 | 最高价 |
low | float64 | 最低价 |
close | float64 | 收盘价 |
vol | float64 | 成交量 |
amount | float64 | 成交额 |
adjust | float64 | 复权因子 |
st | bool | 是否为ST股票(可选扩展字段) |
buy_limit | float64 | 涨停价(可选扩展字段) |
sell_limit | float64 | 跌停价(可选扩展字段) |
1.2. 编码约定¶
-
字段命名:使用
frame
而非date
或timestamp
,因为后两者在某些数据库中不适合作为列名。 -
股票代码编码:为提高查询性能,推荐将字符串格式的股票代码转换为整型:
- 上海证券交易所:000001.SH → 1000001
- 深圳证券交易所:000001.SZ → 2000001
这种编码方式最多可支持9个不同交易所(数字1-9,0不能用作前缀)。
1.3. 使用场景¶
SQEP-BAR-DAY 主要应用于:
- 数据生产者(如Tushare、QMT等数据源)将原始数据转换为标准格式
- 通过Redis等中间件在系统组件间传输
- 数据消费者(如分析引擎、回测系统)处理标准格式数据
- 存储到ClickHouse等时序数据库中进行长期保存
1.4. 007 的代码实现¶
既然规定好了日线场景下的数据交换格式,就可以让 007 设计代码实现了。
007 为我们提供了两个代码文件(sqep_bar_day_producer.py
和sqep_bar_day_consumer.py
),简单修改后可以正常运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
|
1.5. 股票代码编码方式对查询的性能测试¶
接下来,我们将设计一个实验来测试股票代码编码方式对查询性能的影响。这个实验将比较字符串格式和整型编码格式在不同数据量下的查询性能差异。007 很 nice 地帮助我设计了一个实验方案:
1.5.1. 测试方案¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
|
- 测试环境
- 使用SQLite数据库作为测试平台(易于部署且无需额外配置)
- 创建两个表:
bar_day_str
(字符串格式)和bar_day_int
(整型编码) - 为两个表的
symbol
字段创建索引,确保公平比较
- 测试数据
- 生成不同数量的股票代码(上交所和深交所各半)
- 为每只股票生成多天的交易数据
- 数据量从小到大逐步增加(100到5000只股票)
- 测试场景
- 单条查询:根据特定股票代码查询所有交易记录
- 范围查询:查询特定交易所的所有股票记录
- 性能指标
- 查询响应时间(毫秒)
- 性能提升比例(字符串格式时间/整型编码时间)
- 结果分析
- 绘制四个图表展示测试结果:
- 单条查询性能对比
- 范围查询性能对比
- 整型编码性能提升比例
- 查询时间与数据量关系(对数坐标)
1.5.2. 测试结果¶
运行 007 的测试方案,可以得到如下的股票代码编码方式性能测试结果。
1.5.2.1. 测试数据概览¶
数据量 | 股票数量 | 每只股票天数 | 总记录数 |
---|---|---|---|
小 | 100 | 252 | 25,200 |
中小 | 500 | 252 | 126,000 |
中 | 1,000 | 252 | 252,000 |
中大 | 2,000 | 252 | 504,000 |
大 | 5,000 | 252 | 1,260,000 |
1.5.2.2. 查询性能对比¶
数据量(记录数) | 单条查询时间(ms) | 性能提升 | 范围查询时间(ms) | 性能提升 | ||
---|---|---|---|---|---|---|
字符串格式 | 整型编码 | 倍数 | 字符串格式 | 整型编码 | 倍数 | |
25,200 | 0.14 | 0.13 | 1.04x | 7.68 | 0.42 | 18.34x |
126,000 | 0.14 | 0.13 | 1.05x | 37.34 | 2.30 | 16.24x |
252,000 | 0.14 | 0.14 | 1.03x | 76.20 | 4.95 | 15.39x |
504,000 | 0.16 | 0.19 | 0.83x | 148.13 | 10.05 | 14.73x |
1,260,000 | 0.19 | 0.28 | 0.70x | 377.34 | 24.71 | 15.27x |
平均 | - | - | 0.93x | - | - | 15.99x |
1.5.2.3. 结果分析¶
- 单条查询性能:
- 在小到中等数据量(25,200-252,000条)下,整型编码略优于字符串格式(1.03x-1.05x)
- 在较大数据量(504,000-1,260,000条)下,字符串格式反而略优于整型编码(0.70x-0.83x)
-
整体来看,单条查询的性能差异不显著,平均提升为0.93倍
-
范围查询性能:
- 在所有数据量级下,整型编码都显著优于字符串格式
- 性能提升倍数在 14.73x-18.34x 之间
-
平均性能提升达到 15.99 倍
-
随数据量增长的趋势:
- 单条查询:随着数据量增加,整型编码的相对优势逐渐减弱
- 范围查询:整型编码的巨大优势在各数据量级下保持稳定
1.5.2.4. 结论¶
- 对于单条查询:两种编码方式性能相近,在实际应用中差异不明显
- 对于范围查询:整型编码提供了显著的性能优势,平均快约16倍
- 推荐使用场景:
- 如果系统中范围查询较为频繁(如按交易所筛选股票),强烈推荐使用整型编码
-
如果系统主要进行单条查询,编码方式的选择影响不大,可以根据其他因素决定
-
其他考虑因素:
- 整型编码节省存储空间
- 整型编码便于进行数值运算和比较
- 字符串格式更直观,调试时更容易理解
2. SQEP-BAR-MINITE 分钟线场景下的数据交换格式¶
同上,但没有复权因子。这样,无论将来我们从哪个数据源获得的数据,消费者一端的代码都不需要更改。这里,我和 007 将设计一个性能测试方案,比较JSON(带key)和CSV(不带key)两种数据交换格式的性能差异。
2.1. 测试方案¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 |
|
2.2. 测试结果¶
数据量(记录数) | JSON编码时间(ms) | CSV编码时间(ms) | JSON解码时间(ms) | CSV解码时间(ms) | JSON大小(KB) | CSV大小(KB) | 编码比率(JSON/CSV) | 解码比率(JSON/CSV) | 大小比率(JSON/CSV) |
---|---|---|---|---|---|---|---|---|---|
100 | 0.13 | 0.14 | 0.07 | 0.09 | 16.16 | 7.12 | 0.96x | 0.71x | 2.27x |
500 | 0.47 | 0.51 | 0.24 | 0.34 | 58.67 | 25.82 | 0.93x | 0.69x | 2.27x |
1,000 | 1.42 | 1.55 | 0.75 | 0.94 | 160.47 | 70.67 | 0.91x | 0.81x | 2.27x |
5,000 | 6.86 | 7.06 | 3.35 | 4.80 | 802.26 | 353.26 | 0.97x | 0.70x | 2.27x |
10,000 | 13.23 | 13.94 | 6.69 | 9.67 | 1,602.63 | 705.58 | 0.95x | 0.69x | 2.27x |
50,000 | 66.37 | 70.23 | 34.55 | 48.12 | 8,009.18 | 3,526.28 | 0.95x | 0.72x | 2.27x |
平均 | - | - | - | - | - | - | 0.95x | 0.72x | 2.27x |
2.2.3. 结果分析¶
- 编码性能 JSON编码在所有测试数据量下都略快于CSV编码,平均快约5%(0.95x)
- 解码性能 JSON解码明显快于CSV解码,平均快约28%(0.72x)
- 数据大小 JSON格式的数据大小始终是CSV格式的2.27倍
总结¶
在这一章的探索中,我和我的AI助手007一起深入研究了量化交易系统中数据交换格式的性能问题。这次"数据格式大PK"不仅让我们获得了宝贵的技术数据,更展示了人机协作的无限可能!
007不愧是"码力全开"的得力助手,它不仅设计了全面的测试方案,还在遇到除零错误时迅速提供了解决方案。通过我们的共同努力,成功对比了JSON和CSV两种格式在不同数据量下的表现:JSON在处理速度上略胜一筹,而CSV在存储效率上更具优势。
这次测试不仅是技术上的突破,更是我们21天驯化AI打工仔挑战的又一个里程碑!正如测试数据一样,我们的合作也在不断扩展规模,从100条记录到50,000条记录,效率始终保持稳定,这正是我们合作的真实写照!
正如007所说:"数据是一切开始的基础",而我们的合作则是创新的源泉。期待在接下来的SQEP扩展格式探索中,继续与007携手并进,为量化交易系统注入更多智慧的火花!
下一步:SQEP扩展格式探索之旅 接下来,我和007将继续我们的冒险,探索SQEP的两个重要扩展格式: 1. SQEP-ST:专为特殊处理(ST)股票设计的数据格式 - 这些特殊股票信息虽然稀疏,但对投资决策至关重要 - 我们将巧妙地将ST信息整合到现有的SQEP-BAR-DAY表中 - 通过布尔型st字段,让系统能够快速识别特殊股票 2. 涨跌停信息:交易限制的关键指标 - 添加buy_limit和sell_limit字段,为回测系统提供精确的交易约束 - 这些信息将帮助我们模拟真实市场中的交易规则 - 确保回测结果更加贴近实际交易环境
有了这些扩展,我们的量化交易系统将更加完善,能够应对更复杂的市场情况。正如007所展示的那样,只要思路清晰、方法得当,即使是复杂的数据处理问题也能迎刃而解!让我们继续这场激动人心的21天挑战,用数据和智慧创造更多可能!