3、数据获取:Python环境搭建、获取碳市场交易数据(公开API与爬虫)、数据清洗与预处理
说实话,做碳市场波动率交易,最头疼的不是策略本身,而是数据。我见过太多人策略写得漂亮,结果数据源一断,整个系统直接瘫痪。所以这一章,咱们把地基打牢。
我个人习惯是:先搭环境,再找数据源,最后做清洗。三步走,稳得很。
3.1 Python环境搭建——别小看这一步
你可能会想:「装个Python谁不会?」嗯,我以前也这么想。直到有一次在项目里,因为numpy版本冲突,整整debug了两天。从那以后,我老老实实用虚拟环境。
我推荐用 conda 来管理环境,干净利落:
# 创建专用环境
conda create -n carbon_trading python=3.9
# 激活环境
conda activate carbon_trading
# 安装核心库
pip install pandas numpy matplotlib scipy requests beautifulsoup4
pip install statsmodels arch # 波动率建模专用
pip install jupyter # 交互式分析
requirements.txt 保存好。换机器时一行命令搞定:pip install -r requirements.txt。别问我怎么知道的——我换过三台电脑。
这里有个坑要注意:Python 3.10以上版本对某些旧库支持不好。我建议锁定3.9,兼容性最好。
3.2 碳市场数据源——你有哪些选择?
碳市场的数据不像股票那么「大方」。公开API少,爬虫限制多。我整理了一下目前可用的数据源:
| 数据源 | 类型 | 获取方式 | 数据频率 | 费用 |
|---|---|---|---|---|
| 上海环境能源交易所 | 公开 | 网页爬虫 | 日度 | 免费 |
| 湖北碳排放权交易中心 | 公开 | 网页爬虫 | 日度 | 免费 |
| 广州碳排放权交易所 | 公开 | 网页爬虫 | 日度 | 免费 |
| ICE(洲际交易所) | 商业 | API | 分钟级 | 付费 |
| Refinitiv Eikon | 商业 | API | 分钟级 | 付费 |
说白了,国内试点碳市场的数据,基本靠爬。欧盟碳市场(EU ETS)倒是有一些公开API,但延迟高。我个人建议:先用国内试点数据练手,跑通流程后再上商业数据源。
3.3 爬虫实战——以湖北碳市场为例
我选湖北碳市场做例子,为什么?因为它的网页结构相对规整,适合教学。你想想看,要是上来就搞上海环交所那种反爬严格的,估计你直接劝退了。
先看目标网页结构。打开湖北碳排放权交易中心的历史行情页面,F12看一下Network请求。你会发现数据是通过一个JSON接口返回的。嗯,这就好办了。
import requests
import pandas as pd
from datetime import datetime, timedelta
def fetch_hubei_carbon(start_date, end_date):
"""
获取湖北碳市场日度交易数据
我在项目里实测过,这个接口比较稳定
"""
base_url = "https://www.hbets.cn/api/trade/history"
params = {
'startDate': start_date,
'endDate': end_date,
'pageSize': 1000
}
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://www.hbets.cn/'
}
try:
response = requests.get(base_url, params=params, headers=headers, timeout=10)
response.raise_for_status()
data = response.json()
df = pd.DataFrame(data['data'])
# 字段重命名,方便后续处理
df.rename(columns={
'tradeDate': 'date',
'openingPrice': 'open',
'closingPrice': 'close',
'highestPrice': 'high',
'lowestPrice': 'low',
'volume': 'volume',
'turnover': 'amount'
}, inplace=True)
df['date'] = pd.to_datetime(df['date'])
df.sort_values('date', inplace=True)
return df
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
return None
# 使用示例
df_hubei = fetch_hubei_carbon('2023-01-01', '2024-12-31')
print(df_hubei.head())
time.sleep(1),做个文明爬虫。
3.4 数据清洗与预处理——脏活累活但必须做
数据拿到手,你以为就能直接用?太天真了。我见过太多「漂亮策略死在脏数据上」的案例。碳市场的数据尤其脏——缺失值、异常跳空、节假日停牌……
我的清洗流程一般分四步:
- 缺失值处理——碳市场有节假日,非交易日直接NaN。别用前向填充,会引入未来信息。
- 异常值检测——比如某天价格突然涨了50%,可能是数据录入错误。
- 价格序列对齐——不同交易所的交易日历不同,需要统一。
- 计算收益率——波动率交易的核心输入。
来看具体代码:
def clean_carbon_data(df):
"""
碳市场数据清洗流水线
这是我反复迭代后的版本,比较稳健
"""
# 第一步:删除完全空的行
df = df.dropna(how='all')
# 第二步:处理缺失值
# 注意:碳市场非交易日不产生数据,不要用ffill
# 我习惯用插值法,但只插中间缺失,不插尾部
df['close'] = df['close'].interpolate(method='linear', limit=3)
# 第三步:异常值检测——用3倍标准差法
mean = df['close'].mean()
std = df['close'].std()
df['close'] = df['close'].clip(lower=mean - 3*std, upper=mean + 3*std)
# 第四步:计算对数收益率(波动率建模的标准输入)
df['log_return'] = np.log(df['close'] / df['close'].shift(1))
# 第五步:剔除首行(收益率NaN)
df = df.dropna(subset=['log_return'])
return df
# 执行清洗
df_clean = clean_carbon_data(df_hubei)
print(f"清洗前 {len(df_hubei)} 条,清洗后 {len(df_clean)} 条")
3.5 数据存储——别每次都爬
你想想看,每次跑策略都去爬一遍数据,多傻啊。我习惯把清洗好的数据存成本地文件,或者SQLite数据库。
# 存成CSV(简单粗暴)
df_clean.to_csv('hubei_carbon_clean.csv', index=False)
# 或者存SQLite(适合增量更新)
import sqlite3
conn = sqlite3.connect('carbon_data.db')
df_clean.to_sql('hubei_carbon', conn, if_exists='replace', index=False)
conn.close()
我个人更推荐SQLite。为什么?因为你可以写个定时任务,每天增量更新。比如每天早上8点跑一次爬虫,把新数据追加进去。这样你的策略就能「活」起来。
3.6 本章知识体系一览
说了这么多,咱们用一张图把整个流程串起来:
这张图把咱们这章的核心逻辑都串起来了。从环境搭建开始,到数据获取,再到清洗预处理,最后存成可用的格式。每一步都有坑,但每一步也都有解法。
数据准备好了,下一章咱们就可以开始真正的波动率建模了。嗯,到时候你会感谢现在认真做数据清洗的自己的。
公众号:蓝海资料掘金营,微信deep3321