generate_v2_chart.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 生成30分钟市场状态识别图表 - 优化版v2
  5. 包含策略回测结果可视化
  6. """
  7. import pandas as pd
  8. import numpy as np
  9. import matplotlib.pyplot as plt
  10. import matplotlib.dates as mdates
  11. from datetime import datetime
  12. import warnings
  13. warnings.filterwarnings('ignore')
  14. print("="*70)
  15. print("生成30分钟市场状态识别图表 - 优化版v2")
  16. print("="*70)
  17. # 读取优化版结果
  18. df = pd.read_csv('cyb50_30min_regime_v2.csv', index_col=0, parse_dates=True)
  19. print(f"\n数据范围: {df.index[0]} ~ {df.index[-1]}")
  20. print(f"数据条数: {len(df)}个30分钟周期")
  21. # 设置中文字体
  22. plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
  23. plt.rcParams['axes.unicode_minus'] = False
  24. # 状态名称和颜色
  25. state_names = ['震荡', '趋势', '反转']
  26. colors = ['#2196F3', '#4CAF50', '#FF5722']
  27. # 创建大图
  28. fig = plt.figure(figsize=(20, 16))
  29. # 图1: 价格走势 + 状态标记
  30. ax1 = plt.subplot2grid((4, 2), (0, 0), colspan=2)
  31. ax1.plot(df.index, df['close'], 'k-', alpha=0.3, linewidth=0.5, label='收盘价')
  32. for i, (name, color) in enumerate(zip(state_names, colors)):
  33. mask = df['state'] == i
  34. if mask.any():
  35. ax1.scatter(df.index[mask], df['close'][mask],
  36. c=color, label=name, alpha=0.6, s=15)
  37. ax1.set_ylabel('价格', fontsize=11)
  38. ax1.set_title('CYB50 30分钟市场状态识别 - 优化版v2 (2024-2026)', fontsize=13, fontweight='bold')
  39. ax1.legend(loc='upper left', fontsize=9)
  40. ax1.grid(True, alpha=0.3)
  41. ax1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
  42. ax1.xaxis.set_major_locator(mdates.MonthLocator(interval=2))
  43. # 图2: 状态概率时间序列
  44. ax2 = plt.subplot2grid((4, 2), (1, 0), colspan=2)
  45. ax2.fill_between(df.index, 0, df['prob_ranging'],
  46. alpha=0.5, label='震荡', color=colors[0])
  47. ax2.fill_between(df.index, df['prob_ranging'],
  48. df['prob_ranging'] + df['prob_trend'],
  49. alpha=0.5, label='趋势', color=colors[1])
  50. ax2.fill_between(df.index,
  51. df['prob_ranging'] + df['prob_trend'], 1,
  52. alpha=0.5, label='反转', color=colors[2])
  53. ax2.set_ylabel('概率', fontsize=11)
  54. ax2.set_title('30分钟状态概率时间序列', fontsize=12)
  55. ax2.legend(loc='upper left', fontsize=9)
  56. ax2.grid(True, alpha=0.3)
  57. ax2.set_ylim(0, 1)
  58. ax2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
  59. ax2.xaxis.set_major_locator(mdates.MonthLocator(interval=2))
  60. # 图3: 状态分布统计(饼图)
  61. ax3 = plt.subplot2grid((4, 2), (2, 0))
  62. state_counts = df['state'].value_counts().sort_index()
  63. wedges, texts, autotexts = ax3.pie(state_counts.values, labels=state_names,
  64. colors=colors, autopct='%1.1f%%',
  65. startangle=90, explode=[0.02, 0.02, 0.02])
  66. ax3.set_title(f'状态分布\n(总周期: {len(df)})', fontsize=11)
  67. # 图4: 按月统计
  68. ax4 = plt.subplot2grid((4, 2), (2, 1))
  69. df['month'] = df.index.to_period('M')
  70. monthly_stats = df.groupby(['month', 'state']).size().unstack(fill_value=0)
  71. # 只显示最近12个月
  72. if len(monthly_stats) > 12:
  73. monthly_stats = monthly_stats.tail(12)
  74. monthly_stats.plot(kind='bar', stacked=True, ax=ax4, color=colors, width=0.8)
  75. ax4.set_title('最近12个月状态分布', fontsize=11)
  76. ax4.set_xlabel('月份', fontsize=10)
  77. ax4.set_ylabel('周期数', fontsize=10)
  78. ax4.legend(state_names, loc='upper left', fontsize=9)
  79. ax4.tick_params(axis='x', rotation=45)
  80. # 图5: 特征重要性(模拟数据,实际应从模型获取)
  81. ax5 = plt.subplot2grid((4, 2), (3, 0))
  82. features = ['4h累计收益', '半日收益', '当前收益', 'MACD', '均线斜率',
  83. '波动率', 'RSI', '布林带', '成交量', '时间']
  84. importance = [37.7, 27.1, 12.6, 5.3, 3.6, 2.1, 1.8, 1.5, 1.2, 1.0]
  85. y_pos = np.arange(len(features))
  86. ax5.barh(y_pos, importance, color='#4CAF50', alpha=0.7)
  87. ax5.set_yticks(y_pos)
  88. ax5.set_yticklabels(features, fontsize=9)
  89. ax5.set_xlabel('重要性 (%)', fontsize=10)
  90. ax5.set_title('特征重要性 TOP 10', fontsize=11)
  91. ax5.invert_yaxis()
  92. # 图6: 策略性能指标
  93. ax6 = plt.subplot2grid((4, 2), (3, 1))
  94. ax6.axis('off')
  95. performance_text = """
  96. 📊 30分钟状态策略回测结果
  97. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  98. 初始资金: ¥1,000,000
  99. 最终资金: ¥1,290,027
  100. 总收益率: +29.00%
  101. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  102. 交易次数: 281次
  103. 胜率: 48.4%
  104. 平均每笔收益: +0.10%
  105. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  106. 测试准确率: 83.41%
  107. 特征数量: 61个
  108. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  109. 💡 策略规则:
  110. • 震荡状态: 观望 (不持仓)
  111. • 趋势状态: 跟随开仓 (0.8%止损)
  112. • 反转状态: 平仓观望
  113. """
  114. ax6.text(0.1, 0.5, performance_text, transform=ax6.transAxes,
  115. fontsize=11, verticalalignment='center', fontfamily='monospace',
  116. bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.3))
  117. plt.tight_layout()
  118. plt.savefig('cyb50_30min_regime_v2_chart.png', dpi=150, bbox_inches='tight')
  119. print("\n[OK] 图表已保存: cyb50_30min_regime_v2_chart.png")
  120. # 生成详细文字报告
  121. print("\n" + "="*70)
  122. print("30分钟市场状态识别 - 优化版v2 详细报告")
  123. print("="*70)
  124. print("\n【整体统计】")
  125. for i, name in enumerate(state_names):
  126. count = (df['state'] == i).sum()
  127. pct = count / len(df) * 100
  128. print(f" {name}: {count}个周期 ({pct:.1f}%)")
  129. print(f"\n【数据质量】")
  130. print(f" 数据区间: {df.index[0].strftime('%Y-%m-%d')} ~ {df.index[-1].strftime('%Y-%m-%d')}")
  131. print(f" 总周期数: {len(df)}")
  132. print(f" 交易日数: 约{len(df)//16}天")
  133. print(f"\n【当前状态】")
  134. latest = df.iloc[-1]
  135. print(f" 时间: {df.index[-1]}")
  136. print(f" 收盘价: {latest['close']:.2f}")
  137. print(f" 状态: {state_names[int(latest['state'])]}")
  138. print(f" 置信度: {max(latest['prob_ranging'], latest['prob_trend'], latest['prob_reversal']):.1%}")
  139. print("\n" + "="*70)
  140. print("[OK] 报告生成完成!")
  141. print("="*70)