数据准备:可转债行情数据获取、财务数据清洗、因子计算数据对齐
做量化,数据就是命根子。这话我常跟团队里的人讲。
可转债的因子组合优化,听起来高大上,但说白了,你因子算得再漂亮,数据源是脏的,结果就是垃圾进垃圾出。我见过太多人花90%的时间调模型,却不肯花10%的时间检查数据——嗯,最后模型跑出来的结果,连自己都不敢信。
这一章,我们就来聊聊数据准备那点事。说白了就三步:拿到行情数据、洗干净财务数据、把因子对齐到同一个时间轴上。
1. 可转债行情数据获取
行情数据,我习惯从两个渠道拿:一是聚宽、米筐这类量化平台,二是直接爬交易所的公开数据。个人建议,初期用聚宽就够了,省心。
你想想看,可转债的行情数据和股票不太一样。股票就一个价格,可转债有转股价值、溢价率、纯债价值这些衍生数据。所以获取的时候,字段要拿全。
核心字段清单:
- 代码、名称、交易日期
- 开盘价、收盘价、最高价、最低价
- 成交量、成交额
- 转股价值、转股溢价率
- 纯债价值、到期收益率
- 剩余规模、剩余年限
我曾经踩过一个坑:只拿了收盘价,没拿转股价值。结果算出来的因子全是价格相关的,完全忽略了债性。后来回测一塌糊涂,才意识到问题出在哪。
# 示例:使用聚宽获取可转债行情
from jqdatasdk import *
auth('你的账号', '你的密码')
# 获取所有可转债列表
bonds = get_all_securities(['bond_conv'])
# 获取某只转债的历史行情
df = get_price('123456.XSHE',
start_date='2023-01-01',
end_date='2023-12-31',
fields=['open', 'close', 'volume', 'money',
'conv_value', 'premium_ratio'])
print(df.head())
小技巧:行情数据最好按日频获取,不要偷懒用周频。因子计算时,日频数据能让你捕捉到更多细节。我一般会在每天收盘后跑一次数据更新脚本。
2. 财务数据清洗
财务数据,才是真正让人头疼的地方。
为什么?因为上市公司财报的发布时间、格式、口径都不一样。有的公司一季度报拖到4月底才发,有的提前一个月就发了。你想想看,如果不对齐时间,因子计算就会用到未来数据——这是量化里的大忌。
我个人习惯的清洗流程是这样的:
- 去重:同一家公司同一天发布多份财报?只保留最新的一份。
- 填充缺失值:有些财务指标可能为空,比如某季度没有研发费用。我一般用前值填充,或者用行业均值替代。
- 异常值处理:净利润突然暴涨100倍?大概率是数据错误。我会用3σ原则剔除。
- 时间对齐:把财报发布日期和报告期对应起来。比如2023年一季报,虽然可能在4月发布,但数据反映的是2023年3月底的情况。
注意:千万不要直接用财报的发布日期作为因子计算日期。我曾经犯过这个错,结果回测收益高得离谱——后来发现是因为用到了未来信息。正确的做法是:用报告期对应的截止日期,或者用财报发布后第一个交易日。
# 示例:财务数据清洗
import pandas as pd
import numpy as np
# 假设df是原始财务数据
df = pd.read_csv('financial_data.csv')
# 去重:按股票代码和报告期去重,保留最新记录
df = df.sort_values('report_date').drop_duplicates(
subset=['code', 'report_period'], keep='last')
# 填充缺失值:用前值填充
df = df.fillna(method='ffill')
# 异常值处理:3σ原则
for col in ['net_profit', 'revenue']:
mean = df[col].mean()
std = df[col].std()
df = df[(df[col] > mean - 3*std) & (df[col] < mean + 3*std)]
print(f"清洗后数据量: {len(df)}")
3. 因子计算数据对齐
这一步,说白了就是把行情数据和财务数据拼到一张表上。
但这里有个坑:行情数据是日频的,财务数据是季频的。怎么对齐?
我的做法是:
- 以行情数据的日期为基准
- 对于每个交易日,取最近一期已发布的财务数据
- 注意:财务数据只有在发布之后才能使用,不能提前用
举个例子:今天是2023年5月15日,某公司2023年一季报在4月28日发布。那么5月15日这天,我们可以用一季报的数据。但如果今天是4月20日,一季报还没发,那就只能用2022年年报的数据。
对齐后的数据结构示例:
| 交易日期 | 转债代码 | 收盘价 | 转股价值 | 净利润(最近一期) | 营收(最近一期) | 因子A | 因子B |
|---|---|---|---|---|---|---|---|
| 2023-05-15 | 123456 | 128.50 | 115.20 | 1.2亿 | 5.6亿 | 0.85 | 0.32 |
| 2023-05-16 | 123456 | 129.00 | 115.80 | 1.2亿 | 5.6亿 | 0.86 | 0.33 |
# 示例:数据对齐
def align_data(market_data, financial_data):
"""
market_data: 行情数据,日频
financial_data: 财务数据,季频,包含报告期和发布日期
"""
# 对每个交易日,找到最近一期已发布的财务数据
aligned = []
for date in market_data['date'].unique():
# 筛选出在该日期之前已发布的财务数据
available = financial_data[
financial_data['publish_date'] <= date
]
# 取最近一期
latest = available.sort_values('report_date').groupby('code').last()
# 合并
day_data = market_data[market_data['date'] == date]
merged = day_data.merge(latest, on='code', how='left')
aligned.append(merged)
return pd.concat(aligned)
# 运行对齐
result = align_data(market_df, financial_df)
print(result.head())
避坑指南:我曾经在数据对齐时,直接用财务数据的报告期去匹配行情数据,结果回测收益高得离谱。后来发现,有些公司财报发布得晚,但报告期早就过了。正确的做法是用发布日期去匹配,而不是报告期。这一点,你千万要记住。
本章知识体系
下面这张图,是我自己总结的数据准备流程。你看一眼,心里就有数了。
数据准备这一步,看起来繁琐,但做扎实了,后面的因子计算和模型训练就顺风顺水。我见过太多人急着跑模型,结果数据没对齐,白白浪费了几天时间。
嗯,这一章就到这里。记住:数据质量决定模型上限。你花在数据上的每一分钟,都会在回测结果里体现出来。