股东持股变动监控系统:用Python追踪大股东增减持与筹码集中度

大股东的持股变动是判断公司前景的重要信号——大股东增持通常意味着看好公司未来,减持则可能意味着不看好。但手动追踪这些信息太麻烦了,去年我搭建了一个股东持股变动监控系统,自动从本地数据中提取大股东的增减持信息,并结合筹码集中度指标做综合分析。

系统主要用到以下数据接口:持股变动数据在time/f10目录下,包含十大股东、十大流通股东、基金持股等信息;股票列表在base/gplist;财务指标在time/f10/fi/{dm}。

import json
import os
import pandas as pd
import numpy as np
from datetime import datetime

data_dir = "D:/ig50_data"

def read_top10_holders(dm):
    file_path = os.path.join(data_dir, "time", "f10", "sdgd", dm)
    with open(file_path, "r", encoding="utf-8") as f:
        data = json.load(f)
    df = pd.DataFrame(data)
    df.columns = ["dm", "mc", "jyrq", "gdMc", "cgs", "zltb", "gdSort", "gdType", "zjType"]
    df["jyrq"] = pd.to_datetime(df["jyrq"])
    return df

def read_top10_float_holders(dm):
    file_path = os.path.join(data_dir, "time", "f10", "sdltgd", dm)
    with open(file_path, "r", encoding="utf-8") as f:
        data = json.load(f)
    df = pd.DataFrame(data)
    df.columns = ["dm", "mc", "jyrq", "gdMc", "cgs", "zltb", "gdSort", "gdType", "zjType"]
    df["jyrq"] = pd.to_datetime(df["jyrq"])
    return df

def read_stock_list():
    file_path = os.path.join(data_dir, "base", "gplist")
    with open(file_path, "r", encoding="utf-8") as f:
        data = json.load(f)
    df = pd.DataFrame(data)
    df.columns = ["dm", "mc", "dmHk", "mcHk", "isAH", "isSH", "isSZ"]
    return df

字段方面,dm是股票代码,mc是股票名称,jyrq是公告日期,gdMc是股东名称,cgs是持股数,zltb是占流通股比例,gdSort是股东排名,gdType是股东类型(机构/个人/基金等),zjType是增减类型(增持/减持/不变)。

系统的第一个分析模块是股东持股变动追踪。通过对比连续两个季度的十大股东数据,可以计算出每个股东的持股变化——新进、增持、减持、退出。

def analyze_holder_changes(dm):
    df = read_top10_holders(dm)
    if len(df) == 0:
        return None
    
    dates = sorted(df["jyrq"].unique())
    if len(dates) < 2:
        return None
    
    latest_date = dates[-1]
    prev_date = dates[-2]
    
    df_latest = df[df["jyrq"] == latest_date].set_index("gdMc")
    df_prev = df[df["jyrq"] == prev_date].set_index("gdMc")
    
    changes = []
    all_holders = set(df_latest.index) | set(df_prev.index)
    
    for holder in all_holders:
        latest_shares = df_latest.loc[holder, "cgs"] if holder in df_latest.index else 0
        prev_shares = df_prev.loc[holder, "cgs"] if holder in df_prev.index else 0
        
        if latest_shares == 0 and prev_shares > 0:
            action = "退出"
            change = -prev_shares
        elif latest_shares > 0 and prev_shares == 0:
            action = "新进"
            change = latest_shares
        elif latest_shares > prev_shares:
            action = "增持"
            change = latest_shares - prev_shares
        elif latest_shares < prev_shares:
            action = "减持"
            change = latest_shares - prev_shares
        else:
            action = "不变"
            change = 0
        
        if action != "不变":
            changes.append({
                "gdMc": holder,
                "prev_shares": prev_shares,
                "latest_shares": latest_shares,
                "change": change,
                "action": action,
                "latest_ratio": df_latest.loc[holder, "zltb"] if holder in df_latest.index else 0
            })
    
    df_changes = pd.DataFrame(changes)
    if len(df_changes) > 0:
        df_changes = df_changes.sort_values("change", ascending=False)
    
    return df_changes

第二个分析模块是筹码集中度计算。筹码集中度是判断股票"筹码是否集中"的重要指标。如果股东人数减少、人均持股增加,说明筹码在集中,可能有资金在吸筹;反之则说明筹码在分散。

def analyze_chip_concentration(dm):
    df = read_top10_float_holders(dm)
    if len(df) == 0:
        return None
    
    dates = sorted(df["jyrq"].unique())
    
    results = []
    for i in range(len(dates)):
        date = dates[i]
        df_period = df[df["jyrq"] == date]
        
        total_shares = df_period["cgs"].sum()
        top10_ratio = df_period["zltb"].sum()
        
        max_holder_ratio = df_period["zltb"].max()
        
        hhi = (df_period["zltb"] ** 2).sum()
        
        results.append({
            "date": date,
            "top10_ratio": top10_ratio,
            "max_holder_ratio": max_holder_ratio,
            "hhi": hhi,
            "holder_count": len(df_period)
        })
    
    df_result = pd.DataFrame(results)
    
    if len(df_result) >= 2:
        latest = df_result.iloc[-1]
        prev = df_result.iloc[-2]
        
        trend = "集中" if latest["top10_ratio"] > prev["top10_ratio"] else "分散"
        concentration_change = latest["top10_ratio"] - prev["top10_ratio"]
        
        print(f"筹码集中度趋势: {trend}")
        print(f"前十大流通股东占比变化: {concentration_change:.2f}%")
        print(f"当前前十大占比: {latest['top10_ratio']:.2f}%")
        print(f"HHI指数: {latest['hhi']:.4f}")
    
    return df_result

HHI指数(赫芬达尔指数)是衡量集中度的经典指标,值越大说明筹码越集中。

第三个分析模块是机构持股追踪。机构持股的变化往往比散户更有参考价值。我会从股东类型中筛选出机构股东,单独追踪它们的增减持行为。

def analyze_institutional_holders(dm):
    df = read_top10_float_holders(dm)
    if len(df) == 0:
        return None
    
    df_inst = df[df["gdType"].isin(["基金", "社保", "保险", "QFII", "信托", "机构"])]
    
    if len(df_inst) == 0:
        return None
    
    dates = sorted(df_inst["jyrq"].unique())
    latest_date = dates[-1]
    
    df_latest = df_inst[df_inst["jyrq"] == latest_date]
    
    total_inst_ratio = df_latest["zltb"].sum()
    inst_count = len(df_latest)
    
    print(f"机构持股数量: {inst_count}家")
    print(f"机构合计持股比例: {total_inst_ratio:.2f}%")
    print(f"机构明细:")
    print(df_latest[["gdMc", "cgs", "zltb", "gdType"]].to_string(index=False))
    
    return df_latest

第四个分析模块是增减持信号汇总。系统会把所有分析结果汇总成一个综合信号,帮助快速判断股票的筹码面是否健康。

def generate_holder_signal(dm):
    changes = analyze_holder_changes(dm)
    chip = analyze_chip_concentration(dm)
    inst = analyze_institutional_holders(dm)
    
    signal = {"dm": dm}
    
    if changes is not None and len(changes) > 0:
        new_entries = len(changes[changes["action"] == "新进"])
        increases = len(changes[changes["action"] == "增持"])
        decreases = len(changes[changes["action"] == "减持"])
        exits = len(changes[changes["action"] == "退出"])
        
        signal["new_entries"] = new_entries
        signal["increases"] = increases
        signal["decreases"] = decreases
        signal["exits"] = exits
        
        net_action = (new_entries + increases) - (decreases + exits)
        signal["net_action"] = net_action
        
        if net_action >= 2:
            signal["holder_signal"] = "积极"
        elif net_action <= -2:
            signal["holder_signal"] = "消极"
        else:
            signal["holder_signal"] = "中性"
    
    if chip is not None and len(chip) >= 2:
        trend = "集中" if chip.iloc[-1]["top10_ratio"] > chip.iloc[-2]["top10_ratio"] else "分散"
        signal["chip_trend"] = trend
    
    return signal

实际运行下来,这个系统每天可以处理几百只股票的股东数据。系统输出的信号,帮助我避开了几只大股东大幅减持的股票,也捕捉到了几只机构密集增持的机会。

在使用过程中有几点经验。第一,机构增持的信号比减持更有参考价值,因为机构增持通常需要深入研究,而减持可能只是出于资金需求。第二,筹码集中是一个缓慢的过程,通常需要2-3个季度才能确认趋势。第三,股东数据是季度披露的,有一定滞后性,需要结合其他指标(如资金流向)来验证。

做投资分析,股东数据是一个非常重要但经常被忽略的维度。用数据来追踪大股东和机构的行为,比看消息面更靠谱。

我用的数据来自本地数据引擎,十大股东、十大流通股东、持股变动等数据都有完整的接口。感兴趣的朋友可以参考这个思路,构建自己的股东监控体系。


接口说明:

  1. time/f10/sdgd/{股票代码} - 十大股东数据
    本地路径:数据存放目录/time/f10/sdgd/{dm}
    主要字段:公告日期(jyrq)、股东名称(gdMc)、持股数(cgs)、占总股本比例(zltb)、股东排名(gdSort)、股东类型(gdType)、增减类型(zjType)

  2. time/f10/sdltgd/{股票代码} - 十大流通股东数据
    本地路径:数据存放目录/time/f10/sdltgd/{dm}
    主要字段:公告日期(jyrq)、股东名称(gdMc)、持股数(cgs)、占流通股比例(zltb)、股东排名(gdSort)、股东类型(gdType)、增减类型(zjType)

  3. base/gplist - 股票列表
    本地路径:数据存放目录/base/gplist
    用于获取全市场股票代码列表

  4. time/f10/fi/{股票代码} - 财务指标
    本地路径:数据存放目录/time/f10/fi/{dm}
    用于结合基本面分析股东行为

gitee开源地址
github开源地址

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐