交易记录数据结构:从原始数据到可用信号
做量化交易这些年,我处理过的交易记录少说也有几亿条。说实话,很多人一上来就急着找Alpha信号,却忽略了最基础的东西——你连数据都没搞清楚,怎么可能挖出靠谱的信号?
这一章,我们就来聊聊交易记录的数据结构。说白了,就是搞清楚你的数据长什么样,怎么把它收拾干净,让它能为你所用。
一、常见字段:交易记录的“五官”
一条完整的交易记录,通常包含以下几个核心字段。我习惯把它们称为“五官”——少了哪一个,你都看不清市场的全貌。
| 字段名 | 类型 | 说明 | 典型示例 |
|---|---|---|---|
| 时间戳 | datetime | 交易发生的精确时间 | 2024-01-15 09:30:00.123 |
| 价格 | float | 成交价格 | 15.68 |
| 成交量 | int/float | 成交数量 | 1000 |
| 买卖方向 | str/int | B/S 或 1/-1 | B 或 1 |
| 成交金额 | float | 价格 × 成交量 | 15680.0 |
| 交易代码 | str | 股票/合约代码 | 600519.SH |
嗯,这里要注意一点。很多新手拿到数据,看到“时间戳”就以为万事大吉了。其实不然。我在项目中遇到过好几次,同一个交易所的数据,时间精度居然不一样——有的精确到毫秒,有的只到秒。你想想看,高频交易里差个几毫秒,信号就完全变味了。
二、数据清洗与预处理:别让脏数据毁了你的策略
数据清洗这事儿,我怎么说都不为过。曾经有一次,我跑了一个看起来完美的回测,收益曲线漂亮得不行。结果一查,原来是某天的数据里多了一个零——成交量被放大了10倍。嗯,从那以后,我每次拿到数据都要先过一遍清洗流程。
2.1 常见的数据“脏”问题
- 缺失值:某段时间没有交易记录,或者字段为空
- 异常值:价格突然跳空、成交量暴增(可能是数据录入错误)
- 重复记录:同一条交易被记录了两次
- 时间错乱:时间戳顺序颠倒,或者出现未来时间
- 方向标识不一致:有的用B/S,有的用1/0,有的用Buy/Sell
2.2 清洗流程(我常用的套路)
import pandas as pd
import numpy as np
# 1. 加载数据
df = pd.read_csv('trade_records.csv')
# 2. 统一时间格式
df['time'] = pd.to_datetime(df['time'], format='%Y-%m-%d %H:%M:%S.%f')
# 3. 排序并去重
df = df.sort_values('time').drop_duplicates(subset=['time', 'price', 'volume'])
# 4. 处理缺失值
df = df.dropna(subset=['price', 'volume'])
# 5. 统一买卖方向
direction_map = {'B': 1, 'S': -1, 'Buy': 1, 'Sell': -1, '1': 1, '0': -1}
df['direction'] = df['direction'].map(direction_map)
# 6. 过滤异常值(3倍标准差法)
price_mean = df['price'].mean()
price_std = df['price'].std()
df = df[(df['price'] > price_mean - 3*price_std) &
(df['price'] < price_mean + 3*price_std)]
print(f"清洗后剩余记录数: {len(df)}")
你看,代码其实不复杂。但每一步都有它的道理。比如去重这一步,我见过有人直接drop_duplicates()一把梭,结果把不同时间点的正常交易也给删了。所以一定要指定subset,只针对真正重复的字段做判断。
三、时间序列对齐:让不同频率的数据“对上话”
做量化交易,你很少只用单一频率的数据。你可能同时有1分钟K线、逐笔交易、还有日线数据。这些数据的时间戳不一样,怎么对齐?
说白了,时间序列对齐就是让不同数据源在同一个时间轴上“站好队”。我习惯用重采样(resample)和向前填充(forward fill)来处理。
3.1 对齐的两种场景
- 高频→低频:比如把逐笔交易聚合成1分钟K线
- 低频→高频:比如把日线数据填充到分钟级别(注意:这会有信息损失)
3.2 实战代码:逐笔交易对齐到1分钟K线
# 假设df是逐笔交易数据,包含time, price, volume, direction
df['time'] = pd.to_datetime(df['time'])
df = df.set_index('time')
# 重采样到1分钟
minute_data = df.resample('1min').agg({
'price': 'ohlc', # 生成开盘、最高、最低、收盘
'volume': 'sum', # 成交量求和
'direction': lambda x: (x == 1).sum() - (x == -1).sum() # 净买卖量
})
# 重命名列
minute_data.columns = ['open', 'high', 'low', 'close', 'volume', 'net_buy']
# 填充缺失的分钟(比如没有交易的分钟)
minute_data = minute_data.fillna(method='ffill')
print(minute_data.head())
四、知识体系总览
下面这张图,是我自己整理的数据处理流程。每次拿到新数据,我都会按这个框架走一遍。你可以把它当作一个检查清单。
这张图其实就概括了本章的核心。从原始数据到可用信号,中间必须经过字段解析、数据清洗、时间对齐这三步。少一步,你的Alpha信号就可能建立在沙子上。
五、一些碎碎念
做数据处理,其实没什么高深的技巧。更多的是耐心和细心。我见过太多人,花了几周时间写策略,结果数据预处理只用了半天。嗯,结果可想而知——回测漂亮,实盘拉胯。
我个人习惯是,拿到任何新数据,先花70%的时间做清洗和对齐,剩下30%才用来挖信号。你可能会觉得这太慢了。但相信我,数据干净了,信号自然就浮出来了。
最后说一句:别怕脏数据。每个脏数据背后,都可能藏着一个别人没发现的Alpha。关键是你得先把它洗干净。