第二章:数据源与采集——碳排放数据来源与API使用

做碳因子挖掘,第一步不是写代码,而是搞清楚数据从哪来。

我刚开始接触碳金融时,以为碳排放数据就像股票行情一样,拉个API就能拿到。结果发现完全不是这么回事。碳排放数据的来源分散、格式各异、更新频率也不统一。说白了,这是个典型的「脏活累活」。

但没办法,数据质量直接决定因子有效性。你想想看,如果底层数据都是错的,再漂亮的模型也是白搭。

2.1 三大主流碳排放数据源

目前业界公认的三大数据源是:CDPTrucostSustainalytics。我个人的经验是,这三家各有侧重,最好搭配使用。

数据源 覆盖范围 数据特点 适用场景
CDP 全球约1.5万家企业 企业自主披露,含Scope 1/2/3 ESG整合、气候风险分析
Trucost 全球约1.2万家企业 基于投入产出模型估算 碳因子挖掘、组合碳足迹
Sustainalytics 全球约1.3万家企业 结合披露与估算,含争议事件 ESG评级、负面筛选

2.1.1 CDP:最权威的披露数据

CDP(原碳披露项目)的数据是企业自己填的问卷。这意味着什么?意味着数据质量参差不齐。

我记得有一次做回测,发现某家能源公司的碳排放数据突然下降了80%。查了半天,原来是那年他们换了问卷模板,把Scope 2的核算方法从「位置法」改成了「市场法」。嗯,这里要注意:同一家公司不同年份的数据口径可能不一致

CDP的数据结构大致是这样的:

{
  "company_id": "AAPL",
  "year": 2023,
  "scope_1": 12345.6,    // 单位:吨CO2e
  "scope_2": 6789.0,     // 位置法
  "scope_3": 45678.9,    // 仅包含部分类别
  "methodology": "位置法",
  "verification": true   // 是否经过第三方核验
}
避坑指南:我曾经用CDP数据做因子时,发现Scope 3的缺失率高达60%以上。后来我学乖了——只用Scope 1+2做核心因子,Scope 3只作为辅助参考。

2.1.2 Trucost:估算数据的标杆

Trucost(现属S&P Global)用的是投入产出模型。说白了,就是根据企业的营收和行业碳排放强度,倒推出碳排放量。

这种方法的好处是覆盖全——哪怕企业不披露,也能给你一个估算值。但坏处也很明显:估算误差可能很大

我个人的习惯是:对于披露数据完整的企业,优先用CDP;对于缺失数据的企业,用Trucost做插补。两者结合,效果最好。

2.1.3 Sustainalytics:争议事件与碳风险

Sustainalytics(现属Morningstar)的特色是结合了争议事件分析。比如某公司虽然披露的碳排放不高,但被爆出有环境诉讼,那它的碳风险评分就会下调。

这个数据源在做负面筛选时特别有用。我曾在构建碳风险因子时,把Sustainalytics的争议事件得分作为一个哑变量加入模型,结果因子的夏普比率提升了0.3。

2.2 数据采集方法与API使用

数据源选好了,接下来就是怎么把数据拉下来。这里我分享三种常用的采集方式。

2.2.1 手动下载(适合小规模研究)

CDP和Sustainalytics都提供Excel/CSV下载。如果你只是做几十只股票的分析,手动下载完全够用。

但要注意:下载前一定要看清楚数据字典。我见过有人把「Scope 2 (location-based)」和「Scope 2 (market-based)」搞混,结果因子方向都算反了。

2.2.2 API采集(适合自动化流程)

对于量化策略,手动下载显然不现实。这时候就需要API了。

以Trucost的API为例,调用方式如下:

import requests
import pandas as pd

# Trucost API配置
API_KEY = "your_api_key_here"
BASE_URL = "https://api.trucost.com/v1"

# 获取碳排放数据
def get_emissions(company_ids, year):
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    }
    
    payload = {
        "companies": company_ids,
        "year": year,
        "metrics": ["scope_1", "scope_2", "scope_3"]
    }
    
    response = requests.post(
        f"{BASE_URL}/emissions",
        headers=headers,
        json=payload
    )
    
    if response.status_code == 200:
        return pd.DataFrame(response.json())
    else:
        print(f"Error: {response.status_code}")
        return None

# 示例:获取苹果和微软的2023年数据
df = get_emissions(["AAPL", "MSFT"], 2023)
print(df.head())
小技巧:API调用时,建议每次请求不要超过50家公司,否则容易超时。我一般会分批处理,每批40家,中间加1秒延迟。

2.2.3 数据清洗与对齐

数据拉下来后,最头疼的是对齐问题。不同数据源的公司标识符不一样——CDP用LEI码,Trucost用ISIN,Sustainalytics用自有ID。

我常用的做法是:

  1. 先建立一个主映射表,把各数据源的ID统一映射到股票代码
  2. 然后按年份合并数据
  3. 最后做缺失值处理

举个例子:

# 建立映射表
mapping = {
    "AAPL": {"cdp_id": "LEI_123", "trucost_id": "ISIN_US0378331005"},
    "MSFT": {"cdp_id": "LEI_456", "trucost_id": "ISIN_US5949181045"}
}

# 合并数据
def merge_emissions(cdp_df, trucost_df, mapping):
    # 先映射到股票代码
    cdp_df["ticker"] = cdp_df["cdp_id"].map(
        {v["cdp_id"]: k for k, v in mapping.items()}
    )
    trucost_df["ticker"] = trucost_df["trucost_id"].map(
        {v["trucost_id"]: k for k, v in mapping.items()}
    )
    
    # 按ticker和year合并
    merged = pd.merge(
        cdp_df, trucost_df,
        on=["ticker", "year"],
        how="outer",
        suffixes=("_cdp", "_trucost")
    )
    
    return merged

2.3 知识体系总览

说了这么多,我画了一张图帮你理清思路:

碳数据采集知识体系 数据源层 CDP(披露数据) Trucost(估算数据) Sustainalytics(争议数据) 采集层 手动下载(CSV/Excel) API自动化采集 数据清洗与对齐 输出层 统一格式的碳排放面板数据 碳因子矩阵(用于多因子模型)

2.4 实战建议

最后,给几个实战中的建议:

  • 优先用CDP的核验数据——有第三方核验的数据,质量通常更高
  • Trucost做插补——对于缺失数据,用Trucost的估算值填补,但要在因子中加一个「是否估算」的哑变量
  • Sustainalytics做风控——争议事件往往预示着未来的碳风险,可以作为一个预警信号
  • 建立数据质量监控——我每周都会跑一遍数据完整性检查,看看有没有异常跳变
核心要点:碳数据采集不是一次性工作,而是一个持续迭代的过程。数据源会更新、API会升级、公司的披露口径也会变。保持数据管道的自动化与可追溯性,比追求「完美数据」更重要。

嗯,关于数据源和采集,就先聊到这儿。下一章我们会深入讨论碳因子的具体构造方法,到时候会用到今天讲的数据。


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