#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 生成2024-2025年市场状态识别完整图表 """ import sys sys.path.insert(0, '/root/.openclaw/workspace/market-regime-identifier') import numpy as np import pandas as pd import matplotlib.pyplot as plt import matplotlib.dates as mdates from cyb50_market_classifier import fetch_cyb50_data, calculate_features, define_market_regime import pickle import warnings warnings.filterwarnings('ignore') # 设置中文字体 plt.rcParams['font.sans-serif'] = ['DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False print("="*70) print("生成2024年至今市场状态识别图表") print("="*70) # 获取数据 df = fetch_cyb50_data('2024-01-01', '2026-03-06') if df is None: exit(1) print(f"\n数据范围: {df.index[0].date()} ~ {df.index[-1].date()}") # 计算特征和标签 features = calculate_features(df) labels = define_market_regime(df, lookback=10) # 训练模型 valid_idx = ~np.isnan(labels) X = features[valid_idx] y = labels[valid_idx] from sklearn.ensemble import RandomForestClassifier clf = RandomForestClassifier( n_estimators=100, max_depth=10, min_samples_split=20, min_samples_leaf=10, random_state=42, class_weight='balanced' ) clf.fit(X, y) # 预测所有数据 states = clf.predict(X) probs = clf.predict_proba(X) # 对齐数据 df_aligned = df.iloc[-len(states):].copy() df_aligned['state'] = states df_aligned['state_prob'] = [p[s] for s, p in zip(states, probs)] df_aligned['prob_ranging'] = probs[:, 0] # 震荡概率 df_aligned['prob_trend'] = probs[:, 1] # 趋势概率 df_aligned['prob_reversal'] = probs[:, 2] # 反转概率 # 生成图表 fig, axes = plt.subplots(3, 1, figsize=(16, 12)) state_names = ['Ranging', 'Trend', 'Reversal'] colors = ['#2196F3', '#4CAF50', '#FF5722'] # 蓝、绿、橙 # 图1: 价格走势 + 状态标记 ax1 = axes[0] for i, (name, color) in enumerate(zip(state_names, colors)): mask = df_aligned['state'] == i if mask.any(): ax1.scatter(df_aligned.index[mask], df_aligned['close'][mask], c=color, label=name, alpha=0.7, s=30) ax1.plot(df_aligned.index, df_aligned['close'], 'k-', alpha=0.3, linewidth=0.5) ax1.set_ylabel('Price', fontsize=12) ax1.set_title('CYB50 Market Regime Identification 2024-2025', fontsize=14, fontweight='bold') ax1.legend(loc='upper left') ax1.grid(True, alpha=0.3) # 添加关键点位标注 for idx, row in df_aligned.iterrows(): if idx.month == 1 and idx.day == 2: # 年初 ax1.annotate(f'{row["close"]:.0f}', xy=(idx, row['close']), xytext=(10, 10), textcoords='offset points', fontsize=8, alpha=0.7) # 图2: 状态概率时间序列 ax2 = axes[1] ax2.fill_between(df_aligned.index, 0, df_aligned['prob_ranging'], alpha=0.5, label='Ranging', color=colors[0]) ax2.fill_between(df_aligned.index, df_aligned['prob_ranging'], df_aligned['prob_ranging'] + df_aligned['prob_trend'], alpha=0.5, label='Trend', color=colors[1]) ax2.fill_between(df_aligned.index, df_aligned['prob_ranging'] + df_aligned['prob_trend'], 1, alpha=0.5, label='Reversal', color=colors[2]) ax2.set_ylabel('Probability', fontsize=12) ax2.set_title('State Probability Over Time', fontsize=12) ax2.legend(loc='upper left') ax2.grid(True, alpha=0.3) ax2.set_ylim(0, 1) # 图3: 状态分布统计 ax3 = axes[2] state_counts = df_aligned['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('Days', fontsize=12) ax3.set_title('State Distribution 2024-2025', fontsize=12) # 添加数值标签 for i, (bar, count) in enumerate(zip(bars, state_counts.values)): pct = count / len(df_aligned) * 100 ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5, f'{count}d\n({pct:.1f}%)', ha='center', va='bottom', fontsize=10) plt.tight_layout() plt.savefig('/root/.openclaw/workspace/market-regime-identifier/cyb50_regime_2024_2025.png', dpi=150, bbox_inches='tight') print("\n✓ 图表已保存: cyb50_regime_2024_2025.png") # 生成详细报告 print("\n" + "="*70) print("2024-2025年详细识别结果") print("="*70) # 按月份统计 print("\n【月度统计】") print(f"{'月份':<10} {'总天数':<8} {'震荡':<8} {'趋势':<8} {'反转':<8} {'主要状态':<10}") print("-"*70) for year in [2024, 2025]: for month in range(1, 13): mask = (df_aligned.index.year == year) & (df_aligned.index.month == month) if not mask.any(): continue month_data = df_aligned[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"{year}-{month:02d} {total:<8} {ranging:<8} {trend:<8} {reversal:<8} {main_state:<10}") # 关键点位 print("\n【关键点位标注】") print(f"{'日期':<12} {'收盘价':<10} {'状态':<10} {'置信度':<10} {'说明':<20}") print("-"*70) # 每月第一个交易日 for year in [2024, 2025]: for month in range(1, 13): mask = (df_aligned.index.year == year) & (df_aligned.index.month == month) if not mask.any(): continue month_data = df_aligned[mask] first_day = month_data.iloc[0] date_str = month_data.index[0].strftime('%Y-%m-%d') price = first_day['close'] state = state_names[int(first_day['state'])] prob = first_day['state_prob'] # 简单说明 if first_day['state'] == 0: desc = 'Consolidation' elif first_day['state'] == 1: if month_data['close'].iloc[-1] > price: desc = 'Uptrend' else: desc = 'Downtrend' else: desc = 'Reversal' print(f"{date_str:<12} {price:<10.2f} {state:<10} {prob:<10.2%} {desc:<20}") print("\n" + "="*70) print("✓ 报告生成完成!") print("="*70)