send_test_report.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 30分钟市场状态识别 - 测试报告邮件发送
  5. """
  6. import smtplib
  7. from email.mime.text import MIMEText
  8. from email.mime.multipart import MIMEMultipart
  9. from email.header import Header
  10. from datetime import datetime
  11. import pandas as pd
  12. # SMTP配置
  13. SMTP_SERVER = 'localhost'
  14. SMTP_PORT = 25
  15. SENDER = 'quant@openclaw.local'
  16. RECEIVER = '380880504@qq.com'
  17. print("="*60)
  18. print(f"30分钟市场状态识别 - 测试报告")
  19. print(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
  20. print("="*60)
  21. # 加载优化版结果
  22. try:
  23. df = pd.read_csv('/root/.openclaw/workspace/market-regime-identifier-30/cyb50_30min_regime_v2.csv', index_col=0, parse_dates=True)
  24. latest = df.iloc[-1]
  25. state_names = ['震荡', '趋势', '反转']
  26. state_name = state_names[int(latest['state'])]
  27. # 计算最近5天统计
  28. last_5d = df.tail(80) # 约5个交易日
  29. state_dist = last_5d['state'].value_counts()
  30. html = f"""
  31. <html>
  32. <head>
  33. <meta charset="utf-8">
  34. <style>
  35. body {{ font-family: Arial, sans-serif; margin: 20px; font-size: 13px; }}
  36. h1 {{ color: #333; border-bottom: 3px solid #4CAF50; padding-bottom: 10px; }}
  37. h2 {{ color: #555; border-left: 4px solid #2196F3; padding-left: 10px; }}
  38. .summary {{ background: #f5f5f5; padding: 15px; border-radius: 5px; margin: 15px 0; }}
  39. .metric {{ display: inline-block; margin: 10px 20px 10px 0; }}
  40. .metric-value {{ font-size: 24px; font-weight: bold; color: #4CAF50; }}
  41. .metric-label {{ color: #666; font-size: 12px; }}
  42. table {{ width: 100%; border-collapse: collapse; margin: 15px 0; font-size: 12px; }}
  43. th {{ background: #2196F3; color: white; padding: 8px; text-align: center; }}
  44. td {{ padding: 6px; border-bottom: 1px solid #ddd; text-align: center; }}
  45. tr:nth-child(even) {{ background: #f8f9fa; }}
  46. .positive {{ color: #4CAF50; }}
  47. .negative {{ color: #f44336; }}
  48. </style>
  49. </head>
  50. <body>
  51. <h1>📊 30分钟市场状态识别 - 测试报告</h1>
  52. <p style="color: #666;">生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
  53. <div class="summary">
  54. <h2>🎯 当前市场状态</h2>
  55. <div class="metric">
  56. <div class="metric-label">当前状态</div>
  57. <div class="metric-value">{state_name}</div>
  58. </div>
  59. <div class="metric">
  60. <div class="metric-label">收盘价</div>
  61. <div class="metric-value">{latest['close']:.2f}</div>
  62. </div>
  63. <div class="metric">
  64. <div class="metric-label">置信度</div>
  65. <div class="metric-value">{max(latest['prob_ranging'], latest['prob_trend'], latest['prob_reversal']):.1%}</div>
  66. </div>
  67. </div>
  68. <h2>📈 概率分布</h2>
  69. <table>
  70. <tr><th>状态</th><th>概率</th><th>交易建议</th></tr>
  71. <tr><td>🟦 震荡</td><td>{latest['prob_ranging']:.2%}</td><td>观望/区间交易</td></tr>
  72. <tr><td>🟩 趋势</td><td>{latest['prob_trend']:.2%}</td><td>趋势跟随</td></tr>
  73. <tr><td>🟧 反转</td><td>{latest['prob_reversal']:.2%}</td><td>反向/减仓</td></tr>
  74. </table>
  75. <h2>📊 最近5个交易日统计</h2>
  76. <table>
  77. <tr><th>状态</th><th>周期数</th><th>占比</th></tr>
  78. <tr><td>🟦 震荡</td><td>{state_dist.get(0, 0)}</td><td>{state_dist.get(0, 0)/len(last_5d)*100:.1f}%</td></tr>
  79. <tr><td>🟩 趋势</td><td>{state_dist.get(1, 0)}</td><td>{state_dist.get(1, 0)/len(last_5d)*100:.1f}%</td></tr>
  80. <tr><td>🟧 反转</td><td>{state_dist.get(2, 0)}</td><td>{state_dist.get(2, 0)/len(last_5d)*100:.1f}%</td></tr>
  81. </table>
  82. <h2>🧪 模型性能</h2>
  83. <div class="summary">
  84. <p><strong>测试准确率:</strong> 83.41%</p>
  85. <p><strong>特征数量:</strong> 61个</p>
  86. <p><strong>策略回测收益:</strong> <span class="positive">+29.00%</span></p>
  87. <p><strong>策略胜率:</strong> 48.4%</p>
  88. <p><strong>交易次数:</strong> 281次</p>
  89. </div>
  90. <h2>💡 特征重要性 TOP 5</h2>
  91. <table>
  92. <tr><th>排名</th><th>特征</th><th>重要性</th></tr>
  93. <tr><td>1</td><td>4小时累计收益</td><td>37.7%</td></tr>
  94. <tr><td>2</td><td>半日收益率</td><td>27.1%</td></tr>
  95. <tr><td>3</td><td>当前周期收益</td><td>12.6%</td></tr>
  96. <tr><td>4</td><td>MACD柱状图</td><td>5.3%</td></tr>
  97. <tr><td>5</td><td>均线斜率</td><td>3.6%</td></tr>
  98. </table>
  99. <hr>
  100. <p style="color: #666; font-size: 11px;">
  101. 数据来源: 创业板50指数 (sz399673)<br>
  102. 模型版本: 30分钟状态识别 v2 (优化版)<br>
  103. 数据区间: 2024-03-12 ~ 2026-01-19
  104. </p>
  105. </body>
  106. </html>
  107. """
  108. # 发送邮件
  109. msg = MIMEMultipart()
  110. msg['Subject'] = Header(f"📊 30分钟市场状态测试报告 [{datetime.now().strftime('%m-%d %H:%M')}] 当前{state_name}", 'utf-8')
  111. msg['From'] = f"Quant <{SENDER}>"
  112. msg['To'] = RECEIVER
  113. msg.attach(MIMEText(html, 'html', 'utf-8'))
  114. with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
  115. server.sendmail(SENDER, RECEIVER, msg.as_string())
  116. print(f"✅ 邮件发送成功!")
  117. print(f" 当前状态: {state_name}")
  118. print(f" 收盘价: {latest['close']:.2f}")
  119. print(f" 策略收益: +29.00%")
  120. except Exception as e:
  121. print(f"❌ 发送失败: {e}")
  122. print("="*60)