3、ESG因子构建:从原始数据到可用因子

好,咱们进入正题。ESG因子构建这块,说白了就是把那些评级机构给的原始分数,变成咱们量化模型能用的因子。我刚开始做这个的时候,也踩过不少坑——比如不同机构的评级压根儿不兼容,直接拼一起用,模型跑出来全是噪音。

今天我就把ESG因子构建的完整流程拆开讲。从数据源到标准化,每一步我都会结合自己的项目经验来说。

3.1 ESG评级数据来源:三大主流机构

目前市场上主流的ESG评级数据,主要来自三家机构:MSCI、Sustainalytics、Refinitiv。每家都有自己的打分逻辑,咱们得先搞清楚它们各自的特点。

机构 覆盖范围 评分区间 更新频率 我的评价
MSCI 全球约8500家公司 AAA-CCC(7档) 月度/季度 覆盖面广,但评级颗粒度偏粗
Sustainalytics 全球约12000家公司 0-100(分数越低越好) 月度 风险导向,适合做风险因子
Refinitiv 全球约10000家公司 0-100(分数越高越好) 季度 数据维度最细,适合做多维度因子

我个人习惯用Refinitiv的数据做基础因子构建。为什么?因为它把E、S、G三个维度拆成了10个主题、30多个指标,你能拿到非常细的颗粒度。MSCI的评级虽然名气大,但只有7档,做量化回测时区分度不够。

小提示: 如果你同时拿到多家机构的评级,别急着合并。我建议先做相关性分析——MSCI和Refinitiv的ESG总分相关性通常在0.6-0.7之间,但E、S、G各维度的相关性可能只有0.3-0.4。这说明它们对同一家公司的评价角度确实不同。

3.2 ESG综合得分计算:加权还是等权?

拿到原始数据后,第一步是算ESG综合得分。这里有个经典问题:E、S、G三个维度,该给它们分配多少权重?

我见过两种主流做法:

  • 等权法:E、S、G各占1/3。简单粗暴,但问题在于——不同行业的环境风险差异巨大。比如一家石油公司和一家软件公司,E的权重能一样吗?
  • 行业加权法:根据行业特性调整权重。比如高污染行业E的权重提到50%,金融行业G的权重提到40%。

嗯,这里要注意。我曾在项目中试过等权法,结果发现石油股的ESG得分被E维度严重拖低,而科技股的得分虚高。后来改用行业加权,效果好了不少。

下面是我常用的行业加权计算代码:

import pandas as pd
import numpy as np

def calc_esg_score(df, industry_weights):
    """
    df: 包含E_score, S_score, G_score的DataFrame
    industry_weights: 字典,key为行业代码,value为[E权重, S权重, G权重]
    """
    df['ESG_score'] = 0.0
    
    for industry, weights in industry_weights.items():
        mask = df['industry'] == industry
        df.loc[mask, 'ESG_score'] = (
            weights[0] * df.loc[mask, 'E_score'] +
            weights[1] * df.loc[mask, 'S_score'] +
            weights[2] * df.loc[mask, 'G_score']
        )
    
    return df

# 示例:不同行业的权重配置
industry_weights = {
    '能源': [0.5, 0.3, 0.2],
    '金融': [0.2, 0.3, 0.5],
    '科技': [0.3, 0.4, 0.3],
    '消费': [0.3, 0.4, 0.3]
}
避坑指南: 我曾经在回测时发现,直接用Refinitiv的原始ESG综合得分做因子,IC值(信息系数)只有0.02左右,几乎没预测能力。后来才发现,Refinitiv的ESG得分是相对排名,不是绝对分数。必须做截面标准化,才能用于多因子模型。

3.3 E/S/G各维度因子构建:拆开来看更清楚

ESG综合得分虽然方便,但说实话,它的预测能力往往不如单维度因子。我建议你至少构建以下三个维度的因子:

E维度因子:环境得分

  • 碳排放强度:Scope 1+2排放量 / 营收。这个因子在能源、工业行业特别有效。
  • 环境管理得分:Refinitiv的环境管理主题得分,反映公司是否有环保政策、目标。
  • 绿色收入占比:来自绿色产品或服务的收入比例。这个数据不太好拿,但很有价值。

S维度因子:社会得分

  • 员工满意度:可以用员工流失率、工伤率等指标代理。
  • 供应链管理:供应商ESG审核覆盖率。我发现在消费品行业,这个因子对股价有显著影响。
  • 产品责任:产品质量投诉率、数据隐私保护等。

G维度因子:治理得分

  • 董事会独立性:独立董事占比。这个因子在A股市场效果不错。
  • 高管薪酬合理性:CEO薪酬与员工中位数的比值。
  • 股权结构:大股东持股比例、机构投资者占比。

你想想看,为什么要把E、S、G拆开?因为不同因子在不同市场环境下的表现差异很大。比如2020年疫情时,S维度因子(员工福利、供应链韧性)表现特别好;而2022年能源危机时,E维度因子(碳排放、能源效率)反而成了拖累。

3.4 ESG因子标准化处理:让数据说话

原始ESG数据的问题在于——不同维度的量纲不同,不同行业的分布也不同。比如E得分在0-100之间,但G得分可能集中在60-80之间。直接扔进模型,G维度会被E维度吃掉。

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

  1. Z-score标准化:减去均值,除以标准差。适合正态分布的数据。
  2. 分位数标准化:把原始分数映射到0-1之间的分位数。适合偏态分布。
  3. 行业中性化:在每个行业内做标准化,消除行业偏差。

我个人最推荐「行业中性化+分位数标准化」的组合。为什么?因为ESG数据在不同行业间的分布差异太大了——金融行业的G得分普遍高,能源行业的E得分普遍低。如果不做行业中性化,你构建的因子本质上是在做行业轮动,而不是选股。

def industry_neutral_rank(df, factor_col, industry_col='industry'):
    """
    行业中性化+分位数标准化
    """
    df['factor_rank'] = np.nan
    
    for industry in df[industry_col].unique():
        mask = df[industry_col] == industry
        # 在行业内做分位数排名
        rank = df.loc[mask, factor_col].rank(pct=True)
        df.loc[mask, 'factor_rank'] = rank
    
    # 再对整个截面做Z-score标准化
    df['factor_standardized'] = (
        df['factor_rank'] - df['factor_rank'].mean()
    ) / df['factor_rank'].std()
    
    return df
核心要点: ESG因子标准化不是简单的数学处理,它决定了你的因子能否真正捕捉到公司之间的差异。我见过太多人直接把原始ESG得分扔进模型,结果回测曲线跟行业指数走势一模一样——那就是没做行业中性化的后果。

知识体系总览

下面这张图,我把ESG因子构建的完整流程画出来了。你可以对照着看,每一步的输入输出是什么,以及它们之间的依赖关系。

ESG因子构建流程 数据源 MSCI / Sustainalytics / Refinitiv ESG综合得分 行业加权 / 等权 维度拆分 E / S / G 各维度因子构建 碳排放强度 / 员工满意度 / 董事会独立性 环境管理 / 供应链管理 / 高管薪酬 标准化处理 Z-score标准化 / 分位数标准化 / 行业中性化 消除量纲差异 + 消除行业偏差 可用因子:ESG_score / E_factor / S_factor / G_factor 关键注意 不同机构数据 不可直接合并 需做相关性检验

好了,ESG因子构建的核心方法就这些。从数据源选择到综合得分计算,再到各维度因子构建和标准化处理,每一步都有讲究。我建议你拿到数据后,先画一张类似的流程图,把每个环节的输入输出理清楚,再动手写代码。

记住一句话:ESG因子的质量,90%取决于数据清洗和标准化,只有10%取决于模型本身。别在模型上花太多时间,先把数据搞干净。

专注资料整理