股东变化趋势数据挖掘:用Python追踪筹码集中度与主力动向

股东人数是判断筹码集中度的重要指标。股东人数减少意味着筹码在集中,可能有大户在吸筹;股东人数增加意味着筹码在分散,可能有大户在出货。我之前写了一套股东变化趋势分析工具,把全市场的股东户数数据系统地分析了一遍。

数据用的是股东变化趋势接口time/f10/gdbh/{股票代码},返回截止日期、股东户数、比上期变化情况。按截止日期倒序排列。配合十大股东接口time/f10/zygd/{股票代码}和十大流通股东接口time/f10/zygdlt/{股票代码},可以做更深入的分析。

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

data_dir = "D:/stock_data"

def read_gdbh(dm):
    file_path = os.path.join(data_dir, "time", "f10", "gdbh", dm)
    if not os.path.exists(file_path):
        return None
    with open(file_path, "r", encoding="utf-8") as f:
        data = json.load(f)
    df = pd.DataFrame(data)
    df.columns = ["jzrq", "gdhs", "bsqbh"]
    df["jzrq"] = pd.to_datetime(df["jzrq"])
    return df.sort_values("jzrq")

def read_zygd(dm):
    file_path = os.path.join(data_dir, "time", "f10", "zygd", dm)
    if not os.path.exists(file_path):
        return None
    with open(file_path, "r", encoding="utf-8") as f:
        data = json.load(f)
    return data

def read_zygdlt(dm):
    file_path = os.path.join(data_dir, "time", "f10", "zygdlt", dm)
    if not os.path.exists(file_path):
        return None
    with open(file_path, "r", encoding="utf-8") as f:
        data = json.load(f)
    return data

字段用简写,jzrq截止日期gdhs股东户数bsqbh比上期变化情况。十大股东和十大流通股东返回的是嵌套结构,包含截止日期、公告日期、股东总数、平均持股、十大股东列表等信息。

第一个分析:股东人数变化趋势。这是最基础的分析,看股东人数是在减少还是在增加。

def analyze_gdhs_trend(dm):
    df = read_gdbh(dm)
    if df is None or len(df) < 2:
        return None
    
    df["gdhs"] = pd.to_numeric(df["gdhs"], errors="coerce")
    
    latest = df.iloc[-1]
    prev = df.iloc[-2]
    
    change_pct = (latest["gdhs"] - prev["gdhs"]) / prev["gdhs"]
    
    recent_4 = df.tail(4)
    trend = "下降" if recent_4["gdhs"].is_monotonic_decreasing else "上升" if recent_4["gdhs"].is_monotonic_increasing else "波动"
    
    result = {
        "dm": dm,
        "latest_date": latest["jzrq"],
        "latest_gdhs": latest["gdhs"],
        "change_pct": change_pct,
        "trend": trend
    }
    
    if change_pct < -0.15:
        result["signal"] = "股东人数大幅减少,筹码集中"
        result["score"] = 70
    elif change_pct > 0.15:
        result["signal"] = "股东人数大幅增加,筹码分散"
        result["score"] = 35
    else:
        result["signal"] = "股东人数变化不大"
        result["score"] = 50
    
    return result

第二个分析:全市场筹码集中度扫描。遍历所有股票,找出股东人数连续减少的股票。

def scan_chip_concentration(top_n=30):
    gplist_path = os.path.join(data_dir, "base", "gplist")
    with open(gplist_path, "r", encoding="utf-8") as f:
        gp_list = json.load(f)
    
    results = []
    for gp in gp_list:
        dm = gp["股票代码"]
        mc = gp["股票名称"]
        
        df = read_gdbh(dm)
        if df is None or len(df) < 3:
            continue
        
        df["gdhs"] = pd.to_numeric(df["gdhs"], errors="coerce")
        
        recent_3 = df.tail(3)
        if recent_3["gdhs"].is_monotonic_decreasing:
            change_2q = (recent_3["gdhs"].iloc[-1] - recent_3["gdhs"].iloc[0]) / recent_3["gdhs"].iloc[0]
            
            results.append({
                "dm": dm,
                "mc": mc,
                "latest_gdhs": recent_3["gdhs"].iloc[-1],
                "change_2q": change_2q
            })
    
    df_result = pd.DataFrame(results).sort_values("change_2q").head(top_n)
    print("连续3期股东人数减少TOP{}:".format(top_n))
    print(df_result.to_string(index=False))
    return df_result

第三个分析:十大股东变动对比。对比相邻两个季度的十大股东名单,找出新进和退出的股东。

def compare_top10_change(dm):
    data = read_zygd(dm)
    if not data or len(data) < 2:
        return None
    
    latest = data[0]
    prev = data[1]
    
    latest_holders = set()
    for holder in latest.get("ZygdSdgd", []):
        latest_holders.add(holder.get("股东名称", ""))
    
    prev_holders = set()
    for holder in prev.get("ZygdSdgd", []):
        prev_holders.add(holder.get("股东名称", ""))
    
    new_in = latest_holders - prev_holders
    exited = prev_holders - latest_holders
    
    result = {
        "dm": dm,
        "latest_date": latest.get("截止日期", ""),
        "prev_date": prev.get("截止日期", ""),
        "new_holders": list(new_in),
        "exited_holders": list(exited)
    }
    
    institution_keywords = ["基金", "社保", "保险", "信托", "QFII", "证金", "汇金", "资管"]
    new_institutions = [h for h in new_in if any(kw in h for kw in institution_keywords)]
    
    if new_institutions:
        result["signal"] = "机构新进十大股东:{}".format("、".join(new_institutions))
        result["score"] = 72
    elif len(new_in) == 0 and len(exited) == 0:
        result["signal"] = "十大股东无变动"
        result["score"] = 55
    else:
        result["signal"] = "十大股东有变动但无机构新进"
        result["score"] = 50
    
    return result

第四个分析:结合股东人数变化和股价走势。股东人数减少+股价还没大涨的股票最值得关注。

def select_chip_concentration_strategy(top_n=20):
    gplist_path = os.path.join(data_dir, "base", "gplist")
    with open(gplist_path, "r", encoding="utf-8") as f:
        gp_list = json.load(f)
    
    candidates = []
    for gp in gp_list:
        dm = gp["股票代码"]
        mc = gp["股票名称"]
        
        df_gdbh = read_gdbh(dm)
        if df_gdbh is None or len(df_gdbh) < 2:
            continue
        
        df_gdbh["gdhs"] = pd.to_numeric(df_gdbh["gdhs"], errors="coerce")
        change_pct = (df_gdbh["gdhs"].iloc[-1] - df_gdbh["gdhs"].iloc[-2]) / df_gdbh["gdhs"].iloc[-2]
        
        if change_pct > -0.10:
            continue
        
        kline = read_kline(dm, "day")
        if kline is None or len(kline) < 60:
            continue
        
        recent_60 = kline.tail(60)
        price_change = (recent_60["收盘价"].iloc[-1] - recent_60["收盘价"].iloc[0]) / recent_60["收盘价"].iloc[0]
        
        if price_change > 0.10:
            continue
        
        candidates.append({
            "dm": dm,
            "mc": mc,
            "gdhs_change": change_pct,
            "price_change_60d": price_change
        })
    
    return pd.DataFrame(candidates).sort_values("gdhs_change").head(top_n)

策略逻辑是:股东人数减少超过10% + 60日涨幅不超过10%。前一个条件说明筹码在集中,后一个条件排除已经大涨的股票。这种"筹码集中但股价没动"的股票,后续上涨概率较高。

几个使用经验。第一,股东人数数据是季度披露的,有滞后性,只能做中长期参考。第二,股东人数减少要配合业绩看,业绩增长的筹码集中才有意义。第三,十大股东的季度变动比静态名单更有价值,要重点看机构新进。第四,基金持股接口time/f10/jjcg也可以配合使用,看基金持仓变化。

我用的数据来源是ig50的本地数据接口,股东变化趋势、十大股东、十大流通股东这些接口配合使用,能构建一个完整的筹码集中度分析系统。


接口说明:

  1. time/f10/gdbh/{股票代码} - 股东变化趋势
    本地路径:数据存放目录/time/f10/gdbh/{股票代码}
    更新频率:每天15:30(更新完成约12小时)
    数据格式:[{}…],按截止日期倒序
    主要字段:截止日期(jzrq, yyyy-MM-dd)、股东户数(gdhs)、比上期变化情况(bsqbh)

  2. time/f10/zygd/{股票代码} - 十大股东
    本地路径:数据存放目录/time/f10/zygd/{股票代码}
    更新频率:每天15:30(更新完成约12小时)
    数据格式:[{}…],按截止日期倒序
    主要字段:截止日期、公告日期、股东说明、股东总数、平均持股、十大股东列表(含排名、股东名称、持股数量、持股比例、股本性质)

  3. time/f10/zygdlt/{股票代码} - 十大流通股东
    本地路径:数据存放目录/time/f10/zygdlt/{股票代码}
    更新频率:每天15:30(更新完成约12小时)
    数据格式:[{}…],按公告日期倒序
    主要字段:截止日期、公告日期、十大流通股东列表(含排名、股东名称、持股数量、持股比例、股本性质)

gitee开源地址
github开源地址

更多推荐