对应帖子https://quantkt.com/forumDetail?id=201169
指数14连阳,昨天刚统计完13连阳,今天开盘4083.84,收盘4085.77又是阳线。
想到大概率是新纪录了,数学意义上2的14次方=16384,相当于1/16384的概率,也好奇历史上类似行情的之后市场表现。
写个python脚本统计下历史的长阳,连续上涨的各时间区间排名,顺便看一下后续的表现。
注意我们这里只讨论阳线记录,不考虑连续上涨,连续上涨的话有个1991年的99天极端数据
(上证指数1991年10月3日的179.80点上涨到1992年2月24日的361.62点,中间每天的收盘价都要高于先前一天的收盘价, 连续上涨了99天,中间混合了一些阴线和十字星)
26年初的行情
首先用 MiniQMT维护指数数据具体过程与注意点 可以获得上证指数000001.SH的历史数据(打开miniQMT进行数据获取)MiniQMT账号见 QMT模拟端使用指南(附账号)


最终结果
【连续阳线统计(前20)】1. 连续阳线天数排名(前20): 最长连续天数:14 天(20251217 ~ 20260107),累计上涨 260.961 点 次长连续天数:12 天(19920225 ~ 19920311),累计上涨 15.64 点 前20大连续阳线天数区间详情: 第1名:14 天 | 时间:20251217 ~ 20260107 | 累计上涨:260.961 点 | 日均上涨:18.6401 点 第2名:12 天 | 时间:19920225 ~ 19920311 | 累计上涨:15.64 点 | 日均上涨:1.3033 点 第3名:11 天 | 时间:19920505 ~ 19920519 | 累计上涨:144.49 点 | 日均上涨:13.1355 点 第4名:11 天 | 时间:20060615 ~ 20060629 | 累计上涨:140.291 点 | 日均上涨:12.7537 点 第5名:11 天 | 时间:20171228 ~ 20180112 | 累计上涨:153.158 点 | 日均上涨:13.9235 点 第6名:10 天 | 时间:19901227 ~ 19910110 | 累计上涨:8.7 点 | 日均上涨:0.87 点 第7名:10 天 | 时间:19910827 ~ 19910909 | 累计上涨:17.73 点 | 日均上涨:1.773 点 第8名:9 天 | 时间:19970820 ~ 19970901 | 累计上涨:96.96 点 | 日均上涨:10.7733 点 第9名:9 天 | 时间:20070802 ~ 20070814 | 累计上涨:572.222 点 | 日均上涨:63.5802 点 第10名:9 天 | 时间:20150311 ~ 20150323 | 累计上涨:401.66 点 | 日均上涨:44.6289 点 第11名:8 天 | 时间:19910219 ~ 19910228 | 累计上涨:0.48 点 | 日均上涨:0.06 点 第12名:8 天 | 时间:19910807 ~ 19910816 | 累计上涨:11.98 点 | 日均上涨:1.4975 点 第13名:8 天 | 时间:19911009 ~ 19911018 | 累计上涨:15.02 点 | 日均上涨:1.8775 点 第14名:8 天 | 时间:19991228 ~ 20000110 | 累计上涨:199.76 点 | 日均上涨:24.97 点 第15名:8 天 | 时间:20061114 ~ 20061123 | 累计上涨:198.598 点 | 日均上涨:24.8247 点 第16名:8 天 | 时间:20070323 ~ 20070403 | 累计上涨:220.074 点 | 日均上涨:27.5092 点 第17名:8 天 | 时间:20130128 ~ 20130206 | 累计上涨:143.173 点 | 日均上涨:17.8966 点 第18名:8 天 | 时间:20140722 ~ 20140731 | 累计上涨:147.083 点 | 日均上涨:18.3854 点 第19名:8 天 | 时间:20150518 ~ 20150527 | 累计上涨:633.022 点 | 日均上涨:79.1277 点 第20名:8 天 | 时间:20191202 ~ 20191211 | 累计上涨:52.436 点 | 日均上涨:6.5545 点2. 连续阳线上涨点数排名(前20): 最大上涨点数:804.58 点(连续 3 天,19920521 ~ 19920525) 次大上涨点数:633.022 点(连续 8 天,20150518 ~ 20150527) 前20大连续阳线上涨点数区间详情: 第1名:804.58 点 | 连续天数:3 天 | 时间:19920521 ~ 19920525 | 日均上涨:268.1933 点 第2名:633.022 点 | 连续天数:8 天 | 时间:20150518 ~ 20150527 | 日均上涨:79.1277 点 第3名:574.71 点 | 连续天数:6 天 | 时间:20070927 ~ 20071011 | 日均上涨:95.785 点 第4名:572.222 点 | 连续天数:9 天 | 时间:20070802 ~ 20070814 | 日均上涨:63.5802 点 第5名:538.115 点 | 连续天数:7 天 | 时间:20070820 ~ 20070828 | 日均上涨:76.8736 点 第6名:466.051 点 | 连续天数:3 天 | 时间:20080422 ~ 20080424 | 日均上涨:155.3503 点 第7名:440.191 点 | 连续天数:3 天 | 时间:20240926 ~ 20240930 | 日均上涨:146.7303 点 第8名:401.66 点 | 连续天数:9 天 | 时间:20150311 ~ 20150323 | 日均上涨:44.6289 点 第9名:400.163 点 | 连续天数:5 天 | 时间:20070607 ~ 20070613 | 日均上涨:80.0326 点 第10名:398.088 点 | 连续天数:4 天 | 时间:20071219 ~ 20071224 | 日均上涨:99.522 点 第11名:371.364 点 | 连续天数:5 天 | 时间:20200630 ~ 20200706 | 日均上涨:74.2728 点 第12名:351.403 点 | 连续天数:1 天 | 时间:20080204 ~ 20080204 | 日均上涨:351.403 点 第13名:340.103 点 | 连续天数:5 天 | 时间:20141202 ~ 20141208 | 日均上涨:68.0206 点 第14名:334.799 点 | 连续天数:3 天 | 时间:20071026 ~ 20071030 | 日均上涨:111.5997 点 第15名:333.554 点 | 连续天数:6 天 | 时间:20151104 ~ 20151111 | 日均上涨:55.5923 点 第16名:332.536 点 | 连续天数:6 天 | 时间:20061222 ~ 20061229 | 日均上涨:55.4227 点 第17名:318.22 点 | 连续天数:6 天 | 时间:20150716 ~ 20150723 | 日均上涨:53.0367 点 第18名:310.318 点 | 连续天数:5 天 | 时间:20150421 ~ 20150427 | 日均上涨:62.0636 点 第19名:307.424 点 | 连续天数:4 天 | 时间:20070912 ~ 20070917 | 日均上涨:76.856 点 第20名:302.696 点 | 连续天数:8 天 | 时间:20240206 ~ 20240223 | 日均上涨:37.837 点【连续阴线统计(前20)】1. 连续阴线天数排名(前20): 最长连续天数:10 天(20080603 ~ 20080617),累计下跌 664.293 点 次长连续天数:9 天(19920330 ~ 19920409),累计下跌 0.88 点 前20大连续阴线天数区间详情: 第1名:10 天 | 时间:20080603 ~ 20080617 | 累计下跌:664.293 点 | 日均下跌:66.4293 点 第2名:9 天 | 时间:19920330 ~ 19920409 | 累计下跌:0.88 点 | 日均下跌:0.0978 点 第3名:8 天 | 时间:19951221 ~ 19960102 | 累计下跌:63.17 点 | 日均下跌:7.8962 点 第4名:8 天 | 时间:19990805 ~ 19990816 | 累计下跌:89.06 点 | 日均下跌:11.1325 点 第5名:8 天 | 时间:20010920 ~ 20011008 | 累计下跌:96.162 点 | 日均下跌:12.0203 点 第6名:7 天 | 时间:19920720 ~ 19920728 | 累计下跌:19.61 点 | 日均下跌:2.8014 点 第7名:7 天 | 时间:19930317 ~ 19930325 | 累计下跌:277.13 点 | 日均下跌:39.59 点 第8名:7 天 | 时间:19940330 ~ 19940407 | 累计下跌:29.6 点 | 日均下跌:4.2286 点 第9名:7 天 | 时间:19941130 ~ 19941208 | 累计下跌:43.7 点 | 日均下跌:6.2429 点 第10名:7 天 | 时间:19950912 ~ 19950920 | 累计下跌:49.31 点 | 日均下跌:7.0443 点 第11名:7 天 | 时间:19951023 ~ 19951031 | 累计下跌:8.97 点 | 日均下跌:1.2814 点 第12名:7 天 | 时间:19991216 ~ 19991227 | 累计下跌:105.705 点 | 日均下跌:15.1007 点 第13名:7 天 | 时间:20010723 ~ 20010731 | 累计下跌:259.306 点 | 日均下跌:37.0437 点 第14名:7 天 | 时间:20050628 ~ 20050706 | 累计下跌:91.095 点 | 日均下跌:13.0136 点 第15名:6 天 | 时间:19940413 ~ 19940420 | 累计下跌:125.37 点 | 日均下跌:20.895 点 第16名:6 天 | 时间:19940913 ~ 19940920 | 累计下跌:105.58 点 | 日均下跌:17.5967 点 第17名:6 天 | 时间:19960813 ~ 19960820 | 累计下跌:63.604 点 | 日均下跌:10.6007 点 第18名:6 天 | 时间:19971117 ~ 19971124 | 累计下跌:72.891 点 | 日均下跌:12.1485 点 第19名:6 天 | 时间:19990927 ~ 19991011 | 累计下跌:96.846 点 | 日均下跌:16.141 点 第20名:6 天 | 时间:19991028 ~ 19991104 | 累计下跌:71.993 点 | 日均下跌:11.9988 点2. 连续阴线下跌点数排名(前20): 最大下跌点数:866.821 点(连续 5 天,20150820 ~ 20150826) 次大下跌点数:664.293 点(连续 10 天,20080603 ~ 20080617) 前20大连续阴线下跌点数区间详情: 第1名:866.821 点 | 连续天数:5 天 | 时间:20150820 ~ 20150826 | 日均下跌:173.3642 点 第2名:664.293 点 | 连续天数:10 天 | 时间:20080603 ~ 20080617 | 日均下跌:66.4293 点 第3名:637.119 点 | 连续天数:3 天 | 时间:20150625 ~ 20150629 | 日均下跌:212.373 点 第4名:620.763 点 | 连续天数:2 天 | 时间:20080121 ~ 20080122 | 日均下跌:310.3815 点 第5名:501.309 点 | 连续天数:4 天 | 时间:20150701 ~ 20150706 | 日均下跌:125.3272 点 第6名:489.534 点 | 连续天数:2 天 | 时间:20150618 ~ 20150619 | 日均下跌:244.767 点 第7名:439.253 点 | 连续天数:2 天 | 时间:20070601 ~ 20070604 | 日均下跌:219.6265 点 第8名:426.116 点 | 连续天数:5 天 | 时间:20080220 ~ 20080226 | 日均下跌:85.2232 点 第9名:424.725 点 | 连续天数:4 天 | 时间:20071017 ~ 20071022 | 日均下跌:106.1812 点 第10名:412.244 点 | 连续天数:5 天 | 时间:20160107 ~ 20160113 | 日均下跌:82.4488 点 第11名:398.365 点 | 连续天数:2 天 | 时间:20150724 ~ 20150727 | 日均下跌:199.1825 点 第12名:368.25 点 | 连续天数:3 天 | 时间:20150505 ~ 20150507 | 日均下跌:122.75 点 第13名:360.624 点 | 连续天数:5 天 | 时间:20071031 ~ 20071106 | 日均下跌:72.1248 点 第14名:357.647 点 | 连续天数:4 天 | 时间:20180206 ~ 20180209 | 日均下跌:89.4118 点 第15名:342.394 点 | 连续天数:1 天 | 时间:20080128 ~ 20080128 | 日均下跌:342.394 点 第16名:339.321 点 | 连续天数:4 天 | 时间:19961212 ~ 19961217 | 日均下跌:84.8302 点 第17名:333.051 点 | 连续天数:4 天 | 时间:20080114 ~ 20080117 | 日均下跌:83.2628 点 第18名:321.448 点 | 连续天数:1 天 | 时间:20150528 ~ 20150528 | 日均下跌:321.448 点 第19名:309.542 点 | 连续天数:2 天 | 时间:20071121 ~ 20071122 | 日均下跌:154.771 点 第20名:299.85 点 | 连续天数:3 天 | 时间:20090827 ~ 20090831 | 日均下跌:99.95 点
最长的就是20251217到今天20260107的14连阳,新的纪录
连续上涨点数幅度,这个倒是没有进前20,整理上涨还是比较平稳的
连续阴线天数纪录
连续阴线下跌排名
同时输出阳线历史天数前20名的后续指数表现
给出一些00年后的长阳后的指数表现,00之前的那些比较久远就不展示了,如果需要输出完整的,可以自行执行代码来自由生成(代码在文末展示)
输出相应png画出后续上证指数情况

首先是本次20251217-20260107,后续未知

20060615——20060629,后续简单回调后走出牛市
20171228-20180112,后续再次爬升,之后18年熊市

20070802-20070814,后续牛市持续到6124.04,随后A杀

20150311-20150323,15年大牛市,最高达到5178.19

20061114-20061123,后续牛市

20130128-20130206

20140722-20140731,随后15年大牛市正式启动

20150518-20150527,15年牛市接近顶部位置

20191202-20191211
以上统计了历史情况,仅供参考,整体从统计上长阳确实是个好趋势,不过这次14连阳历史是新的纪录,后续拭目以待
完整代码如下
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport osimport warnings# 忽略无关警告warnings.filterwarnings('ignore')# 设置中文字体,避免乱码plt.rcParams['font.sans-serif'] = ['SimHei'] # 黑体plt.rcParams['axes.unicode_minus'] = False# 正常显示负号# 优化绘图性能plt.rcParams['agg.path.chunksize'] = 10000defload_stock_data(file_path: str) -> tuple[pd.DataFrame, str]:""" 加载股票数据并进行预处理 返回处理后的DataFrame和股票代码 """# 读取CSV文件 df = pd.read_csv(file_path)# 数据预处理 df['trade_date'] = df['trade_date'].astype(str) # 日期转字符串 df = df.sort_values(by=['trade_date']).reset_index(drop=True) # 按日期排序# 确保核心字段为数值型for col in ['close', 'open', 'high', 'low', 'change']: df[col] = pd.to_numeric(df[col], errors='coerce')# 成交量字段兼容(如果存在)if'vol'notin df.columns: df['vol'] = 0# 无成交量时填充0# 从文件名提取股票代码 stock_code = file_path.split('\\')[-1].split('_')[0] df['stock_code'] = stock_code# 添加K线序号列(便于定位区间) df['k_index'] = range(len(df))return df, stock_codedefanalyze_consecutive_candlestick(df: pd.DataFrame, stock_code: str) -> tuple[dict, pd.DataFrame]:""" 分析连续阳线/阴线趋势(阳线=收盘>开盘,阴线=收盘<开盘) 返回统计结果和带区间标记的完整DataFrame """# 标记K线类型:1=阳线(收盘>开盘),-1=阴线(收盘<开盘),0=平盘(收盘=开盘) df['candlestick'] = np.where(df['close'] > df['open'], 1, np.where(df['close'] < df['open'], -1, 0))# 识别连续阳/阴线区间 candle_intervals = [] current_candle = None start_idx = 0for idx inrange(len(df)): candle_type = df.iloc[idx]['candlestick']# 跳平平盘,结束当前连续区间if candle_type == 0:if current_candle isnotNone: end_idx = idx - 1# 计算该区间的累计涨跌 total_change = round(df.iloc[start_idx:end_idx + 1]['change'].sum(), 4) avg_daily_change = round(df.iloc[start_idx:end_idx + 1]['change'].mean(), 4)# 记录区间详细信息(包含K线索引) candle_intervals.append({'stock_code': stock_code,'candle_type': '阳线'if current_candle == 1else'阴线','start_date': df.iloc[start_idx]['trade_date'],'end_date': df.iloc[end_idx]['trade_date'],'start_k_index': df.iloc[start_idx]['k_index'],'end_k_index': df.iloc[end_idx]['k_index'],'consecutive_days': end_idx - start_idx + 1,'total_change': total_change,'avg_daily_change': avg_daily_change }) current_candle = Nonecontinue# K线类型变化,结束上一个区间,开始新区间if candle_type != current_candle:if current_candle isnotNone: end_idx = idx - 1 total_change = round(df.iloc[start_idx:end_idx + 1]['change'].sum(), 4) avg_daily_change = round(df.iloc[start_idx:end_idx + 1]['change'].mean(), 4) candle_intervals.append({'stock_code': stock_code,'candle_type': '阳线'if current_candle == 1else'阴线','start_date': df.iloc[start_idx]['trade_date'],'end_date': df.iloc[end_idx]['trade_date'],'start_k_index': df.iloc[start_idx]['k_index'],'end_k_index': df.iloc[end_idx]['k_index'],'consecutive_days': end_idx - start_idx + 1,'total_change': total_change,'avg_daily_change': avg_daily_change }) current_candle = candle_type start_idx = idx# 处理最后一个未结束的区间if current_candle isnotNone: end_idx = len(df) - 1 total_change = round(df.iloc[start_idx:end_idx + 1]['change'].sum(), 4) avg_daily_change = round(df.iloc[start_idx:end_idx + 1]['change'].mean(), 4) candle_intervals.append({'stock_code': stock_code,'candle_type': '阳线'if current_candle == 1else'阴线','start_date': df.iloc[start_idx]['trade_date'],'end_date': df.iloc[end_idx]['trade_date'],'start_k_index': df.iloc[start_idx]['k_index'],'end_k_index': df.iloc[end_idx]['k_index'],'consecutive_days': end_idx - start_idx + 1,'total_change': total_change,'avg_daily_change': avg_daily_change })# 分离阳线和阴线区间(取前20) yang_intervals = [x for x in candle_intervals if x['candle_type'] == '阳线'] yin_intervals = [x for x in candle_intervals if x['candle_type'] == '阴线']# 按连续天数降序排序(取前20) yang_by_days = sorted(yang_intervals, key=lambda x: x['consecutive_days'], reverse=True)[:20] yin_by_days = sorted(yin_intervals, key=lambda x: x['consecutive_days'], reverse=True)[:20]# 按总涨跌点数排序(阳线正序,阴线负序,取前20) yang_by_points = sorted(yang_intervals, key=lambda x: x['total_change'], reverse=True)[:20] yin_by_points = sorted(yin_intervals, key=lambda x: x['total_change'])[:20] # 阴线跌幅越大排越前 results = {'yang': {'by_days': yang_by_days, 'by_points': yang_by_points}, # 阳线'yin': {'by_days': yin_by_days, 'by_points': yin_by_points} # 阴线 }return results, dfdefprint_detailed_results(results: dict, stock_code: str):""" 打印详细的连续阳/阴线统计结果(前20,天数和点数互显) """print(f"\n{'=' * 70}")print(f"股票代码 {stock_code} 连续阳/阴线统计分析(前20名)")print(f"{'=' * 70}")# 连续阳线分析print("\n【连续阳线统计(前20)】") yang_days = results['yang']['by_days'] yang_points = results['yang']['by_points']if yang_days:# 天数统计(前20,同时显示点数)print("\n1. 连续阳线天数排名(前20):")print(f" 最长连续天数:{yang_days[0]['consecutive_days']} 天({yang_days[0]['start_date']} ~ {yang_days[0]['end_date']}),累计上涨 {yang_days[0]['total_change']} 点")iflen(yang_days) >= 2:print(f" 次长连续天数:{yang_days[1]['consecutive_days']} 天({yang_days[1]['start_date']} ~ {yang_days[1]['end_date']}),累计上涨 {yang_days[1]['total_change']} 点")print("\n 前20大连续阳线天数区间详情:")for i, interval inenumerate(yang_days, 1):print(f" 第{i}名:{interval['consecutive_days']} 天 | 时间:{interval['start_date']} ~ {interval['end_date']} | "f"累计上涨:{interval['total_change']} 点 | 日均上涨:{interval['avg_daily_change']} 点")# 点数统计(前20,同时显示天数)print("\n2. 连续阳线上涨点数排名(前20):")print(f" 最大上涨点数:{yang_points[0]['total_change']} 点(连续 {yang_points[0]['consecutive_days']} 天,{yang_points[0]['start_date']} ~ {yang_points[0]['end_date']})")iflen(yang_points) >= 2:print(f" 次大上涨点数:{yang_points[1]['total_change']} 点(连续 {yang_points[1]['consecutive_days']} 天,{yang_points[1]['start_date']} ~ {yang_points[1]['end_date']})")print("\n 前20大连续阳线上涨点数区间详情:")for i, interval inenumerate(yang_points, 1):print(f" 第{i}名:{interval['total_change']} 点 | 连续天数:{interval['consecutive_days']} 天 | "f"时间:{interval['start_date']} ~ {interval['end_date']} | 日均上涨:{interval['avg_daily_change']} 点")else:print(" 无连续阳线记录")# 连续阴线分析print("\n【连续阴线统计(前20)】") yin_days = results['yin']['by_days'] yin_points = results['yin']['by_points']if yin_days:# 天数统计(前20,同时显示点数)print("\n1. 连续阴线天数排名(前20):")print(f" 最长连续天数:{yin_days[0]['consecutive_days']} 天({yin_days[0]['start_date']} ~ {yin_days[0]['end_date']}),累计下跌 {abs(yin_days[0]['total_change'])} 点")iflen(yin_days) >= 2:print(f" 次长连续天数:{yin_days[1]['consecutive_days']} 天({yin_days[1]['start_date']} ~ {yin_days[1]['end_date']}),累计下跌 {abs(yin_days[1]['total_change'])} 点")print("\n 前20大连续阴线天数区间详情:")for i, interval inenumerate(yin_days, 1):print(f" 第{i}名:{interval['consecutive_days']} 天 | 时间:{interval['start_date']} ~ {interval['end_date']} | "f"累计下跌:{abs(interval['total_change'])} 点 | 日均下跌:{abs(interval['avg_daily_change'])} 点")# 点数统计(前20,同时显示天数)print("\n2. 连续阴线下跌点数排名(前20):")print(f" 最大下跌点数:{abs(yin_points[0]['total_change'])} 点(连续 {yin_points[0]['consecutive_days']} 天,{yin_points[0]['start_date']} ~ {yin_points[0]['end_date']})")iflen(yin_points) >= 2:print(f" 次大下跌点数:{abs(yin_points[1]['total_change'])} 点(连续 {yin_points[1]['consecutive_days']} 天,{yin_points[1]['start_date']} ~ {yin_points[1]['end_date']})")print("\n 前20大连续阴线下跌点数区间详情:")for i, interval inenumerate(yin_points, 1):print(f" 第{i}名:{abs(interval['total_change'])} 点 | 连续天数:{interval['consecutive_days']} 天 | "f"时间:{interval['start_date']} ~ {interval['end_date']} | 日均下跌:{abs(interval['avg_daily_change'])} 点")else:print(" 无连续阴线记录")defplot_single_histogram(data: list, title: str, y_label: str, save_name: str, is_days: bool = True, is_yang: bool = True):""" 生成单个直方图(前20,分开生成四个图表) """# 创建画布(缩小尺寸,避免布局问题) plt.figure(figsize=(14, 8))if data:# ========== 核心修改部分 ==========# X轴标签:所有名次都显示 排名 + 时间区间 x_labels = []for i, interval inenumerate(data):# 拼接排名和时间区间,用换行分隔避免拥挤 label = f"第{i + 1}名\n{interval['start_date']}\n~{interval['end_date']}" x_labels.append(label)# ========== 修改结束 ==========# Y轴数值if is_days: y_values = [interval['consecutive_days'] for interval in data]# 标注文本:天数+对应点数 annot_text = [f"{v}天\n({interval['total_change'] if is_yang elseabs(interval['total_change'])}点)"for v, interval inzip(y_values, data)]else: y_values = [interval['total_change'] if is_yang elseabs(interval['total_change']) for interval in data]# 标注文本:点数+对应天数 annot_text = [f"{v:.2f}点\n({interval['consecutive_days']}天)"for v, interval inzip(y_values, data)]# 设置颜色 color = '#FF6B6B'if (is_yang and is_days) else'#4ECDC4'if (is_yang andnot is_days) else \'#95A5A6'if (not is_yang and is_days) else'#F39C12'# 绘制柱状图 bars = plt.bar(x_labels, y_values, color=color, alpha=0.8, edgecolor='black', linewidth=1)# 在柱子上标注(天数+点数/点数+天数)for bar, text inzip(bars, annot_text): height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height + (height * 0.01), text, ha='center', va='bottom', fontsize=9, fontweight='bold')# 设置Y轴范围(留出标注空间) plt.ylim(0, max(y_values) * 1.15)# ========== 新增优化:调整X轴标签字体大小,防止重叠 ========== plt.xticks(rotation=0, fontsize=7)# ========== 优化结束 ==========else:# 无数据时显示提示 plt.text(0.5, 0.5, '无相关数据', ha='center', va='center', transform=plt.gca().transAxes, fontsize=14, fontweight='bold')# 设置图表样式(简化元素) plt.title(title, fontsize=14, fontweight='bold', pad=15) plt.ylabel(y_label, fontsize=11, fontweight='bold') plt.xlabel('排名', fontsize=11, fontweight='bold') plt.xticks(rotation=0, fontsize=9) plt.grid(axis='y', linestyle='--', alpha=0.7)# 手动调整边距,避免tight_layout警告 plt.subplots_adjust(left=0.08, right=0.95, top=0.9, bottom=0.12)# 保存图片(自动创建目录) save_dir = 'stock_charts/summary'ifnot os.path.exists(save_dir): os.makedirs(save_dir) save_path = os.path.join(save_dir, save_name) plt.savefig(save_path, dpi=200, bbox_inches='tight')print(f"汇总图表已保存:{save_path}")# 关闭图表,不显示(避免卡顿) plt.close()defplot_yang_interval_detail(df: pd.DataFrame, interval: dict, rank: int, stock_code: str):""" 为单个连续阳线区间生成详情图(优化布局,解决卡顿问题) """# 确定绘图的K线范围 start_idx = interval['start_k_index'] end_idx = interval['end_k_index']# 结束后再往后数100根K线(如果不足则取到最后) plot_end_idx = min(end_idx + 100, len(df) - 1)# 提取绘图数据 plot_df = df.iloc[start_idx:plot_end_idx + 1].copy()iflen(plot_df) == 0:print(f"第{rank}名阳线区间无足够K线数据,跳过绘图")return# 简化X轴标签(使用相对序号) plot_df['rel_index'] = range(len(plot_df)) yang_end_rel_idx = end_idx - start_idx # 阳线区间结束的相对位置# 创建画布(缩小尺寸,优化布局) fig, ax1 = plt.subplots(1, 1, figsize=(14, 7))# 设置标题 fig.suptitle(f'{stock_code} 连续阳线详情(第{rank}名)| 连续{interval["consecutive_days"]}天 | 累计涨{interval["total_change"]}点', fontsize=12, fontweight='bold', y=0.98)# 绘制收盘价走势 ax1.plot(plot_df['rel_index'], plot_df['close'], color='#1f77b4', linewidth=1.5, label='收盘价')# 绘制高低价区间(简化填充) ax1.fill_between(plot_df['rel_index'], plot_df['low'], plot_df['high'], alpha=0.1, color='gray')# 标记阳线区间(简化样式) ax1.axvspan(0, yang_end_rel_idx, alpha=0.2, color='red', label='连续阳线区间') ax1.axvline(x=yang_end_rel_idx, color='red', linestyle='--', alpha=0.8, linewidth=1, label='阳线结束')# 简化标注 ax1.text(5, plot_df['close'].max() * 0.98,f'阳线开始:{interval["start_date"]}', fontsize=9, color='red', ha='left', va='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8)) ax1.text(yang_end_rel_idx + 5, plot_df['close'].iloc[yang_end_rel_idx],f'阳线结束:{interval["end_date"]}', fontsize=9, color='red', ha='left', va='center', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))# 简化坐标轴 ax1.set_ylabel('价格', fontsize=10, fontweight='bold') ax1.set_xlabel(f'K线序号(从阳线开始,共{len(plot_df)}根)', fontsize=10, fontweight='bold') ax1.grid(True, linestyle='--', alpha=0.5, linewidth=0.8) ax1.legend(loc='upper left', fontsize=9) ax1.set_xlim(0, len(plot_df) - 1)# 手动调整边距,彻底避免tight_layout plt.subplots_adjust(left=0.06, right=0.96, top=0.92, bottom=0.1)# 保存图片(降低分辨率,提升速度) save_dir = f'stock_charts/yang_detail'ifnot os.path.exists(save_dir): os.makedirs(save_dir) save_name = f'{stock_code}_阳线第{rank}名_{interval["start_date"]}_{interval["end_date"]}.png' save_path = os.path.join(save_dir, save_name) plt.savefig(save_path, dpi=200, bbox_inches='tight')print(f"阳线详情图{rank}已保存:{save_path}")# 强制关闭画布,释放内存 plt.close(fig) plt.close('all')defplot_four_summary_charts(results: dict, stock_code: str):""" 生成四个汇总直方图(前20) """print("\n开始生成汇总统计图表...")# 1. 连续阳线天数前20 plot_single_histogram( data=results['yang']['by_days'], title=f'{stock_code} 连续阳线天数前20', y_label='连续天数(天)', save_name=f'{stock_code}_阳线天数前20.png', is_days=True, is_yang=True )# 2. 连续阳线点数前20 plot_single_histogram( data=results['yang']['by_points'], title=f'{stock_code} 连续阳线上涨点数前20', y_label='累计上涨点数', save_name=f'{stock_code}_阳线点数前20.png', is_days=False, is_yang=True )# 3. 连续阴线天数前20 plot_single_histogram( data=results['yin']['by_days'], title=f'{stock_code} 连续阴线天数前20', y_label='连续天数(天)', save_name=f'{stock_code}_阴线天数前20.png', is_days=True, is_yang=False )# 4. 连续阴线点数前20 plot_single_histogram( data=results['yin']['by_points'], title=f'{stock_code} 连续阴线下跌点数前20', y_label='累计下跌点数(绝对值)', save_name=f'{stock_code}_阴线点数前20.png', is_days=False, is_yang=False )print("汇总图表生成完成!")defplot_all_yang_detail_charts(df: pd.DataFrame, yang_intervals: list, stock_code: str):""" 为前20名连续阳线区间生成详情图(批量生成,避免卡顿) """ifnot yang_intervals:print("\n无连续阳线数据,跳过详情图生成")returnprint(f"\n开始生成前{min(len(yang_intervals), 20)}名连续阳线区间的详情图...")# 限制最多生成20张,避免过载for i, interval inenumerate(yang_intervals[:20], 1):try: plot_yang_interval_detail(df, interval, i, stock_code)except Exception as e:print(f"生成第{i}名阳线详情图失败:{str(e)}")continueprint("阳线详情图生成完成!")defmain():""" 主函数:执行完整的阳/阴线分析和可视化流程 """# 设置准确的文件路径 file_path = r"D:\quantkt\data\stock\index_daily_data\000001.SH_daily.csv"try:# 1. 加载数据print("正在加载数据...") df, stock_code = load_stock_data(file_path)print(f"数据加载完成,{stock_code} 共 {len(df)} 行记录")# 2. 分析连续阳/阴线趋势(前20)print("\n正在分析连续阳/阴线情况(统计前20)...") results, df_with_index = analyze_consecutive_candlestick(df, stock_code)# 3. 打印详细统计结果 print_detailed_results(results, stock_code)# 4. 生成四个汇总直方图(前20) plot_four_summary_charts(results, stock_code)# 5. 为前20名连续阳线区间生成详情图 yang_top20_by_days = results['yang']['by_days'] plot_all_yang_detail_charts(df_with_index, yang_top20_by_days, stock_code)print("\n所有分析和可视化任务完成!")except FileNotFoundError:print(f"错误:找不到文件 {file_path},请检查文件路径是否正确")except Exception as e:print(f"执行错误:{str(e)}")if __name__ == "__main__": main()各类代码教程与视频演示放于本人长期维护的量化平台 https://quantkt.com
现阶段粉丝开户福利(万0.8,etf万0.5)解锁平台会员,获取所有量化教程与框架(25年末活动,26年1月后下架此活动)

