龙虎榜数据深度挖掘:用Python追踪机构席位与营业部交易行为

大概是在2021年的时候,我开始关注龙虎榜数据。那时候市场上有个说法叫"龙虎榜选股,胜率翻倍",虽然有点夸张,但龙虎榜确实能反映出市场上最活跃资金的动向。后来我花了几个月时间,把龙虎榜数据系统地研究了一遍,还写了一套分析工具。这篇文章就来聊聊我是怎么用Python分析龙虎榜数据,追踪机构席位和营业部交易行为的。

先说一下龙虎榜数据有什么用。简单来说,龙虎榜是交易所每天公布的异动股票名单,包括当日涨跌幅偏离值达到7%的、换手率达到20%的、连续三个交易日涨幅偏离值累计达到20%的等等。每只上榜股票都会公布买入金额前五和卖出金额前五的营业部席位。通过分析这些席位的买卖行为,我们可以大致判断出市场上的主力资金在关注哪些股票,哪些营业部是"涨停板敢死队",哪些机构席位在默默布局。

我主要用到四块数据:第一块是每日龙虎榜详情,也就是每天有哪些股票上榜,对应的买卖席位;第二块是机构席位追踪,统计近几日机构席位在哪些股票上有交易;第三块是营业部上榜统计,看看哪些营业部最活跃;第四块是机构席位成交明细,近五个交易日机构席位的具体买卖情况。

先从数据读取开始。龙虎榜数据都在all目录下,每日详情是all/ld,机构席位追踪是all/jgzz/{n},营业部统计是all/yyb/{n},机构成交明细是all/jgcj。这里的n表示近n日,可以取5、10、30、60。

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

data_dir = "D:/stock_data"

def read_ld(date_str=None):
    file_path = os.path.join(data_dir, "all", "ld")
    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_jgzz(n=30):
    file_path = os.path.join(data_dir, "all", "jgzz", str(n))
    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 = ["dm", "mc", "ljmre", "mrCs", "ljmce", "mcCs", "je"]
    return df

def read_yyb(n=30):
    file_path = os.path.join(data_dir, "all", "yyb", str(n))
    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 = ["yybMc", "sbCs", "ljmre", "mrXw", "ljmce", "mcXw", "mrQsgp"]
    return df

def read_jgcj():
    file_path = os.path.join(data_dir, "all", "jgcj")
    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 = ["dm", "mc", "jyrq", "jgMre", "jgMce", "lx"]
    return df

字段名我还是用了简写,dm是股票代码,mc是名称,ljmre是累积买入额,mrCs是买入次数,ljmce是累积卖出额,mcCs是卖出次数,je是净额。yyb相关的字段,yybMc是营业部名称,sbCs是上榜次数,mrXw是买入席位,mcXw是卖出席位,mrQsgp是买入前三股票。jg相关的,jyrq是交易日期,jgMre是机构席位买入额,jgMce是机构席位卖出额,lx是上榜类型。

数据读进来之后,我会先做一个整体的概览分析。比如,看看最近一个月机构席位主要在买哪些股票,哪些营业部最活跃,机构席位的净买入额排名等等。这些统计可以帮我们快速把握市场主力资金的动向。

def analyze_jgzz(df_jgzz, top_n=20):
    if df_jgzz is None or len(df_jgzz) == 0:
        return None
    
    df_sorted = df_jgzz.sort_values("je", ascending=False)
    
    top_buy = df_sorted.head(top_n)[["dm", "mc", "ljmre", "ljmce", "je", "mrCs", "mcCs"]]
    top_sell = df_sorted.tail(top_n)[["dm", "mc", "ljmre", "ljmce", "je", "mrCs", "mcCs"]]
    
    print("机构席位净买入TOP{}:".format(top_n))
    print(top_buy.to_string(index=False))
    print("\n机构席位净卖出TOP{}:".format(top_n))
    print(top_sell.to_string(index=False))
    
    return top_buy, top_sell

机构席位追踪数据能告诉我们近一段时间机构在买什么卖什么,但光看这个还不够。因为机构席位的交易有时候是建仓,有时候是出货,单凭一个净额很难判断。所以我会结合机构席位成交明细来看,也就是近五个交易日每天的买卖情况。如果机构连续几天都在净买入某只股票,那信号就比较强了。

def analyze_jgcj(df_jgcj, dm):
    if df_jgcj is None or len(df_jgcj) == 0:
        return None
    
    df_stock = df_jgcj[df_jgcj["dm"] == dm].copy()
    if len(df_stock) == 0:
        return None
    
    df_stock["je"] = df_stock["jgMre"] - df_stock["jgMce"]
    df_stock = df_stock.sort_values("jyrq")
    
    print("股票{}({})近期机构席位成交明细:".format(df_stock["mc"].iloc[0], dm))
    print(df_stock[["jyrq", "jgMre", "jgMce", "je", "lx"]].to_string(index=False))
    
    total_mre = df_stock["jgMre"].sum()
    total_mce = df_stock["jgMce"].sum()
    total_je = total_mre - total_mce
    print("\n近5日机构买入合计:{}万,卖出合计:{}万,净额:{}万".format(total_mre, total_mce, total_je))
    
    return df_stock

接下来是营业部分析。龙虎榜上的营业部大致可以分几类:一类是机构专用席位,这类是基金、保险、社保这些机构在交易;一类是知名游资席位,比如东方财富证券拉萨团结路、中信证券上海溧阳路这些,它们的特点是操作频繁、擅长打板;还有一类是普通营业部,偶尔上榜。

我会先统计近一个月上榜次数最多的营业部,看看哪些最活跃。然后看这些营业部的买入额和卖出额,如果一个营业部的上榜次数多、净买入额也大,说明它比较乐观。反过来,如果上榜次数多但净卖出额大,可能就偏谨慎了。

def analyze_yyb(df_yyb, top_n=30):
    if df_yyb is None or len(df_yyb) == 0:
        return None
    
    df_yyb["je"] = df_yyb["ljmre"] - df_yyb["ljmce"]
    
    df_sorted = df_yyb.sort_values("sbCs", ascending=False)
    print("营业部上榜次数TOP{}:".format(top_n))
    print(df_sorted.head(top_n)[["yybMc", "sbCs", "ljmre", "ljmce", "je"]].to_string(index=False))
    
    df_je_sorted = df_yyb.sort_values("je", ascending=False)
    print("\n营业部净买入额TOP{}:".format(top_n))
    print(df_je_sorted.head(top_n)[["yybMc", "sbCs", "ljmre", "ljmce", "je"]].to_string(index=False))
    
    return df_sorted

在营业部分析中,我发现一个很有意思的现象:有些营业部的操作风格非常鲜明。比如东方财富证券拉萨团结路,几乎天天上榜,买卖的都是市场上最热门的题材股,而且换手率极高,典型的短线游资风格。而有些机构专用席位,上榜次数不多,但每次买卖的金额都很大,而且持有周期相对较长。

为了量化营业部的风格,我写了一个简单的分类函数,根据营业部的上榜次数、平均买卖金额、买卖的股票类型等特征,把营业部分成机构型、游资型、散户型三类。当然这个分类比较粗糙,只是一个大致的判断。

def classify_yyb_style(row):
    sbCs = row["sbCs"]
    avg_mre = row["ljmre"] / sbCs if sbCs > 0 else 0
    
    if "机构专用" in row["yybMc"]:
        return "机构型"
    elif sbCs >= 20 and avg_mre < 2000:
        return "游资型"
    else:
        return "普通型"

有了营业部风格分类之后,就可以做更有针对性的分析了。比如,我会专门跟踪机构型席位的动向,因为机构的研究能力相对更强,它们重仓买入的股票往往有基本面支撑。而游资型席位的动向,可以帮助我们判断市场的热点在哪里,哪些题材正在被炒作。

每日龙虎榜详情的分析是另一个重点。每天的龙虎榜数据包含了多种异动类型,比如涨跌幅偏离值、换手率、振幅等等。我会把每天上榜的股票按类型分类,然后看每类的上榜股票有什么共同特点。

def analyze_ld_detail(ld_data):
    if ld_data is None or len(ld_data) == 0:
        return None
    
    today_data = ld_data[0] if isinstance(ld_data, list) else ld_data
    date_str = today_data.get("日期", "")
    
    types = {
        "zpl7": "涨幅偏离值达7%",
        "dpl7": "跌幅偏离值达7%", 
        "h20": "换手率达20%",
        "z20": "连续三日涨幅偏离值累计达20%",
        "zf15": "振幅值达15%",
        "df15": "连续三日跌幅偏离值累计达20%"
    }
    
    all_stocks = []
    for key, name in types.items():
        stocks = today_data.get(key, [])
        for stk in stocks:
            stk_info = {
                "dm": stk.get("股票代码", ""),
                "mc": stk.get("股票名称", ""),
                "spj": stk.get("收盘价(元)", 0),
                "dyz": stk.get("对应值(%)", 0),
                "cjl": stk.get("成交量(万股)", 0),
                "cje": stk.get("成交额(万元)", 0),
                "lx": name
            }
            all_stocks.append(stk_info)
    
    df = pd.DataFrame(all_stocks)
    print("日期:{},今日上榜股票共{}只".format(date_str, len(df)))
    print("\n上榜类型分布:")
    print(df["lx"].value_counts())
    
    return df

把这些分析串起来,我每天盘后会做这样一个流程:先看今日龙虎榜详情,了解哪些股票异动、是什么类型的异动;然后看机构席位成交明细,看看机构在这些异动股票上的买卖情况;接着看机构席位追踪和营业部统计,掌握一段时间内的资金流向趋势;最后把这些信息综合起来,筛选出值得重点关注的股票。

我还写了一个简单的选股策略,基于龙虎榜数据。策略逻辑是这样的:首先,从近一个月的机构席位追踪数据中,选出机构净买入额排名前50的股票;然后,从这些股票中剔除ST股和近一个月涨幅超过50%的(避免追高);接着,结合营业部数据,如果有知名游资席位也在买入的话加分;最后,选出综合得分最高的10只股票,作为观察池。

def lhb_select_strategy(df_jgzz, df_yyb, df_gp, top_n=10):
    df_jg = df_jgzz.copy()
    
    df_merged = df_jg.merge(df_gp[["dm", "mc", "isSt"]], on=["dm", "mc"], how="left")
    df_merged = df_merged[df_merged["isSt"] == 0]
    
    df_merged = df_merged.sort_values("je", ascending=False)
    candidates = df_merged.head(50).copy()
    
    yyb_hot_stocks = set()
    for _, row in df_yyb.head(20).iterrows():
        if pd.notna(row["mrQsgp"]):
            for stk in row["mrQsgp"].split(","):
                yyb_hot_stocks.add(stk)
    
    candidates["yyb_hot"] = candidates["mc"].apply(lambda x: 1 if x in yyb_hot_stocks else 0)
    candidates["score"] = candidates["je"].rank(pct=True) * 0.7 + candidates["yyb_hot"] * 0.3
    
    selected = candidates.sort_values("score", ascending=False).head(top_n)
    print("龙虎榜策略选股结果(TOP{}):".format(top_n))
    print(selected[["dm", "mc", "je", "yyb_hot", "score"]].to_string(index=False))
    
    return selected

当然,这个策略只是一个基础版本,实际用的时候还要结合其他因素。比如,我会再看一下选出的股票的基本面情况,如果基本面太差,即使龙虎榜数据好看也不会买。还有就是技术面,看看股价是不是在上升趋势中,成交量有没有配合放大。

实际使用下来,龙虎榜数据分析有几个地方特别值得注意。第一,机构席位的数据比营业部数据更有参考价值,因为机构的决策更理性,研究也更深入。第二,不要只看单日的龙虎榜,要结合一段时间的趋势来看,连续多日的机构净买入信号更强。第三,龙虎榜是滞后数据,等你看到的时候股价可能已经涨了一截,所以不能盲目追涨,要结合估值和基本面来判断。

还有一个坑我踩过,就是有些营业部是"一日游"风格,今天买明天卖,如果你跟着买很容易被套。所以我后来加了一个过滤条件,就是看营业部的平均持有周期,如果持有周期太短,即使它在买入,我也不会轻易跟进。

总的来说,龙虎榜数据分析是一个非常实用的工具。它不能保证你每次都赚钱,但可以帮你更清晰地理解市场资金的动向,让你的投资决策多一个维度的参考。如果你也对龙虎榜感兴趣,建议先从跟踪机构席位开始,相对来说确定性更高一些。

我用的数据来源是ig50的本地数据接口,数据更新及时,字段也很完整,做二次开发很方便。感兴趣的朋友可以自行了解。


接口说明:

  1. all/ld - 每日龙虎榜详情
    本地路径:数据存放目录/all/ld
    主要字段:日期、涨幅偏离值达7%的证券(zpl7)、跌幅偏离值达7%的证券(dpl7)、换手率达20%的证券(h20)、连续三日涨幅偏离值累计达20%的证券(z20)、振幅值达15%的证券(zf15)等
    每只证券包含:股票代码、股票名称、收盘价(元)、对应值(%)、成交量(万股)、成交额(万元)

  2. all/jgzz/{近n日} - 机构席位追踪
    本地路径:数据存放目录/all/jgzz/{近n日}(n可取5、10、30、60)
    主要字段:股票代码(dm)、股票名称(mc)、累积买入额(万)(ljmre)、买入次数(mrCs)、累积卖出额(万)(ljmce)、卖出次数(mcCs)、净额(万)(je)

  3. all/yyb/{近n日} - 营业部上榜统计
    本地路径:数据存放目录/all/yyb/{近n日}(n可取5、10、30、60)
    主要字段:营业部名称(yybMc)、上榜次数(sbCs)、累积获取额(万)(ljmre)、买入席位(mrXw)、累积卖出额(万)(ljmce)、卖出席位(mcXw)、买入前三股票(mrQsgp)

  4. all/jgcj - 机构席位成交明细
    本地路径:数据存放目录/all/jgcj
    主要字段:股票代码(dm)、股票名称(mc)、交易日期(jyrq)、机构席位买入额(万)(jgMre)、机构席位卖出额(万)(jgMce)、类型(lx)

资料参考:ig50

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐