第三章:数据获取与预处理——用Tushare/Akshare搞定基金持仓、十大流通股东与北向资金

做机构抱团股分析,第一步就是搞数据。没有数据,再牛的策略也是空中楼阁。

我个人习惯用Tushare和Akshare这两个库。Tushare老牌,数据全,但需要token;Akshare开源免费,上手快。今天我就把这两套工具怎么用、怎么避坑,一次性讲清楚。

3.1 基金持仓数据获取

基金持仓数据,说白了就是看公募基金每个季度买了哪些股票、买了多少。这是识别机构抱团的核心数据源之一。

3.1.1 用Tushare获取基金持仓

先上代码。我习惯用Tushare的fund_portfolio接口,它能拿到基金季报的持仓明细。

import tushare as ts
import pandas as pd

# 初始化Tushare(需要你的token)
ts.set_token('你的token')
pro = ts.pro_api()

# 获取2024年Q1基金持仓数据
df = pro.fund_portfolio(
    ann_date='20240331',  # 公告日期
    fund_type='股票型',    # 基金类型
    fields='fund_code, fund_name, stk_code, stk_name, hold_amount, mkv'
)

print(df.head())

这里有个坑:ann_date是公告日期,不是持仓截止日期。基金季报通常在季度结束后15个工作日内披露,所以2024年Q1的数据,公告日期可能在4月中下旬。我曾经因为没注意这个时间差,拿到的数据滞后了整整两周,回测结果完全失真。

注意:基金持仓数据有滞后性。季报披露的是季度末的持仓,距离实际交易日期可能有1-2个月的时间差。做策略时一定要考虑这个延迟。

3.1.2 用Akshare获取基金持仓

Akshare的接口更直接,不需要token。我用它做快速验证比较多。

import akshare as ak

# 获取某只基金的持仓明细
fund_hold = ak.fund_em_portfolio_hold(
    symbol='110011',  # 基金代码
    date='2024'
)

print(fund_hold.head())

嗯,这里要注意:Akshare的date参数只接受年份,它会返回该年所有季度的数据。如果你只想看某个季度,需要自己过滤。

3.2 十大流通股东数据获取

十大流通股东数据,能看出哪些机构在重仓某只股票。我一般把它和基金持仓数据交叉验证,效果更好。

3.2.1 Tushare获取十大流通股东

# 获取某只股票的十大流通股东
df = pro.top10_floatholders(
    ts_code='600519.SH',  # 贵州茅台
    start_date='20240101',
    end_date='20240331'
)

print(df[['holder_name', 'hold_amount', 'hold_ratio']])

这个接口返回的数据很干净,直接就是股东名称、持股数量、持股比例。我一般会重点关注那些同时出现在多只股票十大流通股东名单里的机构——这往往是抱团的信号。

3.2.2 Akshare获取十大流通股东

import akshare as ak

# 获取十大流通股东
df = ak.stock_gpzy_ggt_hist(
    symbol='600519',
    start_date='20240101',
    end_date='20240331'
)

print(df.head())

说实话,Akshare的股东数据接口不如Tushare稳定。我遇到过几次接口返回空数据的情况,后来发现是参数格式问题。建议用之前先打印一下接口文档。

3.3 北向资金数据获取

北向资金,就是外资通过沪港通、深港通买入A股的资金。这帮外资机构,选股眼光很毒,经常能提前布局优质标的。

3.3.1 Tushare获取北向资金

# 获取北向资金每日持仓
df = pro.moneyflow_hsgt(
    start_date='20240101',
    end_date='20240331'
)

print(df[['date', 'north_money', 'south_money']])

这个接口返回的是北向资金整体流入流出数据。如果你想看具体个股的北向资金变化,需要用pro.moneyflow_hsgt_individual接口。

3.3.2 Akshare获取北向资金

import akshare as ak

# 获取北向资金个股持仓
df = ak.stock_hsgt_individual_em(
    symbol='600519',
    start_date='20240101',
    end_date='20240331'
)

print(df.head())

我个人更喜欢用Akshare的个股北向资金数据,因为它直接返回了持仓市值和占流通股比例,省去了自己计算的步骤。

3.4 数据预处理与清洗

数据拿到手,别急着用。原始数据通常有各种问题:缺失值、重复值、格式不统一。我总结了一套标准流程。

3.4.1 缺失值处理

# 检查缺失值
print(df.isnull().sum())

# 填充或删除
df = df.dropna(subset=['hold_amount'])  # 删除持股数量为空的记录
df['hold_ratio'] = df['hold_ratio'].fillna(0)  # 持股比例为空则填0

我曾经遇到过一个情况:某只基金的持仓数据里,持股比例全是NaN。后来发现是数据源的问题,那个季度该基金没有披露完整持仓。遇到这种情况,我建议直接删除该条记录,不要强行填充。

3.4.2 数据标准化

# 统一日期格式
df['date'] = pd.to_datetime(df['date'])

# 统一股票代码格式(去掉后缀)
df['stock_code'] = df['ts_code'].str[:6]

# 计算持股比例(如果原始数据没有)
df['hold_ratio'] = df['hold_amount'] / df['total_shares']

标准化这一步很重要。不同数据源的股票代码格式可能不一样,Tushare是"600519.SH",Akshare是"600519"。我习惯统一成6位数字代码,方便后续合并数据。

3.4.3 数据合并

# 合并基金持仓和北向资金数据
merged = pd.merge(
    fund_hold,
    north_flow,
    on=['stock_code', 'date'],
    how='inner'
)

print(merged.head())

合并数据时,我建议用inner方式,只保留两边都有的记录。这样能避免因为数据缺失导致的偏差。

3.5 知识体系总览

下面这张图,是我做机构抱团股数据获取的完整流程。你可以把它当作操作手册。

机构抱团股数据获取与预处理流程 基金持仓数据 十大流通股东 北向资金数据 Tushare / Akshare Tushare / Akshare Tushare / Akshare 数据预处理与清洗 缺失值处理 → 标准化 → 合并 最终数据集 基金持仓 + 十大流通股东 + 北向资金

3.6 实战中的避坑指南

做数据获取这么多年,我踩过的坑不少。分享几个最典型的。

技巧:数据源要交叉验证。我习惯同时用Tushare和Akshare获取同一份数据,如果两者差异超过5%,我会手动检查原因。
警告:不要过度依赖单一数据源。Tushare偶尔会更新延迟,Akshare有时接口会变动。我建议至少准备两个数据源作为备份。

我曾经因为Tushare的token过期,导致整个数据管道中断了三天。从那以后,我写了个自动检测token有效性的脚本,每天跑一次。嗯,这种小工具关键时刻能救命。

3.7 数据质量检查清单

每次拿到数据,我都会过一遍这个清单:

  • 时间范围:数据是否覆盖了目标时间段?有没有缺失的日期?
  • 数据完整性:有没有大量缺失值?持股比例加起来是否接近100%?
  • 逻辑一致性:持股数量是否为正数?日期是否在合理范围内?
  • 跨源一致性:不同数据源对同一只股票的持仓数据是否一致?

这个清单看起来简单,但能过滤掉80%的数据问题。我建议你也把它打印出来贴在工位上。


好了,数据获取和预处理就讲到这里。下一章我们会用这些数据,真正开始识别机构抱团股。记住:数据质量决定策略上限,别在这一步偷懒。

专注资料整理