第一章:数据获取与预处理——从交易所/数据商拿到Level-1/Level-2行情
做量化交易,第一道坎就是数据。我见过太多人策略写得漂亮,结果一跑实盘就崩——原因往往是数据源没处理好。今天咱们就聊聊,怎么从交易所和数据商手里,把Level-1和Level-2行情数据拿回来,再洗干净、对齐好。
1.1 Level-1 vs Level-2:到底差在哪?
先搞清楚这两个概念。Level-1行情,说白了就是咱们平时在炒股软件上看到的那一套:开盘价、收盘价、最高价、最低价、成交量、成交额,外加三档买卖盘口。数据频率通常是3秒或5秒快照一次。
Level-2行情就细多了。它提供十档甚至更深的买卖盘口,还有逐笔成交数据。我2018年做高频策略时,发现Level-1的数据根本不够用——你想捕捉盘口瞬间的挂单变化,3秒一次的快照早就把关键信息丢了。
核心区别一句话:Level-1是“结果”,Level-2是“过程”。做高频、做盘口分析,必须上Level-2。
1.2 数据源怎么选?
国内主流的数据源,我列个表给你看:
| 数据源 | Level-1 | Level-2 | 费用 | 我的评价 |
|---|---|---|---|---|
| 万得(Wind) | ✓ | ✓ | 高 | 机构首选,稳定但贵 |
| 聚宽(JoinQuant) | ✓ | ✓ | 中 | 回测方便,实盘需注意 |
| 通联数据 | ✓ | ✓ | 中 | API友好,文档清晰 |
| 交易所直连 | ✓ | ✓ | 极高 | 延迟最低,但门槛高 |
我个人习惯是:回测用聚宽或通联,实盘用交易所直连。为什么?回测时数据量巨大,聚宽的历史数据全,省心。实盘时延迟是关键,直连能省掉中间商那几毫秒。
1.3 数据获取实战:用Python拉取Level-2行情
拿通联数据举个例子。他们提供Python SDK,用起来挺顺手。下面这段代码,是我在项目中实际用过的:
import tushare as ts
import pandas as pd
from datetime import datetime
# 设置token(去通联官网申请)
ts.set_token('your_token_here')
pro = ts.pro_api()
# 获取某只股票的Level-2行情
# 注意:Level-2数据需要额外权限
df = pro.stk_factor_pro(
ts_code='000001.SZ',
start_date='20240101',
end_date='20240131',
fields='ts_code,trade_date,open,high,low,close,vol,amount'
)
print(df.head())
小提示:Level-2的逐笔数据通常按天存储,一天一个文件。我建议你写个定时任务,每天收盘后自动拉取,避免盘中请求被限流。
1.4 数据清洗:缺失值、异常值、复权处理
数据拿到手,别急着用。我踩过的坑太多了——有一次策略回测收益高得离谱,查了半天才发现是某天数据缺失,系统自动用前值填充了,导致信号全错。
1.4.1 缺失值处理
行情数据缺失,常见原因有:停牌、节假日、网络中断。我的处理原则是:
- 停牌日:直接删除该行,不要填充。因为停牌期间没有真实交易,填充会扭曲指标。
- 盘中缺失:用前一个有效值填充,但仅限于短时间(比如1分钟内)。
- 连续缺失:标记为异常,人工核查。
# 缺失值处理示例
df = df.dropna(subset=['close']) # 删除收盘价缺失的行
df = df.fillna(method='ffill', limit=3) # 前向填充,最多3个
1.4.2 异常值检测
什么是异常值?比如某只股票突然涨了1000%,或者成交量比前一天翻了100倍。这些多半是数据错误。
我常用的方法是:用滚动窗口计算均值和标准差,超过3倍标准差就标记为异常。
# 异常值检测:3-sigma法则
mean = df['close'].rolling(20).mean()
std = df['close'].rolling(20).std()
df['is_outlier'] = (abs(df['close'] - mean) > 3 * std)
注意:别一发现异常就删除。我曾经删掉了一个“异常”数据,后来发现那是真实发生的乌龙指事件——那天的行情恰恰是策略需要捕捉的极端情况。
1.4.3 复权处理
复权是个大坑。很多新手直接用不复权的价格做回测,结果分红送股后,价格突然跳空,策略信号全乱套。
我的建议是:回测用后复权,实盘用前复权。为什么?后复权能真实反映历史收益,前复权方便当前价格对比。
# 聚宽平台复权示例
from jqdatasdk import *
auth('account', 'password')
# 获取后复权数据
df = get_price('000001.XSHE',
start_date='2024-01-01',
end_date='2024-01-31',
frequency='daily',
fq='post') # 'post'为后复权,'pre'为前复权
1.5 时间对齐与重采样
不同数据源的时间戳格式可能不一样。有的用Unix时间戳,有的用字符串。更麻烦的是,Level-1和Level-2的采样频率不同——Level-1是3秒一次,Level-2是逐笔。
做流动性指标时,必须把数据对齐到统一的时间轴上。我一般用1分钟或5分钟作为基准。
# 时间对齐:将逐笔数据重采样到1分钟
df['time'] = pd.to_datetime(df['time'], unit='s')
df.set_index('time', inplace=True)
# 重采样:计算每分钟的OHLC
resampled = df.resample('1T').agg({
'price': 'ohlc',
'volume': 'sum'
})
核心原则:时间对齐时,用“左闭右开”原则。比如1分钟K线,包含00:00到00:59的数据,不包含01:00的数据。这样能避免数据重复或遗漏。
1.6 本章知识体系总览
下面这张图,把数据获取与预处理的整个流程串起来了。你可以把它当作操作手册:
1.7 避坑指南
最后,分享几个我踩过的坑:
- 时间戳时区问题:交易所数据是北京时间,但有些数据商存的是UTC。我曾经没做时区转换,导致策略在开盘前就发出了交易信号——嗯,那笔单子自然没成交。
- 复权因子更新:公司分红后,复权因子会变。如果你用历史复权数据做回测,记得定期更新复权因子,否则回测结果会漂移。
- Level-2数据量:一只股票一天的逐笔数据可能有几十万条。如果你做全市场回测,硬盘空间和内存都要提前规划好。
我的习惯:每次拉取数据后,先做一次简单的质量检查——比如看收盘价是否在合理范围内,成交量是否为正。花5分钟检查,能省下后面几小时的调试时间。
数据预处理这一步,看似枯燥,但它是整个量化策略的基石。数据干净了,后面的流动性指标计算才能靠谱。下一章,咱们就聊聊怎么从这些干净数据里,提取出有意义的流动性指标。
公众号:蓝海资料掘金营,微信deep3321