""" Email Service for Daily Helper Handles sending email reports and notifications """ from datetime import datetime, date from typing import Optional, Dict, List from sqlalchemy.orm import Session from app.models.models import Trade, Simulation, Notification import logging logger = logging.getLogger(__name__) class EmailTemplate: """Email template generator""" @staticmethod def daily_report_html( user_email: str, daily_pnl: float, win_rate: float, winning_trades: int, losing_trades: int, best_trade: float, worst_trade: float, trades_count: int, completion_rate: float, portfolio_value: float, ) -> str: """Generate HTML for daily report email""" pnl_color = "green" if daily_pnl >= 0 else "red" win_rate_color = "green" if win_rate >= 50 else "orange" if win_rate >= 40 else "red" html = f"""

📊 Daily Trading Report

{date.today().strftime('%A, %B %d, %Y')}

Performance Summary

Daily P&L
${daily_pnl:,.2f}
Win Rate
{win_rate:.1f}%
Portfolio Value
${portfolio_value:,.2f}

Trade Statistics

Metric Value
Total Trades {trades_count}
Winning Trades ✓ {winning_trades}
Losing Trades ✗ {losing_trades}
Best Trade ${best_trade:,.2f}
Worst Trade ${worst_trade:,.2f}
Daily Checklist {completion_rate:.0f}% Complete

Tomorrow's Preparation

✓ Review today's trades and journal entries

✓ Update your trading plan for tomorrow

✓ Set price alerts for key levels

✓ Prepare your morning checklist

Open Trading Dashboard
""" return html @staticmethod def weekly_report_html( user_email: str, weekly_pnl: float, weekly_trades: int, win_rate: float, best_day: str, worst_day: str, best_trade: float, largest_loss: float, ) -> str: """Generate HTML for weekly report email""" pnl_color = "green" if weekly_pnl >= 0 else "red" html = f"""

📈 Weekly Trading Summary

Week of {(date.today()).strftime('%B %d')}

Weekly Performance

Weekly P&L
${weekly_pnl:,.2f}
Total Trades
{weekly_trades}
Win Rate
{win_rate:.1f}%

Key Insights

Metric Value
Best Day {best_day}
Worst Day {worst_day}
Best Single Trade ${best_trade:,.2f}
Largest Loss ${largest_loss:,.2f}

Action Items for Next Week

1. Review your best performing setups

2. Analyze losing trades for patterns

3. Update your trading journal with insights

4. Adjust your trading plan if needed

""" return html class EmailService: """Service for sending emails""" @staticmethod async def send_email( recipient_email: str, subject: str, html_content: str, ) -> bool: """ Send email (stub for integration with actual email service) In production, integrate with: - SendGrid - Mailgun - AWS SES - SMTP server """ try: # TODO: Implement actual email sending # For now, just log it logger.info(f"Email to {recipient_email}: {subject}") logger.debug(f"HTML content length: {len(html_content)}") # In production, replace this with actual email sending: # import smtplib # from email.mime.text import MIMEText # from email.mime.multipart import MIMEMultipart # # msg = MIMEMultipart('alternative') # msg['Subject'] = subject # msg['From'] = EMAIL_FROM # msg['To'] = recipient_email # msg.attach(MIMEText(html_content, 'html')) # # with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server: # server.starttls() # server.login(SMTP_USER, SMTP_PASSWORD) # server.send_message(msg) return True except Exception as e: logger.error(f"Failed to send email to {recipient_email}: {str(e)}") return False @staticmethod async def send_daily_report( db: Session, user_email: str, ) -> bool: """Send daily trading report email""" try: # Get today's trades today = date.today() trades = db.query(Trade).filter( db.func.date(Trade.timestamp) == today ).all() # Calculate metrics daily_pnl = sum(trade.pnl or 0 for trade in trades) winning_trades = sum(1 for trade in trades if (trade.pnl or 0) > 0) losing_trades = sum(1 for trade in trades if (trade.pnl or 0) < 0) best_trade = max((trade.pnl or 0 for trade in trades), default=0) worst_trade = min((trade.pnl or 0 for trade in trades), default=0) win_rate = (winning_trades / len(trades) * 100) if trades else 0 # Get portfolio value simulation = db.query(Simulation).first() portfolio_value = simulation.current_capital if simulation else 0 # Placeholder for completion rate completion_rate = 75.0 # Generate HTML html = EmailTemplate.daily_report_html( user_email, daily_pnl, win_rate, winning_trades, losing_trades, best_trade, worst_trade, len(trades), completion_rate, portfolio_value, ) # Send email return await EmailService.send_email( user_email, f"Daily Trading Report - {today.strftime('%B %d, %Y')}", html, ) except Exception as e: logger.error(f"Failed to send daily report: {str(e)}") return False @staticmethod async def send_weekly_report( db: Session, user_email: str, ) -> bool: """Send weekly trading report email""" try: from datetime import timedelta # Get this week's trades today = date.today() week_start = today - timedelta(days=today.weekday()) week_end = week_start + timedelta(days=6) trades = db.query(Trade).filter( db.func.date(Trade.timestamp) >= week_start, db.func.date(Trade.timestamp) <= week_end ).all() # Calculate metrics weekly_pnl = sum(trade.pnl or 0 for trade in trades) winning_trades = sum(1 for trade in trades if (trade.pnl or 0) > 0) win_rate = (winning_trades / len(trades) * 100) if trades else 0 best_trade = max((trade.pnl or 0 for trade in trades), default=0) largest_loss = min((trade.pnl or 0 for trade in trades), default=0) # Find best/worst trading day daily_pnls = {} for trade in trades: day = trade.timestamp.date() if day not in daily_pnls: daily_pnls[day] = 0 daily_pnls[day] += trade.pnl or 0 best_day = max(daily_pnls, key=daily_pnls.get).strftime('%A') if daily_pnls else "N/A" worst_day = min(daily_pnls, key=daily_pnls.get).strftime('%A') if daily_pnls else "N/A" # Generate HTML html = EmailTemplate.weekly_report_html( user_email, weekly_pnl, len(trades), win_rate, best_day, worst_day, best_trade, largest_loss, ) # Send email return await EmailService.send_email( user_email, f"Weekly Trading Summary - Week of {week_start.strftime('%B %d')}", html, ) except Exception as e: logger.error(f"Failed to send weekly report: {str(e)}") return False