#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 创业板50指数 - 只做多T+1自动化交易报告系统 (实时信号增强版) 在14:50等时间点会使用实时行情数据计算当前信号 """ import sys sys.path.insert(0, '/root/.openclaw/workspace/cat-fly') import pandas as pd import numpy as np from datetime import datetime, timedelta import smtplib import ssl from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Header import warnings warnings.filterwarnings('ignore') # 导入策略模块 from cyb50_30min_dual_direction import ( ConfigManager, IntradayDataFetcher, DualDirectionSignalGenerator, DualDirectionExecutor ) from t1_converter import simulate_t1_trades_v2, compare_results # ==================== 邮件配置 ==================== EMAIL_CONFIG = { "smtp_server": "localhost", "smtp_port": 25, "sender_email": "cyb50-t1@erwin.wang", "receiver_emails": ["380880504@qq.com", "1095512042@qq.com"] } def send_email(subject, html_content, text_content=""): """发送邮件""" try: msg = MIMEMultipart('alternative') msg['Subject'] = Header(subject, 'utf-8') msg['From'] = EMAIL_CONFIG['sender_email'] msg['To'] = ', '.join(EMAIL_CONFIG['receiver_emails']) text_part = MIMEText(text_content, 'plain', 'utf-8') msg.attach(text_part) html_part = MIMEText(html_content, 'html', 'utf-8') msg.attach(html_part) with smtplib.SMTP(EMAIL_CONFIG['smtp_server'], EMAIL_CONFIG['smtp_port']) as server: server.sendmail( EMAIL_CONFIG['sender_email'], EMAIL_CONFIG['receiver_emails'], msg.as_string() ) print(f"✅ 邮件发送成功: {subject}") return True except Exception as e: print(f"❌ 邮件发送失败: {e}") return False def get_realtime_price(): """获取创业板50实时价格""" try: import akshare as ak # 使用新浪接口获取实时行情(更稳定) df = ak.stock_zh_index_spot_sina() cyb50 = df[df['代码'] == 'sz399673'] if len(cyb50) > 0: return { 'price': float(cyb50.iloc[0]['最新价']), 'open': float(cyb50.iloc[0]['今开']), 'high': float(cyb50.iloc[0]['最高']), 'low': float(cyb50.iloc[0]['最低']), 'volume': float(cyb50.iloc[0]['成交量']), 'change_pct': float(cyb50.iloc[0].get('涨跌幅', 0)), 'time': datetime.now().strftime('%H:%M:%S') } except Exception as e: print(f"获取实时行情失败: {e}") return None def calculate_realtime_signal(current_price, last_kline_data): """ 基于最新价格和最后一根K线数据,估算当前信号状态 """ score = 0 signals = [] last_close = last_kline_data['Close'] price_change_pct = (current_price - last_close) / last_close # 1. RSI估算 estimated_rsi = last_kline_data['RSI'] + price_change_pct * 250 if estimated_rsi < 30: score += 2 signals.append(f"RSI超卖(估{estimated_rsi:.1f})") elif estimated_rsi < 35: score += 1 signals.append(f"RSI偏弱(估{estimated_rsi:.1f})") # 2. KDJ估算 estimated_j = last_kline_data['J'] + price_change_pct * 300 if estimated_j < 0: score += 1 signals.append(f"KDJ极端超卖(估J={estimated_j:.1f})") # 3. 布林带位置 bb_lower = last_kline_data['BB_lower'] if current_price <= bb_lower * 1.005: score += 2 signals.append("触及下轨") elif current_price <= bb_lower * 1.02: score += 1 signals.append("接近下轨") # 4. 价格动量 if price_change_pct < -0.015: score += 1 signals.append(f"动量超卖({price_change_pct*100:.2f}%)") # 5. MA趋势 if last_kline_data['MA6'] > last_kline_data['MA12']: score += 1 signals.append("MA短期上行") else: score -= 1 signals.append("MA下降趋势惩罚") return { 'score': score, 'signals': signals, 'estimated_rsi': estimated_rsi, 'estimated_j': estimated_j, 'price_change_pct': price_change_pct, 'bb_lower': bb_lower, 'bb_upper': last_kline_data['BB_upper'], 'current_price': current_price, 'last_close': last_close, 'triggered': score >= 4 } def is_pre_close_time(): """检查是否是收盘前10分钟(14:50左右)""" now = datetime.now() return now.hour == 14 and now.minute >= 50 def check_today_trades(trades_df): """检查当天是否有交易""" if len(trades_df) == 0: return False, pd.DataFrame() today = datetime.now().date() today_trades = trades_df[ (pd.to_datetime(trades_df['开仓时间']).dt.date == today) | (pd.to_datetime(trades_df['平仓时间']).dt.date == today) ] has_today_trade = len(today_trades) > 0 if has_today_trade: print(f"📊 当天交易数量: {len(today_trades)}笔") for _, trade in today_trades.iterrows(): print(f" {trade['开仓时间']} → {trade['平仓时间']} | {trade['盈亏金额']:+.0f}元") else: print("📭 当天无交易") return has_today_trade, today_trades def is_post_close_time(): """检查是否是盘后时间(15:00-15:30)""" now = datetime.now() return now.hour == 15 and now.minute >= 0 and now.minute <= 30 def generate_report(trades_df, initial_capital=1000000, realtime_signal=None): """生成只做多T+1交易报告(增强版,包含实时信号)""" if len(trades_df) == 0: final_capital = initial_capital total_return = 0 else: total_pnl = trades_df['盈亏金额'].sum() final_capital = initial_capital + total_pnl total_return = (final_capital - initial_capital) / initial_capital * 100 total_trades = len(trades_df) winning_trades = trades_df[trades_df['盈亏金额'] > 0] losing_trades = trades_df[trades_df['盈亏金额'] < 0] win_rate = len(winning_trades) / total_trades * 100 if total_trades > 0 else 0 total_profit = winning_trades['盈亏金额'].sum() if len(winning_trades) > 0 else 0 total_loss = abs(losing_trades['盈亏金额'].sum()) if len(losing_trades) > 0 else 0 profit_factor = total_profit / total_loss if total_loss > 0 else 0 # T+1调整统计 t1_adjusted = trades_df[trades_df['T+1调整'] == '是(T0→T1)'] t1_count = len(t1_adjusted) t1_pnl = t1_adjusted['盈亏金额'].sum() if len(t1_adjusted) > 0 else 0 # 构建HTML报告 now_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S') # 实时信号部分 realtime_html = "" if realtime_signal: signal_color = "green" if realtime_signal['triggered'] else "orange" if realtime_signal['score'] >= 3 else "gray" signal_text = "🟢 触发买入" if realtime_signal['triggered'] else "🟡 接近触发" if realtime_signal['score'] >= 3 else "⚪ 未触发" realtime_html = f"""
| 指标 | 数值 |
|---|---|
| 检测时间 | {now_str} |
| 实时价格 | {realtime_signal['current_price']:.2f} |
| 较上一K线 | {realtime_signal['price_change_pct']*100:+.2f}% |
| RSI(估算) | {realtime_signal['estimated_rsi']:.2f} |
| KDJ J(估算) | {realtime_signal['estimated_j']:.2f} |
| 布林带下轨 | {realtime_signal['bb_lower']:.2f} |
| 信号评分 | {realtime_signal['score']}/4 |
| 触发信号 | {', '.join(realtime_signal['signals']) if realtime_signal['signals'] else '无'} |
| 最终判断 | {signal_text} |
生成时间: {now_str}
数据区间: 近3个月
{realtime_html}| 指标 | 数值 |
|---|---|
| 初始资金 | {initial_capital:,.0f}元 |
| 最终资金 | {final_capital:,.0f}元 |
| 总收益率 | {total_return:+.2f}% |
| 总交易次数 | {total_trades}笔 |
| 胜率 | {win_rate:.1f}% |
| 盈亏比 | {profit_factor:.2f} |
| 开仓时间 | 平仓时间 | 开仓价 | 平仓价 | 盈亏 | 退出原因 | T+1调整 |
|---|---|---|---|---|---|---|
| {trade['开仓时间'].strftime('%m-%d %H:%M')} | {trade['平仓时间'].strftime('%m-%d %H:%M')} | {trade['开仓价格']:.2f} | {trade['平仓价格']:.2f} | {trade['盈亏金额']:+.0f} | {trade['退出原因']} | {t1_flag} |
| 近2个月无交易信号触发 | ||||||