#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 创业板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') # ==================== 邮件配置 ==================== EMAIL_CONFIG = { "smtp_server": "localhost", "smtp_port": 25, "sender_email": "catfly@erwin.wang", "receiver_email": "380880504@qq.com" } # ==================== 趋势跟踪策略 ==================== class TrendTrackingStrategy: """趋势跟踪策略 - 实时计算版""" def __init__(self): self.data = None self.signals = [] self.trades = [] def load_data(self, csv_file='cyb50_baostock.csv'): """加载历史数据""" try: df = pd.read_csv(f'/root/.openclaw/workspace/quant/{csv_file}') df['date'] = pd.to_datetime(df['date']) df = df.set_index('date').sort_index() # 转换数据类型 for col in ['open', 'high', 'low', 'close', 'volume']: df[col] = pd.to_numeric(df[col], errors='coerce') self.data = df print(f"数据加载成功: {df.index[0].date()} ~ {df.index[-1].date()}") return True except Exception as e: print(f"数据加载失败: {e}") return False def calculate_indicators(self): """计算技术指标""" df = self.data.copy() # 均线 df['ma10'] = df['close'].rolling(window=10).mean() df['ma30'] = df['close'].rolling(window=30).mean() # 20日高低点 df['high_20'] = df['high'].rolling(window=20).max() df['low_20'] = df['low'].rolling(window=20).min() # 10日涨幅 df['ret_10'] = df['close'].pct_change(periods=10) # ATR tr1 = df['high'] - df['low'] tr2 = abs(df['high'] - df['close'].shift(1)) tr3 = abs(df['low'] - df['close'].shift(1)) df['tr'] = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1) df['atr'] = df['tr'].rolling(window=20).mean() self.data = df return df def generate_signals(self): """生成交易信号""" df = self.data # 买入条件 buy_cond = ( (df['close'] > df['ma10']) & (df['ma10'] > df['ma30']) & (df['close'] >= df['high_20'] * 0.995) & (df['ret_10'] > 0.02) ) # 卖出条件 sell_cond = ( (df['close'] < df['ma30']) | (df['close'] <= df['low_20'] * 1.005) ) df['signal'] = 0 df.loc[buy_cond, 'signal'] = 1 df.loc[sell_cond, 'signal'] = -1 return df def backtest(self, initial_capital=1000000): """回测计算""" df = self.generate_signals() position = 0 entry_price = 0 peak_price = 0 capital = initial_capital trades = [] for i in range(30, len(df)): date = df.index[i] price = df['close'].iloc[i] signal = df['signal'].iloc[i] # 移动止损检查 if position > 0: if price > peak_price: peak_price = price if price < peak_price * 0.90: # 10%回撤止损 signal = -1 # 执行交易 if signal == 1 and position == 0: position = 1 entry_price = price peak_price = price trades.append({ 'date': date, 'action': 'BUY', 'price': price, 'capital': capital }) elif signal == -1 and position == 1: pnl = (price / entry_price - 1) * capital capital += pnl position = 0 trades.append({ 'date': date, 'action': 'SELL', 'price': price, 'capital': capital, 'pnl': pnl, 'return_pct': (price / entry_price - 1) * 100 }) # 计算当前持仓 current_position = position current_price = df['close'].iloc[-1] if position == 1: unrealized_pnl = (current_price / entry_price - 1) * capital total_value = capital + unrealized_pnl else: total_value = capital total_return = (total_value / initial_capital - 1) * 100 return { 'trades': trades, 'current_position': current_position, 'current_price': current_price, 'entry_price': entry_price if position == 1 else None, 'capital': capital, 'total_value': total_value, 'total_return': total_return, 'trade_count': len([t for t in trades if t['action'] == 'SELL']) } def get_recent_indicators(self, days=20): """获取近N天指标详情""" df = self.data.tail(days).copy() indicators = [] for date, row in df.iterrows(): indicators.append({ 'date': date.strftime('%Y-%m-%d'), 'close': round(row['close'], 2), 'ma10': round(row['ma10'], 2) if not pd.isna(row['ma10']) else '-', 'ma30': round(row['ma30'], 2) if not pd.isna(row['ma30']) else '-', 'high_20': round(row['high_20'], 2) if not pd.isna(row['high_20']) else '-', 'ret_10': f"{row['ret_10']*100:.2f}%" if not pd.isna(row['ret_10']) else '-', 'signal': '买入' if row['signal'] == 1 else ('卖出' if row['signal'] == -1 else '持有'), 'atr': round(row['atr'], 2) if not pd.isna(row['atr']) else '-' }) return indicators def get_recent_trades(self, n=20): """获取近N次交易详情""" result = self.backtest() trades = result['trades'][-n:] return trades def generate_report(): """生成完整报告""" strategy = TrendTrackingStrategy() if not strategy.load_data(): return None, None strategy.calculate_indicators() result = strategy.backtest() # 获取近期数据 recent_indicators = strategy.get_recent_indicators(20) recent_trades = strategy.get_recent_trades(20) # 生成HTML报告 html = f"""

🚀 创业板50趋势跟踪策略报告

生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

📊 总体绩效

当前持仓
{'持有中' if result['current_position'] == 1 else '空仓'}
当前价格
{result['current_price']:.2f}
累计收益率
{result['total_return']:+.2f}%
总资产
{result['total_value']:,.0f}元
交易次数
{result['trade_count']}
""" # 持仓详情 if result['current_position'] == 1: unrealized = (result['current_price'] / result['entry_price'] - 1) * 100 html += f"""

📈 持仓详情

入场价格: {result['entry_price']:.2f} 元

当前浮盈: {unrealized:+.2f}%

""" # 近20天指标 html += """

📅 近20天指标详情

""" for ind in recent_indicators: signal_class = 'buy' if ind['signal'] == '买入' else ('sell' if ind['signal'] == '卖出' else '') html += f""" """ html += """
日期 收盘价 MA10 MA30 20日高 10日涨幅 信号 ATR
{ind['date']} {ind['close']} {ind['ma10']} {ind['ma30']} {ind['high_20']} {ind['ret_10']} {ind['signal']} {ind['atr']}

💼 近20次交易详情

""" for trade in recent_trades: action_class = 'buy' if trade['action'] == 'BUY' else 'sell' pnl = trade.get('pnl', 0) ret = trade.get('return_pct', 0) html += f""" """ html += """
日期 操作 价格 盈亏金额 盈亏比例 总资产
{trade['date'].strftime('%Y-%m-%d') if isinstance(trade['date'], pd.Timestamp) else trade['date']} {trade['action']} {trade['price']:.2f} {f'{pnl:+,.0f}元' if 'pnl' in trade else '-'} {f'{ret:+.2f}%' if 'return_pct' in trade else '-'} {trade['capital']:,.0f}元
策略说明:
• 买入条件: 价格>MA10>MA30 且 突破20日高×0.995 且 10日涨幅>2%
• 卖出条件: 跌破MA30 或 创20日新低 或 回撤10%止损
• 风险提示: 右侧交易可能错过牛市初期涨幅
""" # 生成文本报告 text = f""" 创业板50趋势跟踪策略报告 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} 【总体绩效】 当前持仓: {'持有中' if result['current_position'] == 1 else '空仓'} 当前价格: {result['current_price']:.2f}元 累计收益率: {result['total_return']:+.2f}% 总资产: {result['total_value']:,.0f}元 交易次数: {result['trade_count']} 【近20天指标】 日期 收盘价 MA10 MA30 20日高 10日涨幅 信号 """ for ind in recent_indicators: text += f"{ind['date']} {ind['close']:>8} {ind['ma10']:>8} {ind['ma30']:>8} {ind['high_20']:>8} {ind['ret_10']:>8} {ind['signal']}\n" text += "\n【近20次交易】\n" text += "日期 操作 价格 盈亏金额 盈亏比例 总资产\n" for trade in recent_trades: pnl_str = f"{trade.get('pnl', 0):+,.0f}元" if 'pnl' in trade else '-' ret_str = f"{trade.get('return_pct', 0):+.2f}%" if 'return_pct' in trade else '-' date_str = trade['date'].strftime('%Y-%m-%d') if isinstance(trade['date'], pd.Timestamp) else trade['date'] text += f"{date_str} {trade['action']:>6} {trade['price']:>8.2f} {pnl_str:>12} {ret_str:>10} {trade['capital']:>12,.0f}元\n" return html, text, result 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'] = EMAIL_CONFIG['receiver_email'] 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_email'], msg.as_string() ) print(f"✅ 邮件发送成功: {subject}") return True except Exception as e: print(f"❌ 邮件发送失败: {e}") return False def main(): """主程序""" print("="*60) print("🚀 创业板50趋势跟踪实时报告") print("="*60) print(f"执行时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") # 生成报告 print("\n📊 生成报告中...") html, text, result = generate_report() if html is None: print("❌ 报告生成失败") return print(f"✅ 报告生成完成") print(f" 当前持仓: {'持有中' if result['current_position'] == 1 else '空仓'}") print(f" 累计收益: {result['total_return']:+.2f}%") print(f" 交易次数: {result['trade_count']}") # 发送邮件 print("\n📧 发送邮件...") position_status = "持仓" if result['current_position'] == 1 else "空仓" subject = f"🚀 创业板50趋势报告 {datetime.now().strftime('%m-%d %H:%M')} | {position_status} | 收益{result['total_return']:+.2f}%" send_email(subject, html, text) print("\n✅ 全部完成!") print("="*60) if __name__ == "__main__": main()