数据获取与预处理:REITs数据来源与清洗实战
做REITs量化投资,第一步就是搞数据。这活儿看着简单,其实坑特别多。我刚开始做REITs因子研究时,就因为在数据清洗上偷了懒,结果回测曲线漂亮得不行,实盘一跑直接翻车。嗯,从那以后我再也不敢小看数据预处理了。
数据来源:Wind vs Bloomberg
国内做REITs研究,数据源基本就两个选择:Wind和Bloomberg。我个人习惯用Wind做主力,因为覆盖的A股REITs更全,更新也及时。Bloomberg呢,适合做全球REITs对比分析时用。
说几个实际体验:
- Wind:API接口方便,用Python的
wset函数就能拉数据。但要注意,有些REITs的财务数据会有延迟,尤其是季报披露期。 - Bloomberg:数据质量高,但贵。而且它的字段命名跟Wind不一样,比如分红率在Wind叫
dividend_yield,在Bloomberg可能叫DVD_YLD。
我建议你两个都备着。为什么?因为做因子研究最怕数据源单一,万一某天Wind某个字段出错了,你还能用Bloomberg交叉验证。我在项目中就遇到过Wind把某只REITs的NAV数据算错了,还好用Bloomberg对了一下才发现。
数据清洗:别让脏数据毁了你的模型
数据拉回来,第一件事不是跑模型,而是清洗。你想想看,如果输入的是垃圾,输出能是黄金吗?
清洗流程我一般分三步走:
- 去重:同一只REITs在不同交易日可能出现重复记录,尤其是跨市场品种。
- 格式统一:日期格式、数值精度、字段命名,全部标准化。比如日期统一成
YYYY-MM-DD,价格保留两位小数。 - 逻辑校验:检查价格是否为正、分红率是否在合理范围(比如0%-20%)。
核心原则:清洗时宁可多删,不要少删。一条脏数据可能让你的因子IC值从0.05变成-0.03,整个策略逻辑都变了。
缺失值处理:填还是不填?
REITs数据缺失很常见,尤其是早期上市的品种。比如某只REITs刚上市前三个月,可能没有完整的财务数据。这时候怎么办?
我常用的方法有三种:
| 方法 | 适用场景 | 我的经验 |
|---|---|---|
| 直接删除 | 缺失比例小于5% | 最省事,但要注意别把关键数据删了 |
| 前向填充 | 时间序列数据,如价格、成交量 | 适合日频数据,但别用在财务指标上 |
| 插值法 | 连续型数据,如NAV、分红率 | 线性插值就够用,别搞太复杂 |
举个例子。我曾经处理一只物流REITs的数据,它的分红率在2023年Q2缺失了。我直接用前向填充,把Q1的数据填进去。结果呢?Q3实际分红率比Q1低了30%,我的因子信号完全错了。后来我改成用同行业均值插值,效果好了很多。
小技巧:对于财务类缺失值,可以用同行业、同规模REITs的均值来填充。这样比简单前向填充更合理。
异常值检测:揪出那些离谱的数据
异常值检测,说白了就是找那些明显不对劲的数据。比如某只REITs一天涨了50%,或者分红率突然变成负数。这些数据不处理,模型会学歪。
我常用的检测方法:
- 3σ原则:超出均值±3倍标准差的数据,标记为异常。适合正态分布的数据。
- 箱线图法:用四分位数来识别异常值,对非正态分布更友好。
- 业务规则:比如REITs的日收益率超过±20%,基本就是数据错误。
我记得有一次,某只REITs的成交量数据突然飙到正常值的100倍。用3σ法检测出来了,但我不确定是数据错误还是真有大事发生。后来查了新闻,发现是那天有笔大宗交易。嗯,这种异常值就不能简单删除,得结合业务判断。
注意:异常值不一定是错误。有些异常值可能是市场极端事件,比如疫情爆发时的暴跌。处理前一定要先确认原因。
知识体系:数据预处理全流程
下面这张图是我自己总结的数据预处理流程,每次做新项目都会拿出来对照一遍。
实战代码:数据预处理模板
下面是我自己用的数据预处理模板,每次做新项目都会拿它改一改。你直接复制就能用。
import pandas as pd
import numpy as np
def clean_reits_data(df):
"""
REITs数据预处理函数
df: 原始DataFrame,包含日期、代码、价格、分红率等字段
"""
# 1. 去重
df = df.drop_duplicates(subset=['date', 'code'])
# 2. 格式统一
df['date'] = pd.to_datetime(df['date'])
df['price'] = df['price'].round(2)
# 3. 逻辑校验
df = df[df['price'] > 0]
df = df[df['dividend_yield'].between(0, 0.2)]
# 4. 缺失值处理(前向填充)
df = df.sort_values(['code', 'date'])
df['nav'] = df.groupby('code')['nav'].ffill()
# 5. 异常值检测(3σ法)
for col in ['price', 'volume']:
mean = df[col].mean()
std = df[col].std()
df = df[df[col].between(mean - 3*std, mean + 3*std)]
return df
# 使用示例
raw_data = pd.read_csv('reits_data.csv')
clean_data = clean_reits_data(raw_data)
print(f"清洗前:{len(raw_data)}条,清洗后:{len(clean_data)}条")
个人建议:这个模板我用了两年,基本覆盖了90%的常见问题。但每个数据集都有自己的特殊性,比如有些REITs的分红率可能因为特殊分红而超过20%,这时候阈值就得调整。灵活一点,别死板套用。
数据预处理这步,说白了就是磨刀。刀磨好了,后面砍柴才快。我见过太多人急着跑模型,结果被数据坑得死去活来。你想想看,与其花一周调参,不如花一天把数据搞干净。这笔账,怎么算都划算。