#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 创业板50指数 - 只做多T+1自动化交易报告系统 基于 cyb50_30min_long_only_t1.py 策略 """ 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 check_today_trades(trades_df): """检查当天是否有交易 返回: has_today_trade: bool, 当天是否有交易 today_trades: DataFrame, 当天的交易记录 """ 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): """生成只做多T+1交易报告(基于多空版本转换后的数据)""" if len(trades_df) == 0: final_capital = initial_capital total_return = 0 html = f"""
生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
数据区间: 近2个月
近2个月无交易信号触发
初始资金: {initial_capital:,.0f}元
最终资金: {final_capital:,.0f}元
收益率: {total_return:+.2f}%
""" text = f"近2个月无交易信号\n初始资金: {initial_capital:,.0f}元\n最终资金: {final_capital:,.0f}元" return html, text, final_capital # 计算统计数据 - 使用实际盈亏总和计算最终资金(更准确) 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_win = len(t1_adjusted[t1_adjusted['盈亏金额'] > 0]) if len(t1_adjusted) > 0 else 0 t1_pnl = t1_adjusted['盈亏金额'].sum() if len(t1_adjusted) > 0 else 0 # 非T+1调整交易 t1_normal = trades_df[trades_df['T+1调整'] != '是(T0→T1)'] normal_count = len(t1_normal) normal_win = len(t1_normal[t1_normal['盈亏金额'] > 0]) if len(t1_normal) > 0 else 0 normal_pnl = t1_normal['盈亏金额'].sum() if len(t1_normal) > 0 else 0 # HTML报告 - 使用原版cat-fly蓝色主题 html = f"""生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
数据区间: 近2个月
数据来源: 多空双向策略的做多交易 + T+1规则转换
| 指标 | 数值 |
|---|---|
| 初始资金 | {initial_capital:,.0f}元 |
| 最终资金 | {final_capital:,.0f}元 |
| 总收益率 | {total_return:+.2f}% |
| 总交易次数 | {total_trades}笔 |
| 胜率 | {win_rate:.1f}% |
| 盈亏比 | {profit_factor:.2f} |
| 类型 | 交易次数 | 胜率 | 总盈亏 |
|---|---|---|---|
| 正常交易 | {normal_count}笔 | {(normal_win/normal_count*100 if normal_count>0 else 0):.1f}% | {normal_pnl:+,.0f}元 |
| T+1调整(T0→T1) | {t1_count}笔 | {(t1_win/t1_count*100 if t1_count>0 else 0):.1f}% | {t1_pnl:+,.0f}元 |
| 开仓时间 | 原平仓 | 新平仓 | 原盈亏 | 新盈亏 | 变化 |
|---|---|---|---|---|---|
| {trade['开仓时间'].strftime('%m-%d %H:%M')} | {trade['原平仓时间'].strftime('%m-%d %H:%M')} | {trade['平仓时间'].strftime('%m-%d %H:%M')} | {trade['原盈亏']:+.0f} | {trade['盈亏金额']:+.0f} | {change:+.0f} |
| 无T+1调整交易 | |||||
| 开仓时间 | 平仓时间 | 开仓价 | 平仓价 | 盈亏 | 退出原因 | T+1调整 |
|---|---|---|---|---|---|---|
| {trade['开仓时间'].strftime('%m-%d %H:%M')} | {trade['平仓时间'].strftime('%m-%d %H:%M')} | {trade['开仓价格']:.2f} | {trade['平仓价格']:.2f} | {trade['盈亏金额']:+.0f} | {trade['退出原因']} | {t1_flag} |