""" Daily Routine Automation Service Handles scheduling and execution of daily trading routines """ from datetime import datetime, time from typing import List, Dict, Optional from sqlalchemy.orm import Session from apscheduler.schedulers.asyncio import AsyncIOScheduler from app.models.models import DailyRoutine, RoutineExecution, Notification from app.services.ai_analysis import analyze_trade from app.services.news_service import fetch_news from app.models.models import Trade, Simulation class RoutineTask: """Base class for routine tasks""" def __init__(self, task_name: str): self.task_name = task_name async def execute(self, db: Session) -> Dict: """Execute the task and return results""" raise NotImplementedError class MarketBriefTask(RoutineTask): """Generate daily market brief""" def __init__(self): super().__init__("market_brief") async def execute(self, db: Session) -> Dict: """Generate market brief with current price and news""" try: news = await fetch_news() return { "success": True, "task": self.task_name, "data": { "news_articles": len(news.get("articles", [])), "overall_sentiment": news.get("overall_sentiment"), "timestamp": datetime.utcnow().isoformat() } } except Exception as e: return { "success": False, "task": self.task_name, "error": str(e) } class ReviewPlanTask(RoutineTask): """Review trading plan for the day""" def __init__(self): super().__init__("review_plan") async def execute(self, db: Session) -> Dict: """Review and validate trading plan""" try: # In future, this would validate actual trading plans return { "success": True, "task": self.task_name, "data": { "plan_status": "active", "timestamp": datetime.utcnow().isoformat() } } except Exception as e: return { "success": False, "task": self.task_name, "error": str(e) } class ChecklistTask(RoutineTask): """Initialize daily checklist""" def __init__(self): super().__init__("checklist") async def execute(self, db: Session) -> Dict: """Initialize checklist for the day""" try: from app.models.models import DailyChecklist from datetime import date # Check if checklist already exists existing = db.query(DailyChecklist).filter( DailyChecklist.checklist_date == date.today() ).first() if not existing: checklist = DailyChecklist( checklist_date=date.today(), checklist_type="morning", items=[ {"id": "1", "title": "Check Economic Calendar", "completed": False}, {"id": "2", "title": "Scan Market News", "completed": False}, {"id": "3", "title": "Analyze Market Sentiment", "completed": False}, {"id": "4", "title": "Identify Key Levels", "completed": False}, {"id": "5", "title": "Create Trading Plan", "completed": False}, ] ) db.add(checklist) db.commit() return { "success": True, "task": self.task_name, "data": { "checklist_created": existing is None, "timestamp": datetime.utcnow().isoformat() } } except Exception as e: return { "success": False, "task": self.task_name, "error": str(e) } class PerformanceReviewTask(RoutineTask): """Review daily trading performance""" def __init__(self): super().__init__("performance_review") async def execute(self, db: Session) -> Dict: """Calculate and review daily performance metrics""" try: # Get today's trades from datetime import date, datetime as dt today = date.today() trades = db.query(Trade).filter( db.func.date(Trade.timestamp) == today ).all() 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) return { "success": True, "task": self.task_name, "data": { "trades_count": len(trades), "daily_pnl": daily_pnl, "winning_trades": winning_trades, "losing_trades": losing_trades, "timestamp": datetime.utcnow().isoformat() } } except Exception as e: return { "success": False, "task": self.task_name, "error": str(e) } class RoutineService: """Service for managing and executing daily routines""" # Task registry TASK_REGISTRY = { "market_brief": MarketBriefTask, "review_plan": ReviewPlanTask, "checklist": ChecklistTask, "performance_review": PerformanceReviewTask, } @staticmethod def get_task(task_name: str) -> Optional[RoutineTask]: """Get task instance by name""" task_class = RoutineService.TASK_REGISTRY.get(task_name) if task_class: return task_class() return None @staticmethod async def execute_routine( routine: DailyRoutine, db: Session ) -> RoutineExecution: """Execute a complete routine with all its tasks""" tasks_completed = [] failed_tasks = [] for task_name in (routine.tasks or []): task = RoutineService.get_task(task_name) if task: result = await task.execute(db) if result["success"]: tasks_completed.append(task_name) else: failed_tasks.append(task_name) # Determine completion status if not failed_tasks: status = "completed" elif len(tasks_completed) > 0: status = "partial" else: status = "failed" # Create execution record execution = RoutineExecution( routine_id=routine.id, completion_status=status, tasks_completed=tasks_completed, execution_notes=f"Completed: {len(tasks_completed)}/{len(routine.tasks or [])} tasks" ) db.add(execution) db.commit() db.refresh(execution) return execution @staticmethod def parse_time(time_str: str) -> time: """Parse time string HH:MM format""" try: hours, minutes = map(int, time_str.split(":")) return time(hour=hours, minute=minutes) except: return time(9, 0) # Default to 9:00 @staticmethod def should_run_routine(routine: DailyRoutine) -> bool: """Check if routine should run now""" if not routine.enabled: return False routine_time = RoutineService.parse_time(routine.scheduled_time) current_time = datetime.now().time() # Allow 5-minute window for execution time_diff = ( datetime.combine(datetime.today(), current_time) - datetime.combine(datetime.today(), routine_time) ).total_seconds() return 0 <= time_diff <= 300 # 5 minutes class RoutineScheduler: """Scheduler for automated routine execution""" def __init__(self): self.scheduler: Optional[AsyncIOScheduler] = None async def initialize(self): """Initialize the scheduler""" from apscheduler.schedulers.asyncio import AsyncIOScheduler self.scheduler = AsyncIOScheduler() self.scheduler.start() # Add job to check and run routines every minute self.scheduler.add_job( self.check_and_run_routines, "interval", minutes=1, id="routine_checker" ) async def check_and_run_routines(self, db: Session = None): """Check if any routines should run and execute them""" if not db: from app.db.database import SessionLocal db = SessionLocal() try: routines = db.query(DailyRoutine).filter( DailyRoutine.enabled == True ).all() for routine in routines: if RoutineService.should_run_routine(routine): await RoutineService.execute_routine(routine, db) finally: db.close() async def shutdown(self): """Shutdown the scheduler""" if self.scheduler: self.scheduler.shutdown() # Global scheduler instance routine_scheduler = RoutineScheduler()