第一章:数据获取与预处理——打好ESG轮动的地基

各位同学,欢迎来到《ESG因子在行业轮动中的应用实战》的第一章。

做量化投资,尤其是ESG因子相关的策略,最怕什么?我个人觉得,最怕数据还没整明白,就开始跑模型。你想想看,ESG评级数据、财务数据、行情数据,这三个东西来源不同、频率不同、格式不同,硬凑在一起,那结果肯定是一团浆糊。

所以,这一章咱们就踏踏实实地把数据这块地基打牢。说白了,就是教会你怎么用Python把这些数据对齐、清洗、标准化,最终构建出一个可以直接用于分析的面板数据结构。

1.1 数据获取:三路数据源的汇合

做ESG行业轮动,我们需要三路数据:

  • ESG评级数据:来自商道融绿、华证、中财绿金院等机构。我习惯用华证ESG评级,因为它的覆盖度广,更新也及时。
  • 财务数据:主要是ROE、营收增速、资产负债率等。这些可以从Wind、Tushare或者AkShare获取。
  • 行情数据:日频的收盘价、成交量,用于计算行业指数收益。

这里有个坑,我必须要提醒你。ESG评级通常是季度或半年度更新,而财务数据是季度更新,行情数据是日频。频率不一致,是第一个要解决的问题。

⚠️ 我曾经踩过的坑: 直接拿最新一期的ESG评级去匹配当天的行情,结果发现评级还没更新,数据全是前几个月的。后来我养成了一个习惯:每次获取数据时,先检查数据的“报告期”字段,确保时间戳是对的。

下面是我常用的数据获取代码示例:

import akshare as ak
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

# 1. 获取华证ESG评级数据(示例)
def get_esg_data():
    # 注意:实际使用时需要替换为真实的API接口
    esg_df = ak.stock_hsgt_esg(symbol="全部A股")
    esg_df['报告期'] = pd.to_datetime(esg_df['报告期'])
    return esg_df

# 2. 获取财务数据(以ROE为例)
def get_financial_data():
    fin_df = ak.stock_yjbb_em(date="20240930")
    fin_df = fin_df[['股票代码', '净资产收益率']]
    fin_df.columns = ['code', 'roe']
    return fin_df

# 3. 获取行业指数行情
def get_market_data():
    # 这里用申万一级行业指数为例
    index_df = ak.stock_zh_index_daily(symbol="sw801010")
    index_df['date'] = pd.to_datetime(index_df['date'])
    return index_df

1.2 数据对齐:让三个时钟同步

数据拿回来了,接下来就是对账。我一般会按照“季度”这个粒度来对齐。为什么是季度?因为ESG评级和财务数据都是季度更新,行情数据可以取季度末的收盘价。

具体做法是:

  1. 把ESG评级数据按“股票代码+报告期”做唯一索引
  2. 把财务数据也按“股票代码+报告期”做唯一索引
  3. 把行情数据按“股票代码+日期”做索引,然后只保留每个季度最后一个交易日的数据

嗯,这里要注意一个细节:ESG评级的报告期可能和财务报告期不完全一致。比如有些ESG评级是6月底发布,但覆盖的是3月底的数据。我建议统一使用“数据覆盖的截止日期”作为对齐基准。

💡 我的小技巧: 在合并数据前,先对每个数据集做一次去重。因为有些数据源可能会重复推送同一期的数据,不去重的话,合并后会出现很多重复行。
# 数据对齐示例
def align_data(esg_df, fin_df, market_df):
    # 1. 提取季度末日期
    market_df['quarter'] = market_df['date'].dt.to_period('Q')
    quarter_end = market_df.groupby('quarter')['date'].max().reset_index()
    quarter_end.columns = ['quarter', 'end_date']
    
    # 2. 合并行情与季度末日期
    market_aligned = pd.merge(market_df, quarter_end, on=['quarter', 'date'], how='inner')
    
    # 3. 合并ESG与财务数据
    esg_fin = pd.merge(esg_df, fin_df, on=['code', '报告期'], how='outer')
    
    # 4. 最终对齐
    final_data = pd.merge(esg_fin, market_aligned, 
                          left_on=['code', '报告期'], 
                          right_on=['code', 'end_date'], 
                          how='inner')
    return final_data

1.3 缺失值处理:别让NaN毁了你的策略

数据对齐后,你会发现有很多缺失值。尤其是ESG评级数据,小市值公司经常没有评级。怎么办?

我个人的处理原则是:

  • ESG评级缺失:如果某只股票连续两个季度都没有ESG评级,直接剔除。如果只是偶尔缺失,用前向填充(forward fill)。
  • 财务数据缺失:用行业均值填充。比如某公司的ROE缺失,就用同行业其他公司的平均ROE来补。
  • 行情数据缺失:如果停牌导致缺失,用复牌后的第一个交易日数据填充。如果退市了,直接剔除。
🔑 核心原则: 宁可少一些样本,也不要让错误的数据混进来。我曾经因为偷懒,直接用0填充缺失的ESG评级,结果策略回测表现异常好——后来发现那些被填充0的公司全是垃圾股,策略实际上是在做空它们。
# 缺失值处理
def handle_missing(data):
    # 1. ESG评级前向填充
    data['esg_score'] = data.groupby('code')['esg_score'].transform(
        lambda x: x.fillna(method='ffill')
    )
    
    # 2. 财务数据用行业均值填充
    data['roe'] = data.groupby('industry')['roe'].transform(
        lambda x: x.fillna(x.mean())
    )
    
    # 3. 剔除ESG评级连续缺失超过2期的股票
    data['esg_missing_count'] = data.groupby('code')['esg_score'].transform(
        lambda x: x.isna().rolling(2).sum()
    )
    data = data[data['esg_missing_count'] < 2]
    
    return data.drop(columns=['esg_missing_count'])

1.4 标准化:让不同量纲的数据可比较

ESG评级可能是A、B、C这样的等级,ROE是百分比,行情收益率是小数。量纲不同,没法直接放在一起做因子分析。

我常用的标准化方法有两种:

方法 适用场景 公式
Z-score标准化 连续型数据(如ROE、收益率) (x - mean) / std
排序标准化 等级型数据(如ESG评级) rank(x) / N

对于ESG评级,我建议先把它映射成数值。比如A=5,B=4,C=3,D=2,E=1。然后再做排序标准化,这样能保留评级之间的相对顺序关系。

# 标准化处理
def standardize_factors(data):
    # 1. ESG评级映射为数值
    esg_map = {'A': 5, 'B': 4, 'C': 3, 'D': 2, 'E': 1}
    data['esg_numeric'] = data['esg_rating'].map(esg_map)
    
    # 2. 排序标准化(按行业分组)
    data['esg_rank'] = data.groupby('industry')['esg_numeric'].rank(pct=True)
    
    # 3. Z-score标准化(按行业分组)
    data['roe_zscore'] = data.groupby('industry')['roe'].transform(
        lambda x: (x - x.mean()) / x.std()
    )
    
    return data

1.5 构建面板数据结构:最终形态

经过以上步骤,我们最终得到的数据结构应该是这样的:

code date industry esg_rank roe_zscore return
000001 2024-03-31 银行 0.85 1.23 0.05
000002 2024-03-31 房地产 0.42 -0.67 -0.02
... ... ... ... ... ...

这就是标准的面板数据结构。每一行代表一只股票在某个时间点的截面数据,所有时间点的数据堆叠在一起,就形成了面板数据。

有了这个结构,我们就可以做后续的因子分析、行业分组、轮动策略回测了。

📌 总结一下: 数据获取与预处理,说白了就是三件事——对齐、清洗、标准化。每一步都马虎不得。我见过太多人花90%的时间写策略代码,结果数据没处理好,策略表现一塌糊涂。记住:好的数据是成功策略的一半。
ESG行业轮动数据预处理流程 ESG评级数据 季度更新 财务数据 季度更新 行情数据 日频更新 步骤1:数据对齐(按季度末日期合并) 统一时间戳 → 去重 → 合并 步骤2:缺失值处理 前向填充 | 行业均值填充 | 剔除连续缺失 步骤3:因子标准化 ESG评级:排序标准化 | 财务指标:Z-score标准化

好了,这一章的内容就到这里。数据预处理虽然繁琐,但它是整个ESG行业轮动策略的基石。下一章我们会基于这个面板数据,开始构建真正的ESG因子。


公众号:蓝海资料掘金营,微信deep3321