第四章:数据工程——构建股票关系图

说实话,很多做量化的人把精力都花在模型上,却忽略了数据工程。我个人觉得,这其实是本末倒置。你想想看,再牛的图神经网络,喂进去一堆脏数据,出来的结果能靠谱吗?

这一章,我们就来聊聊怎么构建股票关系图。说白了,就是把股票之间的关联关系,用图的形式表达出来。我做过不少项目,踩过很多坑,今天把这些经验都分享给你。

4.1 为什么要构建股票关系图?

传统的量化分析,往往只盯着个股的价格、成交量这些时间序列数据。但股票不是孤岛。一只股票的涨跌,往往会带动同行业的股票,或者影响它的上下游企业。

举个例子。我记得有一次,某家芯片设计公司突然宣布断供。结果呢?它的下游客户——那些手机厂商,股价当天就跌了。而它的竞争对手,股价反而涨了。这种联动关系,传统的时间序列模型很难捕捉到。

图神经网络的优势就在这里。它能天然地处理这种「关系」信息。你只需要把股票当作节点,把它们的关联关系当作边,剩下的交给模型去学。

核心思想: 股票关系图 = 节点(股票) + 边(关系) + 节点特征(基本面数据)

4.2 三种核心关系:行业、供应链、股东

构建股票关系图,最常用的就是这三种关系。我一个个来说。

4.2.1 行业关联

这是最直观的关系。同行业的股票,往往受到相同的宏观因素影响。比如,所有白酒股都会受到「禁酒令」的影响。

怎么构建?很简单。用申万行业分类或者GICS行业分类。同一行业分类下的股票,两两之间连一条边。

嗯,这里要注意一点。行业分类的粒度很重要。太粗了(比如「制造业」),关系太弱。太细了(比如「白酒Ⅲ」),又可能样本太少。我个人习惯用申万二级行业分类,平衡性比较好。

4.2.2 供应链关联

这个就更有意思了。A公司是B公司的上游供应商,或者C公司是D公司的大客户。这种关系往往能带来很强的股价联动。

我在项目中遇到过这样的情况:某家汽车零部件公司,它的最大客户是一家新能源车企。结果新能源车企的销量一公布,零部件公司的股价立马跟着动。这种关系,光看行业分类是看不出来的。

供应链数据从哪里来?我后面会讲数据源。这里先记住:供应链关系是有方向的。A→B 表示 A 是 B 的供应商。在构建图的时候,可以用有向边来表示。

4.2.3 股东关联

这个稍微复杂一点。两家公司如果有共同的机构股东,或者有交叉持股,那么它们的股价走势往往也会趋同。

为什么?因为机构投资者在做资产配置时,往往会同时买入或卖出同一板块的股票。或者,交叉持股意味着两家公司的利益深度绑定。

我曾经踩过一个坑。当时我直接把所有共同股东都算作关联,结果图变得非常稠密,模型根本跑不动。后来我加了一个阈值:只有共同持股比例超过5%的,才建立关联。效果好了很多。

小技巧: 三种关系可以叠加使用。比如,行业关联 + 供应链关联,构建一个多关系图。这在GNN中叫做「异构图」,处理起来稍微复杂一点,但效果往往更好。

4.3 数据源:Tushare 与 Yahoo Finance

数据从哪里来?我主要用两个数据源。

4.3.1 Tushare(国内数据)

Tushare 是国内最常用的金融数据接口之一。它提供了非常全面的A股数据,包括行业分类、股东信息、财务数据等。

我个人比较推荐用 Tushare Pro 版本。虽然需要积分,但数据质量比免费版好很多。尤其是股东数据,免费版经常缺字段。

下面是一个获取行业分类的示例代码:

import tushare as ts

# 设置token
ts.set_token('你的token')

# 初始化pro接口
pro = ts.pro_api()

# 获取所有股票的行业分类
df = pro.stock_basic(
    exchange='',
    list_status='L',
    fields='ts_code,symbol,name,industry'
)

print(df.head())

嗯,这里要注意。Tushare 的行业分类默认是申万一级行业。如果你需要二级或三级分类,需要额外调用 industry_code 接口。

4.3.2 Yahoo Finance(海外数据)

如果你做的是美股或者港股,Yahoo Finance 是个不错的选择。它免费,而且数据覆盖面广。

不过,Yahoo Finance 的行业分类比较粗糙。它用的是自己的分类体系,只有十几个大类。对于供应链数据,Yahoo Finance 基本不提供。你需要从其他渠道获取。

下面是一个获取美股行业分类的示例:

import yfinance as yf

# 获取苹果公司的信息
aapl = yf.Ticker("AAPL")

# 获取行业信息
info = aapl.info
print(f"行业: {info['sector']}")
print(f"子行业: {info['industry']}")
注意: Yahoo Finance 的API经常变动。我去年就遇到过接口突然失效的情况。建议做好异常处理,或者定期检查数据是否正常更新。

4.4 数据清洗实战

数据拿到手,不等于就能直接用。清洗这一步,往往比获取数据更耗时。我一般会做以下几件事。

4.4.1 处理缺失值

行业分类缺失?股东数据不全?这是家常便饭。我的处理原则是:

  • 行业分类缺失:直接剔除这只股票。因为行业关联是基础关系,缺失了就没法建图。
  • 股东数据缺失:可以保留,但只建立行业关联和供应链关联。
  • 供应链数据缺失:这个比较棘手。我一般会用文本挖掘的方法,从年报中提取客户和供应商信息。

4.4.2 去重与标准化

同一个公司,在不同数据源里可能有不同的名字。比如「贵州茅台」和「贵州茅台酒股份有限公司」。需要做实体对齐。

我常用的方法是:先用股票代码作为唯一标识。如果代码不一致,再用公司名称做模糊匹配。

# 简单的去重示例
df_clean = df.drop_duplicates(subset=['ts_code'])

# 标准化公司名称
df_clean['name'] = df_clean['name'].str.replace('股份有限公司', '')
df_clean['name'] = df_clean['name'].str.replace('有限公司', '')

4.4.3 时间对齐

这个坑我踩过好几次。不同数据源的数据更新频率不一样。Tushare 的财务数据是季度更新,而 Yahoo Finance 的价格数据是日更新。

如果你要构建动态图(随时间变化的图),一定要做好时间对齐。我一般会以交易日为基准,把财务数据、股东数据都对齐到最近的交易日。

避坑指南: 我曾经直接用最新一期的财务数据去建图,结果发现模型在回测时用了未来数据。后来我强制要求:建图时只能使用截止到当前时间点的数据。这一点非常重要,否则你的回测结果全是假的。

4.5 知识体系总览

说了这么多,我画了一张图,帮你把这一章的核心逻辑串起来。

股票关系图构建流程 数据源 Tushare(A股) Yahoo Finance(海外) 数据清洗 缺失值处理 去重与标准化 时间对齐 关系构建 行业关联 供应链关联 股东关联 图构建 节点 = 股票,边 = 关系,特征 = 基本面数据 输出 股票关系图(可输入GNN模型) 图神经网络与基本面分析 · 第四章

4.6 实战:从零构建一个简单的股票关系图

光说不练假把式。我们来写一段完整的代码,从数据获取到图构建,一步到位。

import tushare as ts
import pandas as pd
import networkx as nx

# 1. 获取数据
pro = ts.pro_api('你的token')
df_stocks = pro.stock_basic(exchange='', list_status='L')

# 2. 构建行业关系
industry_groups = df_stocks.groupby('industry')
G = nx.Graph()

for industry, group in industry_groups:
    stocks = group['ts_code'].tolist()
    # 同一行业内的股票两两相连
    for i in range(len(stocks)):
        for j in range(i+1, len(stocks)):
            G.add_edge(stocks[i], stocks[j], relation='industry')

# 3. 添加节点特征(以市盈率为例)
for code in G.nodes():
    try:
        df_fin = pro.daily_basic(ts_code=code, trade_date='20240131')
        if not df_fin.empty:
            pe = df_fin['pe'].values[0]
            G.nodes[code]['pe'] = pe
    except:
        G.nodes[code]['pe'] = None

print(f"图构建完成:{G.number_of_nodes()} 个节点,{G.number_of_edges()} 条边")

这段代码很简单,但已经能跑通。你可以在它的基础上,加入供应链和股东关系。

核心要点: 图的质量,决定了模型的上限。花80%的时间在数据工程上,剩下的20%给模型,这是值得的。

好了,这一章的内容就到这里。数据工程是个细活,急不来。你慢慢做,把每一步都走扎实了,后面模型训练的时候会轻松很多。

专注资料整理