Parcourir la source

feat: 新增Trend-Max三大策略综合分析

- 新增 trend-max-daily.py: 汇总Trend-Mix、TQE、Regime三大策略
- 工作日15:30定时推送综合分析报告
- 发件人: trend-max@openclaw.local
- 包含策略一致性分析和综合建议

定时任务:
- Trend-Max工作日综合分析: 工作日15:30
openclaw il y a 2 mois
Parent
commit
9261ed76b1
1 fichiers modifiés avec 317 ajouts et 0 suppressions
  1. 317 0
      trend-max-daily.py

+ 317 - 0
trend-max-daily.py

@@ -0,0 +1,317 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Trend-Max: 三大策略综合分析
+汇总 Trend-Mix, TQE, Regime 的结果
+工作日15:30定时推送
+发件人: trend-max
+"""
+
+import sys
+sys.path.insert(0, '/root/.openclaw/workspace/trend-mix')
+sys.path.insert(0, '/root/.openclaw/workspace/trend-quality-evaluator')
+sys.path.insert(0, '/root/.openclaw/workspace/market-regime-identifier')
+
+import numpy as np
+import pandas as pd
+from trend_mix_strategy import TrendMixStrategy
+from trend_quality_evaluator import fetch_stock_data as tqe_fetch, TrendQualityEvaluator
+from cyb50_market_classifier import fetch_cyb50_data, calculate_features, define_market_regime
+from sklearn.ensemble import RandomForestClassifier
+from datetime import datetime
+import smtplib
+from email.mime.text import MIMEText
+from email.mime.multipart import MIMEMultipart
+from email.header import Header
+import warnings
+warnings.filterwarnings('ignore')
+
+print("="*70)
+print(f"Trend-Max 三大策略综合分析 - {datetime.now().strftime('%Y-%m-%d %H:%M')}")
+print("="*70)
+
+# ============================================
+# 1. Trend-Mix 分析
+# ============================================
+print("\n[1/3] 运行 Trend-Mix 分析...")
+strategy_mix = TrendMixStrategy()
+df_mix = strategy_mix.fetch_data("399673", "2024-01-01", "2026-12-31")
+
+if df_mix is not None:
+    # 计算综合状态
+    composite_df = strategy_mix.calc_composite_state()
+    latest_mix = composite_df.iloc[-1]
+    
+    mix_result = {
+        'state': latest_mix['Composite_State'],
+        'adx': latest_mix['ADX'],
+        'adx_state': latest_mix['ADX_State'],
+        'vr_state': latest_mix['VR_State'],
+        'hurst_state': latest_mix['Hurst_State'],
+        'bb_state': latest_mix['BB_State'],
+        'deviation': latest_mix['Deviation']
+    }
+    print(f"   ✓ Trend-Mix: {mix_result['state']}")
+else:
+    mix_result = None
+    print("   ✗ Trend-Mix 失败")
+
+# ============================================
+# 2. TQE 分析
+# ============================================
+print("\n[2/3] 运行 TQE 分析...")
+df_tqe = tqe_fetch("399673", "2024-01-01", "2026-12-31", "d")
+
+if df_tqe is not None:
+    evaluator = TrendQualityEvaluator()
+    score = evaluator.evaluate(df_tqe)
+    
+    tqe_result = {
+        'total_score': score.total_score,
+        'is_tradeable': score.is_tradeable,
+        'adx_score': score.adx_score,
+        'ma_slope_score': score.ma_slope_score,
+        'volatility_score': score.volatility_score,
+        'timeframe_score': score.timeframe_score,
+        'volume_score': score.volume_score,
+        'adx_value': score.adx_value,
+        'ma_slope': score.ma_slope,
+        'volatility_ratio': score.volatility_ratio,
+        'volume_ratio': score.volume_ratio
+    }
+    print(f"   ✓ TQE: {score.total_score:.1f}分 {'✅可交易' if score.is_tradeable else '❌观望'}")
+else:
+    tqe_result = None
+    print("   ✗ TQE 失败")
+
+# ============================================
+# 3. Regime 分析
+# ============================================
+print("\n[3/3] 运行 Regime 分析...")
+df_regime = fetch_cyb50_data("2024-01-01", "2026-12-31")
+
+if df_regime is not None:
+    features = calculate_features(df_regime)
+    labels = define_market_regime(df_regime, 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)
+    
+    # 预测
+    latest_features = features.iloc[-1:]
+    pred = clf.predict(latest_features)[0]
+    proba = clf.predict_proba(latest_features)[0]
+    
+    state_names = ['震荡', '趋势', '反转']
+    regime_result = {
+        'state': state_names[pred],
+        'confidence': proba[pred],
+        'prob_ranging': proba[0],
+        'prob_trend': proba[1],
+        'prob_reversal': proba[2]
+    }
+    print(f"   ✓ Regime: {regime_result['state']} (置信度{regime_result['confidence']:.1%})")
+else:
+    regime_result = None
+    print("   ✗ Regime 失败")
+
+# ============================================
+# 4. 综合分析
+# ============================================
+print("\n" + "="*70)
+print("综合分析")
+print("="*70)
+
+state_names_cn = ['震荡', '趋势', '反转']
+final_recommendation = "观望"
+final_confidence = 0.0
+
+if mix_result and tqe_result and regime_result:
+    # 统计各策略的状态
+    states = [mix_result['state'], '趋势' if tqe_result['is_tradeable'] else '震荡', regime_result['state']]
+    
+    # 交易建议映射
+    trade_advice = {
+        '强趋势': '重仓',
+        '趋势': '持仓/加仓',
+        '潜在爆发': '建仓',
+        '反转': '止盈/减仓',
+        '震荡': '观望'
+    }
+    
+    mix_advice = trade_advice.get(mix_result['state'], '观望')
+    tqe_advice = '交易' if tqe_result['is_tradeable'] else '观望'
+    regime_advice = regime_result['state']
+    
+    # 综合判断
+    trend_count = sum([s in ['强趋势', '趋势'] for s in states])
+    ranging_count = sum([s == '震荡' for s in states])
+    reversal_count = sum([s == '反转' for s in states])
+    
+    if trend_count >= 2 and tqe_result['is_tradeable']:
+        final_recommendation = "✅ 交易 (趋势确认)"
+        final_confidence = 0.8
+    elif ranging_count >= 2 or not tqe_result['is_tradeable']:
+        final_recommendation = "❌ 观望 (震荡整理)"
+        final_confidence = 0.7
+    elif reversal_count >= 2:
+        final_recommendation = "⚠️ 谨慎 (可能反转)"
+        final_confidence = 0.6
+    else:
+        final_recommendation = "❓ 分歧 (信号不一)"
+        final_confidence = 0.4
+    
+    print(f"\nTrend-Mix: {mix_result['state']} → {mix_advice}")
+    print(f"TQE: {tqe_result['total_score']:.0f}分 → {tqe_advice}")
+    print(f"Regime: {regime_result['state']} → {regime_advice}")
+    print(f"\n综合建议: {final_recommendation}")
+
+# ============================================
+# 5. 生成邮件
+# ============================================
+print("\n生成邮件...")
+
+today = datetime.now().strftime('%Y-%m-%d')
+
+html = f"""
+<html>
+<head>
+    <meta charset="utf-8">
+    <style>
+        body {{ font-family: Arial, sans-serif; margin: 20px; font-size: 13px; }}
+        h1 {{ color: #333; border-bottom: 3px solid #673AB7; padding-bottom: 10px; font-size: 20px; }}
+        h2 {{ color: #555; margin-top: 25px; border-left: 4px solid #2196F3; padding-left: 10px; font-size: 15px; }}
+        h3 {{ color: #666; font-size: 14px; }}
+        .summary {{ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 10px; margin: 20px 0; }}
+        .strategy {{ background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 15px 0; border-left: 4px solid #4CAF50; }}
+        .trade-yes {{ background: #e8f5e9; border-left: 4px solid #4CAF50; }}
+        .trade-no {{ background: #ffebee; border-left: 4px solid #f44336; }}
+        .trade-warn {{ background: #fff3cd; border-left: 4px solid #FFC107; }}
+        table {{ width: 100%; border-collapse: collapse; margin: 15px 0; font-size: 12px; }}
+        th {{ background: #673AB7; color: white; padding: 10px; text-align: center; }}
+        td {{ padding: 8px; border-bottom: 1px solid #ddd; text-align: center; }}
+        tr:nth-child(even) {{ background: #f8f9fa; }}
+        .score-high {{ color: #4CAF50; font-size: 28px; font-weight: bold; }}
+        .score-mid {{ color: #FF9800; font-size: 28px; font-weight: bold; }}
+        .score-low {{ color: #f44336; font-size: 28px; font-weight: bold; }}
+        .final-rec {{ font-size: 18px; font-weight: bold; text-align: center; padding: 15px; border-radius: 8px; margin: 20px 0; }}
+    </style>
+</head>
+<body>
+    <h1>📊 Trend-Max 三大策略综合分析</h1>
+    
+    <div class="summary">
+        <h2 style="color: white; border-left: none;">🎯 综合建议</h2>
+        <p style="font-size: 16px;"><strong>日期:</strong> {today} | <strong>标的:</strong> 创业板50 (399673)</p>
+        <div class="final-rec {'trade-yes' if '✅' in final_recommendation else 'trade-no' if '❌' in final_recommendation else 'trade-warn'}">
+            {final_recommendation}
+        </div>
+        <p><strong>置信度:</strong> {final_confidence*100:.0f}% | <strong>三策略一致率:</strong> {max(trend_count, ranging_count, reversal_count)}/3</p>
+    </div>
+    
+    <h2>📈 各策略详细分析</h2>
+    
+    <!-- Trend-Mix -->
+    <div class="strategy">
+        <h3>1️⃣ Trend-Mix (6种方法综合)</h3>
+        <p><strong>状态:</strong> {mix_result['state'] if mix_result else 'N/A'}</p>
+        <table>
+            <tr><th>指标</th><th>数值</th><th>状态</th></tr>
+            <tr><td>ADX(14)</td><td>{mix_result['adx']:.2f}</td><td>{mix_result['adx_state']}</td></tr>
+            <tr><td>价格偏离MA20</td><td>{mix_result['deviation']:.2f}%</td><td>-</td></tr>
+            <tr><td>方差比检验</td><td>-</td><td>{mix_result['vr_state']}</td></tr>
+            <tr><td>Hurst指数</td><td>-</td><td>{mix_result['hurst_state']}</td></tr>
+            <tr><td>布林带</td><td>-</td><td>{mix_result['bb_state']}</td></tr>
+        </table>
+        <p><strong>建议:</strong> {trade_advice.get(mix_result['state'], '观望') if mix_result else 'N/A'}</p>
+    </div>
+    
+    <!-- TQE -->
+    <div class="strategy">
+        <h3>2️⃣ TQE 趋势质量评估 (0-100分)</h3>
+        <p><strong>总分:</strong> <span class="{'score-high' if tqe_result['total_score'] >= 80 else 'score-mid' if tqe_result['total_score'] >= 60 else 'score-low'}">{tqe_result['total_score']:.1f}分</span></p>
+        <p><strong>是否可交易:</strong> {'✅ 是 (≥60分)' if tqe_result['is_tradeable'] else '❌ 否 (<60分)'}</p>
+        
+        <table>
+            <tr><th>因子</th><th>得分</th><th>原始值</th></tr>
+            <tr><td>ADX趋势强度 (30分)</td><td>{tqe_result['adx_score']:.1f}</td><td>ADX={tqe_result['adx_value']:.1f}</td></tr>
+            <tr><td>均线斜率 (25分)</td><td>{tqe_result['ma_slope_score']:.1f}</td><td>斜率={tqe_result['ma_slope']:.4f}</td></tr>
+            <tr><td>波动率收缩 (20分)</td><td>{tqe_result['volatility_score']:.1f}</td><td>ATR比={tqe_result['volatility_ratio']:.3f}</td></tr>
+            <tr><td>时间框架共振 (15分)</td><td>{tqe_result['timeframe_score']:.1f}</td><td>-</td></tr>
+            <tr><td>成交量确认 (10分)</td><td>{tqe_result['volume_score']:.1f}</td><td>量比={tqe_result['volume_ratio']:.2f}x</td></tr>
+        </table>
+        <p><strong>建议:</strong> {'交易' if tqe_result['is_tradeable'] else '观望'}</p>
+    </div>
+    
+    <!-- Regime -->
+    <div class="strategy">
+        <h3>3️⃣ Regime 市场状态分类</h3>
+        <p><strong>预测状态:</strong> {regime_result['state']} (置信度 {regime_result['confidence']:.1%})</p>
+        
+        <table>
+            <tr><th>状态</th><th>概率</th><th>可视化</th></tr>
+            <tr><td>震荡</td><td>{regime_result['prob_ranging']:.1%}</td><td>{'█' * int(regime_result['prob_ranging'] * 20)}</td></tr>
+            <tr><td>趋势</td><td>{regime_result['prob_trend']:.1%}</td><td>{'█' * int(regime_result['prob_trend'] * 20)}</td></tr>
+            <tr><td>反转</td><td>{regime_result['prob_reversal']:.1%}</td><td>{'█' * int(regime_result['prob_reversal'] * 20)}</td></tr>
+        </table>
+        <p><strong>建议:</strong> {regime_result['state']}期 {'持仓' if regime_result['state'] == '趋势' else '观望' if regime_result['state'] == '震荡' else '减仓'}</p>
+    </div>
+    
+    <h2>📊 策略一致性分析</h2>
+    
+    <table>
+        <tr><th>策略</th><th>判断</th><th>建议</th></tr>
+        <tr><td>Trend-Mix</td><td>{mix_result['state']}</td><td>{trade_advice.get(mix_result['state'], '观望')}</td></tr>
+        <tr><td>TQE</td><td>{tqe_result['total_score']:.0f}分 {'✅' if tqe_result['is_tradeable'] else '❌'}</td><td>{'交易' if tqe_result['is_tradeable'] else '观望'}</td></tr>
+        <tr><td>Regime</td><td>{regime_result['state']}</td><td>{regime_result['state']}期操作</td></tr>
+    </table>
+    
+    <h2>💡 关键洞察</h2>
+    
+    <ul>
+        <li><strong>ADX指标:</strong> 三策略均显示高ADX值({tqe_result['adx_value']:.1f}),表明趋势强度较高</li>
+        <li><strong>MA方向:</strong> 但MA斜率({tqe_result['ma_slope']:.4f})向下,价格低于MA20,短期偏弱</li>
+        <li><strong>成交量:</strong> 量比仅{tqe_result['volume_ratio']:.2f}x,缩量整理,缺乏资金流入</li>
+        <li><strong>结论:</strong> 高ADX但价格下跌,处于下跌趋势延续中,建议观望等待明确信号</li>
+    </ul>
+    
+    <hr>
+    <p style="color: #666; font-size: 11px;">
+        生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}<br>
+        分析模块: Trend-Mix + TQE + Regime<br>
+        发件人: trend-max | 推送时间: 工作日 15:30
+    </p>
+</body>
+</html>
+"""
+
+# 发送邮件
+EMAIL_CONFIG = {
+    "smtp_server": "localhost",
+    "smtp_port": 25,
+    "sender_email": "trend-max@openclaw.local",
+    "receiver_email": "380880504@qq.com"
+}
+
+msg = MIMEMultipart('related')
+msg['Subject'] = Header(f"📊 Trend-Max综合分析 [{today}] {final_recommendation}", 'utf-8')
+msg['From'] = EMAIL_CONFIG['sender_email']
+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"\n✅ 邮件发送成功! 发件人: trend-max")
+except Exception as e:
+    print(f"\n❌ 邮件发送失败: {e}")
+
+print("="*70)