#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ CYB50市场状态识别 - 每日邮件发送脚本 数据范围: 2024年至今 发送时间: 每天15:10 """ import sys sys.path.insert(0, '/root/.openclaw/workspace/market-regime-identifier') import numpy as np import pandas as pd from cyb50_market_classifier import fetch_cyb50_data, calculate_features, define_market_regime from sklearn.ensemble import RandomForestClassifier import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase from email.header import Header from email import encoders from datetime import datetime import warnings warnings.filterwarnings('ignore') print("="*60) print(f"CYB50每日市场状态报告 - {datetime.now().strftime('%Y-%m-%d %H:%M')}") print("="*60) # 获取数据 df = fetch_cyb50_data('2024-01-01', '2026-12-31') if df is None: print("❌ 数据获取失败") exit(1) # 计算特征和标签 features = calculate_features(df) labels = define_market_regime(df, lookback=10) # 训练模型 valid_idx = ~np.isnan(labels) X = features[valid_idx] y = labels[valid_idx] 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['prob_ranging'] = probs[:, 0] df_aligned['prob_trend'] = probs[:, 1] df_aligned['prob_reversal'] = probs[:, 2] # 获取最近365天 last_365 = df_aligned.tail(365).copy() last_365['change'] = last_365['close'].pct_change() * 100 # 获取最新数据 today = df_aligned.iloc[-1] yesterday = df_aligned.iloc[-2] if len(df_aligned) > 1 else today state_names = ['震荡', '趋势', '反转'] colors = ['#2196F3', '#4CAF50', '#FF5722'] state_name = state_names[int(today['state'])] state_color = colors[int(today['state'])] # 生成365天详细数据表格 html_rows = "" for idx, row in last_365.iterrows(): s = int(row['state']) change = row['change'] if not pd.isna(row['change']) else 0 change_str = f"{change:+.2f}%" if change != 0 else "-" change_color = "green" if change > 0 else "red" if change < 0 else "gray" # 高亮最新一天 highlight = 'style="background: #fff3cd; font-weight: bold;"' if idx == df_aligned.index[-1] else '' html_rows += f""" {idx.strftime('%y-%m-%d')} {row['close']:.2f} {state_names[s]} {row['prob_ranging']:.1%} {row['prob_trend']:.1%} {row['prob_reversal']:.1%} {change_str} """ # 计算涨跌 daily_change = today['close'] - yesterday['close'] daily_change_pct = daily_change / yesterday['close'] * 100 # 计算区间涨跌 range_change_pct = (today['close'] / last_365['close'].iloc[0] - 1) * 100 # 邮件内容 html = f"""

📊 CYB50每日市场状态报告

📈 今日状态 ({df_aligned.index[-1].strftime('%Y-%m-%d')})

收盘价: {today['close']:.2f}

日涨跌: {daily_change:+.2f} ({daily_change_pct:+.2f}%)

市场状态: {state_name}

状态概率: 震荡 {today['prob_ranging']:.1%} / 趋势 {today['prob_trend']:.1%} / 反转 {today['prob_reversal']:.1%}

📊 最近365天统计 (2024-至今)

365天前价格: {last_365['close'].iloc[0]:.2f}

区间涨跌: {range_change_pct:+.2f}%

最高价: {last_365['close'].max():.2f} ({last_365['close'].idxmax().strftime('%m-%d')}) / 最低价: {last_365['close'].min():.2f} ({last_365['close'].idxmin().strftime('%m-%d')})


状态分布: 🟦 震荡 {(last_365['state']==0).sum()}天 / 🟩 趋势 {(last_365['state']==1).sum()}天 / 🟧 反转 {(last_365['state']==2).sum()}天

📋 最近365天详细数据

🟦 震荡 🟩 趋势 🟧 反转 (黄色背景 = 最新)

{html_rows}
日期 收盘价 状态 震荡概率 趋势概率 反转概率 日涨跌

生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}
数据更新至: {df_aligned.index[-1].strftime('%Y-%m-%d')}
模型准确率: 72.10% | 创业板50指数 (sz399673)

""" # 发送邮件 EMAIL_CONFIG = { "smtp_server": "localhost", "smtp_port": 25, "sender_email": "kalman@openclaw.local", "receiver_email": "380880504@qq.com" } msg = MIMEMultipart('related') msg['Subject'] = Header(f"📊 CYB50-Regime每日市场状态报告 [{df_aligned.index[-1].strftime('%m-%d')}] 当前{state_name}", 'utf-8') msg['From'] = "regime " msg['To'] = EMAIL_CONFIG['receiver_email'] msg.attach(MIMEText(html, 'html', 'utf-8')) try: with smtplib.SMTP(EMAIL_CONFIG['smtp_server'], EMAIL_CONFIG['smtp_port']) as server: server.sendmail( EMAIL_CONFIG['sender_email'], EMAIL_CONFIG['receiver_email'], msg.as_string() ) print(f"✅ 邮件发送成功! [{df_aligned.index[-1].strftime('%Y-%m-%d')}] 当前状态: {state_name}") except Exception as e: print(f"❌ 邮件发送失败: {e}")