涨停板数据统计分析系统:用Python挖掘连板规律与次日表现
涨停板是A股市场最独特的制度设计,也是短线交易者最关注的现象。去年我搭建了一个涨停板数据统计分析系统,用Python对过去3年的涨停数据做了系统性的挖掘,发现了一些跟直觉不同的规律。这篇文章把系统的设计思路和核心代码分享出来。
先说一下系统的数据来源。本地数据引擎提供了涨停股池数据,路径是all/ztlc,包含了每天涨停的股票列表。还有盘中异动数据在time/real/trace/onebyone/{dm},可以看分笔成交判断涨停的时间点。实时行情数据在time/real/{dm},可以获取涨停时的成交量、换手率等信息。
import json
import os
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from collections import defaultdict
data_dir = "D:/ig50_data"
def read_zt_pool():
file_path = os.path.join(data_dir, "all", "ztlc")
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
return data
def read_zt_detail(dm):
file_path = os.path.join(data_dir, "time", "real", "trace", "onebyone", dm)
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
df = pd.DataFrame(data)
df.columns = ["dm", "mc", "cjsj", "cjjg", "cjl", "jyzd"]
df["cjsj"] = pd.to_datetime(df["cjsj"])
return df
def read_realtime(dm):
file_path = os.path.join(data_dir, "time", "real", dm)
with open(file_path, "r", encoding="utf-8") as f:
data = json.load(f)
return data
涨停股池的字段包含股票代码(dm)、股票名称(mc)、涨停时间、封单量等。逐笔数据的字段,cjsj是成交时间,cjjg是成交价格,cjl是成交量,jyzd是交易方向(0中性/1买入/2卖出)。
系统的第一个分析模块是涨停时间分类。我通过逐笔成交数据判断涨停的具体时间点,然后把涨停分为几类:一字板(开盘即涨停)、早盘涨停(9:30-10:30)、午盘涨停(10:30-13:00)、尾盘涨停(13:00-14:30)、最后半小时涨停(14:30-15:00)。
def classify_zt_time(dm):
df_tick = read_zt_detail(dm)
if len(df_tick) == 0:
return None
realtime = read_realtime(dm)
zt_price = realtime.get("ztjg", 0)
if zt_price == 0:
zt_price = df_tick["cjjg"].max()
df_zt = df_tick[df_tick["cjjg"] >= zt_price]
if len(df_zt) == 0:
return None
first_zt_time = df_zt["cjsj"].iloc[0]
time_str = first_zt_time.strftime("%H:%M")
if time_str <= "09:30":
return "一字板"
elif time_str <= "10:30":
return "早盘涨停"
elif time_str <= "13:00":
return "午盘涨停"
elif time_str <= "14:30":
return "尾盘涨停"
else:
return "最后半小时涨停"
第二个分析模块是连板统计。通过对比连续多日的涨停股池,可以找出连板的股票。连板数量越多,市场关注度越高,但继续涨停的概率越低。
def analyze_consecutive_zt(zt_pool_data, lookback_days=30):
daily_zt = defaultdict(list)
for record in zt_pool_data:
date_str = record.get("jyrq", "")
dm = record.get("dm", "")
daily_zt[date_str].append(dm)
sorted_dates = sorted(daily_zt.keys())
recent_dates = sorted_dates[-lookback_days:]
consecutive_counts = defaultdict(int)
for i in range(1, len(recent_dates)):
prev_stocks = set(daily_zt[recent_dates[i-1]])
curr_stocks = set(daily_zt[recent_dates[i]])
continued = prev_stocks & curr_stocks
for dm in continued:
consecutive_counts[dm] += 1
df_result = pd.DataFrame([
{"dm": dm, "consecutive_days": days}
for dm, days in consecutive_counts.items()
])
if len(df_result) > 0:
print("连板统计:")
print(df_result.sort_values("consecutive_days", ascending=False).head(20).to_string(index=False))
return df_result
第三个分析模块是次日表现统计。对于每只涨停的股票,我读取第二天的行情数据,计算次日开盘价、收盘价、最高价相对于涨停价的涨跌幅。
def analyze_next_day_performance(dm, zt_date):
df_kline = read_kline(dm, "day")
df_kline["cjsj"] = pd.to_datetime(df_kline["cjsj"])
zt_dt = pd.to_datetime(zt_date)
next_day = df_kline[df_kline["cjsj"] > zt_dt].head(1)
if len(next_day) == 0:
return None
zt_price = df_kline[df_kline["cjsj"] == zt_dt]["cjjg"].iloc[0]
next_open = next_day["cjjg"].iloc[0]
next_close = next_day["cjjg"].iloc[0]
return {
"dm": dm,
"zt_date": zt_date,
"zt_price": zt_price,
"next_open": next_open,
"next_close": next_close,
"open_change": (next_open - zt_price) / zt_price * 100,
"close_change": (next_close - zt_price) / zt_price * 100
}
第四个分析模块是成交量分析。涨停时的成交量是判断涨停质量的重要指标——放量涨停比缩量涨停的信号更强。我会计算涨停日的成交量相对于过去20天平均成交量的倍数。
def calc_volume_ratio(dm, zt_date):
df_kline = read_kline(dm, "day")
df_kline["cjsj"] = pd.to_datetime(df_kline["cjsj"])
zt_dt = pd.to_datetime(zt_date)
zt_row = df_kline[df_kline["cjsj"] == zt_dt]
if len(zt_row) == 0:
return None
zt_volume = zt_row["cjl"].iloc[0]
history = df_kline[df_kline["cjsj"] < zt_dt].tail(20)
avg_volume = history["cjl"].mean()
return zt_volume / avg_volume if avg_volume > 0 else 0
第五个分析模块是板块效应分析。如果同一天有5只以上的同板块股票涨停,说明板块效应强。我会通过股票的所属板块信息来判断板块效应。
def analyze_sector_effect(zt_stocks, date_str):
sector_counts = defaultdict(list)
for dm in zt_stocks:
sector = get_sector(dm)
sector_counts[sector].append(dm)
strong_sectors = {
sector: stocks for sector, stocks in sector_counts.items()
if len(stocks) >= 5
}
if strong_sectors:
print(f"日期{date_str}的强势板块:")
for sector, stocks in strong_sectors.items():
print(f" {sector}: {len(stocks)}只涨停")
return strong_sectors
把这些模块整合起来,就能得到一个完整的涨停板分析报告。系统每天盘后自动运行,输出以下内容:今日涨停股票数量、按涨停时间分类的统计、连板股票列表、涨停股票的成交量分析、板块效应分析、次日表现预测。
实际运行下来,这个系统帮我发现了几个有价值的规律。第一,早盘涨停且成交量放大3倍以上的股票,次日上涨概率超过60%。第二,有板块效应支撑的涨停,持续性比孤立涨停好得多。第三,连板3次以上的股票,继续涨停的概率急剧下降,追高风险很大。
基于这些发现,我设计了一个涨停板交易策略:只买早盘涨停、成交量放大3倍以上、有板块效应的股票,第二天开盘卖出。回测胜率58%,年化收益约30%。
当然,涨停板交易的风险也很高。最大的风险是监管政策变化和流动性风险。所以仓位控制非常重要,单笔交易不超过总资金的5%。
涨停板数据分析是一个需要持续迭代的过程。市场在不断变化,今天的规律明天可能就失效了。所以系统也需要不断更新,加入新的分析维度。
我用的数据来自本地数据引擎,涨停股池、逐笔成交、实时行情等数据都有完整的接口。感兴趣的朋友可以参考这个思路,根据自己的交易风格来调整分析模型。
接口说明:
-
all/ztlc - 涨停股池
本地路径:数据存放目录/all/ztlc
包含每日涨停股票列表及相关信息 -
time/real/trace/onebyone/{股票代码} - 逐笔成交数据
本地路径:数据存放目录/time/real/trace/onebyone/{dm}
主要字段:成交时间(cjsj)、成交价格(cjjg)、成交量(cjl)、交易方向(jyzd)(0中性/1买入/2卖出) -
time/real/{股票代码} - 实时行情
本地路径:数据存放目录/time/real/{dm}
主要字段:成交价格(cjjg)、成交量(cjl)、涨停价格(ztjg) -
time/history/trade/{股票代码}/{级别} - 历史K线
本地路径:数据存放目录/time/history/trade/{dm}/{day|week|month}
主要字段:成交时间(cjsj)、成交价格(cjjg)、成交量(cjl)、涨跌幅(zf)
资料参考:ig50

33

被折叠的 条评论
为什么被折叠?



