from __future__ import annotations from PyQt6.QtCore import QThread, pyqtSignal from core.market_data import get_quote from utils.notifications import send_toast class AlertWorker(QThread): alert_triggered = pyqtSignal(str, str, str) # symbol, type, message check_complete = pyqtSignal() def __init__(self, interval_seconds: int = 300): super().__init__() self._interval = interval_seconds self._running = False def run(self): self._running = True while self._running: self._check_alerts() self.check_complete.emit() for _ in range(self._interval * 10): if not self._running: return self.msleep(100) def stop(self): self._running = False def _check_alerts(self): from db.database import get_session from db.models import Alert from datetime import datetime with get_session() as session: active = session.query(Alert).filter_by(is_active=True).all() for alert in active: try: quote = get_quote(alert.symbol, force=True) price = quote.get("price", 0.0) volume = quote.get("volume", 0) triggered = False message = "" if alert.alert_type == "price_above" and price >= alert.target_value: triggered = True message = f"{alert.symbol} hit ${price:.2f} (above ${alert.target_value:.2f})" elif alert.alert_type == "price_below" and price <= alert.target_value: triggered = True message = f"{alert.symbol} hit ${price:.2f} (below ${alert.target_value:.2f})" elif alert.alert_type == "volume_spike": avg_vol = quote.get("avg_volume") or (volume / 1.5) if avg_vol and volume >= avg_vol * alert.target_value: triggered = True message = f"{alert.symbol} volume spike: {volume:,} ({alert.target_value:.1f}x avg)" if triggered: alert.is_active = False alert.triggered_at = datetime.utcnow() session.commit() send_toast("StockMind Alert", message) self.alert_triggered.emit(alert.symbol, alert.alert_type, message) except Exception: continue