#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 生成30分钟级别市场状态识别图表 """ import pandas as pd import matplotlib.pyplot as plt import matplotlib.dates as mdates from datetime import datetime import warnings warnings.filterwarnings('ignore') print("="*70) print("生成30分钟市场状态识别图表") print("="*70) # 读取30分钟结果数据 df = pd.read_csv('cyb50_30min_regime_result.csv', index_col='datetime', parse_dates=True) print(f"\n数据范围: {df.index[0]} ~ {df.index[-1]}") print(f"数据条数: {len(df)}个30分钟周期") # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False # 状态名称和颜色 state_names = ['震荡', '趋势', '反转'] colors = ['#2196F3', '#4CAF50', '#FF5722'] # 蓝、绿、橙 # 创建图表 fig, axes = plt.subplots(3, 1, figsize=(20, 14)) # 图1: 价格走势 + 状态标记 ax1 = axes[0] ax1.plot(df.index, df['close'], 'k-', alpha=0.3, linewidth=0.5, label='收盘价') for i, (name, color) in enumerate(zip(state_names, colors)): mask = df['state'] == i if mask.any(): ax1.scatter(df.index[mask], df['close'][mask], c=color, label=name, alpha=0.6, s=20) ax1.set_ylabel('价格', fontsize=12) ax1.set_title('CYB50 30分钟市场状态识别 (2024-2026)', fontsize=14, fontweight='bold') ax1.legend(loc='upper left', fontsize=10) ax1.grid(True, alpha=0.3) # 格式化x轴日期 ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m')) ax1.xaxis.set_major_locator(mdates.MonthLocator(interval=2)) # 图2: 状态概率时间序列 ax2 = axes[1] ax2.fill_between(df.index, 0, df['prob_ranging'], alpha=0.5, label='震荡', color=colors[0]) ax2.fill_between(df.index, df['prob_ranging'], df['prob_ranging'] + df['prob_trend'], alpha=0.5, label='趋势', color=colors[1]) ax2.fill_between(df.index, df['prob_ranging'] + df['prob_trend'], 1, alpha=0.5, label='反转', color=colors[2]) ax2.set_ylabel('概率', fontsize=12) ax2.set_title('30分钟状态概率时间序列', fontsize=12) ax2.legend(loc='upper left', fontsize=10) ax2.grid(True, alpha=0.3) ax2.set_ylim(0, 1) ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m')) ax2.xaxis.set_major_locator(mdates.MonthLocator(interval=2)) # 图3: 状态分布统计 ax3 = axes[2] state_counts = df['state'].value_counts().sort_index() bars = ax3.bar(range(3), state_counts.values, color=colors, alpha=0.7) ax3.set_xticks(range(3)) ax3.set_xticklabels(state_names) ax3.set_ylabel('周期数', fontsize=12) ax3.set_title('30分钟状态分布统计', fontsize=12) # 添加数值标签 for i, (bar, count) in enumerate(zip(bars, state_counts.values)): pct = count / len(df) * 100 ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 20, f'{count}\n({pct:.1f}%)', ha='center', va='bottom', fontsize=11, fontweight='bold') plt.tight_layout() plt.savefig('cyb50_30min_regime_chart.png', dpi=150, bbox_inches='tight') print("\n[OK] 图表已保存: cyb50_30min_regime_chart.png") # 生成详细报告 print("\n" + "="*70) print("30分钟级别详细识别结果") print("="*70) # 按月份统计 print("\n【月度统计 - 最近6个月】") print(f"{'月份':<12} {'总周期':<8} {'震荡':<8} {'趋势':<8} {'反转':<8} {'主要状态':<10}") print("-"*70) recent_months = df[df.index >= '2024-09-01'].copy() for year_month in sorted(recent_months.index.to_period('M').unique()): mask = recent_months.index.to_period('M') == year_month month_data = recent_months[mask] total = len(month_data) ranging = (month_data['state'] == 0).sum() trend = (month_data['state'] == 1).sum() reversal = (month_data['state'] == 2).sum() main_state = state_names[month_data['state'].mode()[0]] print(f"{str(year_month):<12} {total:<8} {ranging:<8} {trend:<8} {reversal:<8} {main_state:<10}") # 最近一周详细数据 print("\n【最近一周详细数据】") print(f"{'日期时间':<20} {'收盘价':<10} {'状态':<8} {'震荡%':<8} {'趋势%':<8} {'反转%':<8}") print("-"*70) last_week = df.tail(40) # 约5个交易日 for idx, row in last_week.iterrows(): state = state_names[int(row['state'])] print(f"{str(idx):<20} {row['close']:<10.2f} {state:<8} " f"{row['prob_ranging']:<8.1%} {row['prob_trend']:<8.1%} {row['prob_reversal']:<8.1%}") print("\n" + "="*70) print("[OK] 报告生成完成!") print("="*70)