第三章:数据源与采集

做量化交易,数据就是你的弹药库。没有好数据,再牛的模型也是白搭。

这一章,我重点聊聊黑色系的数据源怎么选、怎么拿、怎么洗、怎么存。这些都是我这些年踩坑踩出来的经验。

3.1 常用数据源:Mysteel、Wind、SMM

黑色系交易,绕不开这三家数据源。我一个个说。

3.1.1 Mysteel(我的钢铁)

做螺纹钢、热卷、铁矿石的朋友,Mysteel是首选。它的核心数据包括:

  • 库存数据:五大品种钢材社会库存、钢厂库存,每周更新
  • 产量数据:高炉开工率、电炉开工率、螺纹钢产量
  • 表观消费:测算出来的需求数据,市场关注度极高
  • 价格指数:全国各地区的现货价格

我个人习惯用Mysteel的周度库存数据做库存周期判断。为什么?因为它连续性好,从2015年到现在几乎没有断过。

关键点:Mysteel的数据通常在每周四下午发布,这是黑色系交易员的"数据之夜"。我建议你提前写好脚本,数据一出来就自动拉取。

3.1.2 Wind(万得)

Wind是金融终端,覆盖面广。黑色系方面,它主要提供:

  • 期货行情:螺纹、热卷、铁矿、焦煤、焦炭的日线、分钟线
  • 宏观数据:房地产投资、基建投资、PMI等
  • 行业数据:粗钢产量、生铁产量(月度)
  • 基差数据:期现价差、跨期价差

Wind的API接口叫WindPy,Python可以直接调用。不过要注意,Wind的API有并发限制,我遇到过同时拉太多数据被限流的情况。

3.1.3 SMM(上海有色网)

SMM在有色金属领域是老大,但黑色系方面也有特色数据:

  • 铁矿石港口库存:45港库存数据,比Mysteel更细
  • 钢厂原料库存:烧结矿、焦炭的可用天数
  • 废钢数据:废钢到货量、消耗量

做铁矿石的朋友,我建议把SMM和Mysteel的数据交叉验证。两家口径不同,有时候数据会打架,这时候就要靠经验判断了。

数据源 核心优势 更新频率 API支持
Mysteel 钢材库存、产量 周度 HTTP API
Wind 期货行情、宏观 实时/日/周/月 WindPy
SMM 铁矿石港口库存 周度 HTTP API

3.2 API接口调用

数据拿到了,怎么用代码拉取?我分享几个实战代码片段。

3.2.1 Mysteel API调用示例

Mysteel的API需要申请key,一般走HTTP请求。这是我常用的写法:

import requests
import pandas as pd
from datetime import datetime, timedelta

class MysteelAPI:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://api.mysteel.com/v1"
    
    def get_inventory_data(self, variety="螺纹钢", date=None):
        """获取库存数据"""
        if date is None:
            date = datetime.now().strftime("%Y-%m-%d")
        
        url = f"{self.base_url}/inventory"
        params = {
            "key": self.api_key,
            "variety": variety,
            "date": date
        }
        
        response = requests.get(url, params=params)
        if response.status_code == 200:
            data = response.json()
            return pd.DataFrame(data["data"])
        else:
            print(f"请求失败: {response.status_code}")
            return None

# 使用示例
api = MysteelAPI("your_api_key_here")
df = api.get_inventory_data("螺纹钢", "2024-01-04")
print(df.head())

小技巧:Mysteel的API有调用次数限制,我一般用time.sleep(1)控制频率,避免被封。

3.2.2 Wind API调用示例

WindPy需要先安装,然后启动Wind终端。代码是这样的:

from WindPy import w
import pandas as pd

# 启动Wind
w.start()

# 获取螺纹钢主力合约日线数据
code = "RB.SHF"  # 螺纹钢主力合约
fields = "close,volume,open_interest"
start_date = "2023-01-01"
end_date = "2024-01-01"

# 拉取数据
data = w.wsd(code, fields, start_date, end_date)
df = pd.DataFrame(data.Data, index=data.Fields, columns=data.Times).T

print(df.head())

# 别忘了关闭连接
w.close()

嗯,这里要注意。Wind的wsd函数返回的数据格式有点特殊,需要转成DataFrame。我刚开始用的时候也折腾了半天。

3.3 数据清洗与预处理

数据拉下来,别急着用。原始数据通常有各种问题。我遇到过的情况包括:

  • 缺失值:某个日期没有数据
  • 异常值:突然跳变,比如库存一天涨了50%
  • 重复值:同一日期出现多条记录
  • 格式问题:日期格式不统一

这是我的清洗流程:

def clean_inventory_data(df):
    """清洗库存数据"""
    # 1. 处理缺失值
    df = df.dropna(subset=["inventory"])  # 删除库存为空的记录
    df = df.fillna(method="ffill")  # 前向填充其他缺失值
    
    # 2. 处理异常值
    # 用3倍标准差法检测异常
    mean = df["inventory"].mean()
    std = df["inventory"].std()
    df = df[(df["inventory"] > mean - 3*std) & 
            (df["inventory"] < mean + 3*std)]
    
    # 3. 去重
    df = df.drop_duplicates(subset=["date"])
    
    # 4. 统一日期格式
    df["date"] = pd.to_datetime(df["date"])
    df = df.sort_values("date")
    
    return df

# 应用清洗
df_clean = clean_inventory_data(df_raw)

避坑指南:我曾经因为没处理异常值,导致模型在某个极端数据点上过度拟合。后来回测才发现问题。所以清洗这步,千万别偷懒。

3.4 数据存储方案

数据清洗完了,存哪里?我推荐分层存储:

3.4.1 原始数据层(Raw Data)

存原始API返回的数据,不做任何修改。格式用CSV或Parquet。我习惯按日期分文件夹:

data/
├── raw/
│   ├── mysteel/
│   │   ├── 2023/
│   │   │   ├── 01/
│   │   │   │   ├── inventory_2023-01-05.csv
│   │   │   │   └── price_2023-01-05.csv
│   │   │   └── ...
│   │   └── 2024/
│   └── wind/
│       └── futures/
│           └── RB_2023.csv

3.4.2 清洗数据层(Clean Data)

存清洗后的数据,统一格式。推荐用Parquet格式,压缩率高、读取快:

data/
└── clean/
    ├── inventory.parquet
    ├── price.parquet
    └── macro.parquet

3.4.3 特征数据层(Feature Data)

存计算好的技术指标、库存周期信号等。用HDF5或SQLite:

import pandas as pd

# 计算库存周期信号
def compute_inventory_signal(df):
    """计算库存周期信号"""
    df["inventory_change"] = df["inventory"].pct_change()
    df["price_change"] = df["price"].pct_change()
    
    # 库存周期四象限
    conditions = [
        (df["inventory_change"] > 0) & (df["price_change"] > 0),  # 主动补库
        (df["inventory_change"] > 0) & (df["price_change"] < 0),  # 被动补库
        (df["inventory_change"] < 0) & (df["price_change"] < 0),  # 主动去库
        (df["inventory_change"] < 0) & (df["price_change"] > 0)   # 被动去库
    ]
    choices = [1, 2, 3, 4]
    df["cycle_signal"] = np.select(conditions, choices, default=0)
    
    return df

# 保存特征数据
df_features = compute_inventory_signal(df_clean)
df_features.to_hdf("features.h5", key="inventory_cycle")

3.5 知识体系总览

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

数据源与采集知识体系 数据源层 Mysteel Wind SMM 钢材库存/产量 期货行情/宏观 铁矿石港口库存 采集层 HTTP API WindPy 定时任务调度 requests库 wsd/wst函数 cron/APScheduler 清洗层 缺失值处理 异常值检测 格式统一 dropna/ffill 3倍标准差法 pd.to_datetime 存储层 原始数据层 (CSV/Parquet) 清洗数据层 (Parquet) 特征数据层 (HDF5/SQLite)

这张图把整个数据流程串起来了。从数据源到采集,再到清洗和存储,每一步都有对应的工具和方法。

我的建议:刚开始做的时候,别追求大而全。先搞定一个数据源(比如Mysteel的库存数据),把整个流程跑通,再慢慢扩展。我当年就是先从螺纹钢库存数据入手的。

数据源这块,说白了就是"拿来主义"。但怎么拿、怎么洗、怎么存,决定了你后续策略的成败。记住一句话:垃圾数据进,垃圾策略出。

专注资料整理