3、数据结构概览:DataFrame基础、时间序列索引、多合约数据合并
好,咱们进入第三讲。说实话,很多做套利的新手,一开始就栽在数据上。策略逻辑想得挺美,一跑代码发现数据对不上、索引乱套、合约合并后全是空值。嗯,这节我就带你把这些坑填平。
3.1 DataFrame基础:你手里的“电子表格”
我个人习惯把DataFrame想象成一个超级智能的Excel表格。它有行、有列,但比Excel灵活得多。在套利分析中,每一行通常是一个时间点,每一列是某个合约的字段——比如收盘价、持仓量、成交量。
创建一个最简单的DataFrame,代码长这样:
import pandas as pd
data = {
'RB01': [3800, 3810, 3820],
'RB05': [3850, 3860, 3870],
'时间': ['2024-01-01', '2024-01-02', '2024-01-03']
}
df = pd.DataFrame(data)
print(df)
输出结果:
RB01 RB05 时间
0 3800 3850 2024-01-01
1 3810 3860 2024-01-02
2 3820 3870 2024-01-03
你看,这就是最原始的样子。但注意——这里的索引是默认的0、1、2,不是时间。这在套利里是致命的。为什么?因为你要对齐两个合约的同一时刻数据,没有时间索引,你根本不知道哪行跟哪行对应。
3.2 时间序列索引:让数据“说话”
我刚开始做套利时,犯过一个低级错误。我把两个合约的数据直接按行合并,以为第一行对第一行就行。结果一个合约有夜盘,另一个没有,数据全错位了。那次回测结果漂亮得离谱,实盘直接亏钱。嗯,从那以后,我养成了一个习惯——拿到数据第一件事,就是把时间列设为索引。
设置时间索引的代码:
df['时间'] = pd.to_datetime(df['时间'])
df.set_index('时间', inplace=True)
print(df)
输出:
RB01 RB05
时间
2024-01-01 3800 3850
2024-01-02 3810 3860
2024-01-03 3820 3870
现在,索引是时间了。你可以用 df.loc['2024-01-02'] 直接取某天的数据。这在回测中非常方便。
pd.to_datetime(df['时间']).dt.tz_localize('Asia/Shanghai')。我曾经因为时区问题,把夜盘数据对到了第二天白天,折腾了两天才发现。
3.3 多合约数据合并:套利的核心操作
套利嘛,就是同时盯着两个或多个合约。数据必须合并到一起。常用的合并方式有两种:横向合并和纵向合并。
3.3.1 横向合并:把合约“并排”放
比如你有RB01和RB05两个合约的日线数据,想放在一个DataFrame里,按时间对齐。用 pd.merge 或者 pd.concat 都可以。
# 假设 df1 是RB01数据,df2 是RB05数据
df_merged = pd.merge(df1, df2, left_index=True, right_index=True, suffixes=('_RB01', '_RB05'))
print(df_merged.head())
这里的关键是 left_index=True, right_index=True,意思是用索引(时间)来对齐。我见过有人用 on='时间' 但没设索引,结果两个DataFrame的时间列名不一样,合并出来全是NaN。嗯,细节决定成败。
3.3.2 纵向合并:把数据“摞”起来
有时候你需要把不同时间段的数据拼起来。比如你下载了2023年的数据和2024年的数据,想合成一个完整序列。用 pd.concat:
df_full = pd.concat([df_2023, df_2024], axis=0)
df_full.sort_index(inplace=True) # 按时间排序
注意,纵向合并后索引可能乱序,一定要 sort_index。我曾经忘了这一步,回测时数据跳来跳去,策略信号完全错乱。
3.4 知识体系结构图
下面这张图,是我自己总结的数据处理流程。你跟着这个思路走,基本不会乱。
3.5 实战中的避坑指南
讲几个我踩过的坑,你记一下:
- 索引重复:同一个时间点出现两行数据。比如你从两个数据源各取了一份数据,合并时没去重。用
df.index.duplicated()检查,再用df[~df.index.duplicated(keep='first')]去重。 - 缺失值处理:合并后某些时间点只有一个合约有数据。比如RB01有夜盘,RB05没有。这时候用
df.fillna(method='ffill')向前填充,或者直接删除缺失行。我个人建议用前向填充,因为套利策略通常需要连续数据。 - 数据类型:价格列必须是浮点数。我遇到过从数据库读出来是字符串的情况,一算价差全是NaN。用
df['price'] = pd.to_numeric(df['price'], errors='coerce')强制转换。
3.6 小结
这一节的内容,说白了就是三件事:把数据装进DataFrame、把时间设成索引、把多个合约合并到一起。你把这三点练熟了,后面讲价差计算、协整检验、回测框架,你才能跟得上。
记住,数据清洗不是最炫酷的部分,但它是最重要的部分。没有干净的数据,再牛逼的策略也是白搭。
公众号:蓝海资料掘金营,微信deep3321