#!/usr/bin/env bash ############################################################################### # Warren Scanner - Automated Multi-Market Scheduler # Runs warren_scan.py with --market ALL flag # Supports error handling, logging, and multi-attachment email notifications ############################################################################### # Configuration SCRIPT_DIR="/mnt/ssd/data/python-lab/Trading" VENV_DIR="$SCRIPT_DIR/venv" PYTHON_SCRIPT="$SCRIPT_DIR/warren_scan.py" LOG_DIR="$SCRIPT_DIR/logs" LOG_FILE="$LOG_DIR/warren_scan_cron.log" EMAIL_TO="maurof.gagliardi@gmail.com" TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S") # Create logs directory if it doesn't exist mkdir -p "$LOG_DIR" # Function to log messages log() { echo "[$TIMESTAMP] $1" | tee -a "$LOG_FILE" } # Function to send email notification with multiple attachments send_notification() { local subject="$1" local body="$2" shift 2 local attachments=("$@") # Using mail command (requires mailutils package) if command -v mail &> /dev/null; then if [ ${#attachments[@]} -gt 0 ]; then # Build attachment arguments for mail command local attach_args=() for attachment in "${attachments[@]}"; do if [ -f "$attachment" ]; then attach_args+=("-A" "$attachment") fi done # Send with attachments if [ ${#attach_args[@]} -gt 0 ]; then echo -e "$body" | mail -s "$subject" "${attach_args[@]}" "$EMAIL_TO" else # Fallback: no valid attachments echo -e "$body" | mail -s "$subject" "$EMAIL_TO" fi else # Send without attachment echo -e "$body" | mail -s "$subject" "$EMAIL_TO" fi else log "WARNING: mail command not found. Install mailutils for email notifications." fi } # Main execution log "==========================================" log "Starting Warren Scanner Multi-Market Scan" log "==========================================" # Change to script directory cd "$SCRIPT_DIR" || { log "ERROR: Cannot change to directory $SCRIPT_DIR" send_notification "Warren Scanner FAILED" "Cannot access script directory: $SCRIPT_DIR" exit 1 } # Check internet connectivity log "Checking internet connectivity..." if ! ping -c 1 8.8.8.8 &> /dev/null; then log "ERROR: No internet connection" send_notification "Warren Scanner FAILED" "No internet connection available" exit 1 fi # Backup database log "Backing up database..." DB_FILE="$SCRIPT_DIR/data/trading_system.db" if [ -f "$DB_FILE" ]; then cp "$DB_FILE" "$DB_FILE.backup.$(date +%Y%m%d)" # Keep only last 4 backups ls -t "$DB_FILE.backup."* 2>/dev/null | tail -n +5 | xargs -r rm log "Database backed up successfully" fi # Activate virtual environment log "Activating virtual environment..." source "$VENV_DIR/bin/activate" || { log "ERROR: Cannot activate virtual environment" send_notification "Warren Scanner FAILED" "Cannot activate virtual environment at $VENV_DIR" exit 1 } # Run the scanner with --market ALL flag log "Running warren_scan.py --market ALL..." python3 "$PYTHON_SCRIPT" --market ALL >> "$LOG_FILE" 2>&1 EXIT_CODE=$? # Check exit status if [ $EXIT_CODE -eq 0 ]; then log "SUCCESS: Warren Scanner completed successfully" # Find all HTML files generated in the last 60 minutes # This ensures we get only the current run's files CURRENT_TIMESTAMP=$(date +"%Y%m%d_%H") ALL_HTML_FILES=() MARKETS_SCANNED=() # Detect market reports by pattern: warren_scan_{MARKET}_{TIMESTAMP}.html for market_code in IT FR DE USA; do # Find the most recent file for this market (created in last hour) MARKET_HTML=$(find "$SCRIPT_DIR" -name "warren_scan_${market_code}_*.html" -type f -mmin -60 | sort -r | head -1) if [ -n "$MARKET_HTML" ] && [ -f "$MARKET_HTML" ]; then ALL_HTML_FILES+=("$MARKET_HTML") MARKETS_SCANNED+=("$market_code") log "Found report for $market_code: $MARKET_HTML" fi done if [ ${#ALL_HTML_FILES[@]} -eq 0 ]; then log "WARNING: No HTML files found. Scanner may have failed silently." send_notification "⚠️ Warren Scanner - No Reports Generated" "Il scanner è terminato ma non ha generato report HTML.\n\nVerifica i log: $LOG_FILE" exit 1 fi log "Found ${#ALL_HTML_FILES[@]} market reports: ${MARKETS_SCANNED[*]}" # Initialize aggregate counters TOTAL_STRONG_BUY=0 TOTAL_BUY=0 TOTAL_STOCKS_ANALYZED=0 TOTAL_STOCKS_AVAILABLE=0 MIN_QUALITY=100.0 TOTAL_STALE_DATA=0 TOTAL_FAILED_DATA=0 HAS_UPGRADES=0 # Aggregate metrics from all market reports for HTML_FILE in "${ALL_HTML_FILES[@]}"; do # Extract STRONG BUY count STRONG_BUY=$(grep -c 'class="stock strong-buy"' "$HTML_FILE" 2>/dev/null || echo "0") STRONG_BUY=$(echo "$STRONG_BUY" | tr -d '\n' | tr -d ' ') TOTAL_STRONG_BUY=$((TOTAL_STRONG_BUY + STRONG_BUY)) # Extract BUY count BUY=$(grep -c 'class="stock buy"' "$HTML_FILE" 2>/dev/null || echo "0") BUY=$(echo "$BUY" | tr -d '\n' | tr -d ' ') TOTAL_BUY=$((TOTAL_BUY + BUY)) # Extract stocks analyzed (format: "40/40") STOCKS_ANALYZED=$(grep -oP "Azioni analizzate: \K[0-9]+/[0-9]+" "$HTML_FILE" | head -1) if [ -n "$STOCKS_ANALYZED" ]; then ANALYZED=$(echo "$STOCKS_ANALYZED" | cut -d'/' -f1) AVAILABLE=$(echo "$STOCKS_ANALYZED" | cut -d'/' -f2) TOTAL_STOCKS_ANALYZED=$((TOTAL_STOCKS_ANALYZED + ANALYZED)) TOTAL_STOCKS_AVAILABLE=$((TOTAL_STOCKS_AVAILABLE + AVAILABLE)) fi # Extract data quality percentage (keep minimum) QUALITY_PCT=$(grep -oP "Qualità Dati: \K[0-9]+\.[0-9]+" "$HTML_FILE" 2>/dev/null || echo "100.0") if (( $(echo "$QUALITY_PCT < $MIN_QUALITY" | bc -l) )); then MIN_QUALITY=$QUALITY_PCT fi # Extract stale data count STALE=$(grep -oP "⚠️ \K[0-9]+ con dati vecchi" "$HTML_FILE" 2>/dev/null | grep -o "^[0-9]*" || echo "0") TOTAL_STALE_DATA=$((TOTAL_STALE_DATA + STALE)) # Extract failed data count FAILED=$(grep -oP "❌ \K[0-9]+ errori" "$HTML_FILE" 2>/dev/null | grep -o "^[0-9]*" || echo "0") TOTAL_FAILED_DATA=$((TOTAL_FAILED_DATA + FAILED)) # Check for upgrades if grep -q "Novità Questa Settimana" "$HTML_FILE"; then HAS_UPGRADES=1 fi done TOTAL_ALL_BUY=$((TOTAL_STRONG_BUY + TOTAL_BUY)) # Build email summary with aggregated metrics SUMMARY="Warren Scanner Multi-Market completato con successo!\n\n" SUMMARY+="🌍 Mercati analizzati: ${MARKETS_SCANNED[*]}\n\n" SUMMARY+="📊 Risultati Aggregati:\n" if [ "$TOTAL_STRONG_BUY" -gt 0 ]; then SUMMARY+=" • 🌟 STRONG BUY: ${TOTAL_STRONG_BUY}\n" fi if [ "$TOTAL_BUY" -gt 0 ]; then SUMMARY+=" • 💰 BUY: ${TOTAL_BUY}\n" fi if [ "$TOTAL_ALL_BUY" -eq 0 ]; then SUMMARY+=" • Nessuna raccomandazione BUY trovata\n" fi SUMMARY+=" • Azioni analizzate: ${TOTAL_STOCKS_ANALYZED}/${TOTAL_STOCKS_AVAILABLE}\n" # Add quality alert if < 95% MIN_QUALITY_INT=$(echo "$MIN_QUALITY" | cut -d'.' -f1) if [ "$MIN_QUALITY_INT" -lt 95 ]; then SUMMARY+=" • ⚠️ Qualità dati (minimo): ${MIN_QUALITY}%" if [ "$TOTAL_STALE_DATA" -gt 0 ]; then SUMMARY+=" (${TOTAL_STALE_DATA} dati vecchi)" fi if [ "$TOTAL_FAILED_DATA" -gt 0 ]; then SUMMARY+=" (${TOTAL_FAILED_DATA} errori)" fi SUMMARY+="\n" else SUMMARY+=" • ✅ Qualità dati (minimo): ${MIN_QUALITY}%\n" fi if [ "$HAS_UPGRADES" -gt 0 ]; then SUMMARY+=" • 📣 Ci sono upgrade di raccomandazione questa settimana!\n" fi SUMMARY+="\n📎 Report HTML allegati:\n" for market in "${MARKETS_SCANNED[@]}"; do case "$market" in IT) SUMMARY+=" • 🇮🇹 FTSE MIB (Italia)\n" ;; FR) SUMMARY+=" • 🇫🇷 CAC 40 (Francia)\n" ;; DE) SUMMARY+=" • 🇩🇪 DAX 40 (Germania)\n" ;; USA) SUMMARY+=" • 🇺🇸 Dow Jones 30 (USA)\n" ;; esac done SUMMARY+="\nApri gli allegati nel browser per vedere le analisi dettagliate con tutti i dettagli, grafici e raccomandazioni per ogni mercato." # Create email subject based on aggregated results if [ "$TOTAL_STRONG_BUY" -gt 0 ]; then SUBJECT="🌟 Warren Multi-Market - ${TOTAL_STRONG_BUY} STRONG BUY + ${TOTAL_BUY} BUY" elif [ "$TOTAL_BUY" -gt 0 ]; then SUBJECT="✅ Warren Multi-Market - ${TOTAL_BUY} BUY trovate" else SUBJECT="📊 Warren Multi-Market - Nessun BUY questa settimana" fi # Send success notification with all HTML attachments send_notification "$SUBJECT" "$SUMMARY" "${ALL_HTML_FILES[@]}" # Send CRITICAL alert if quality < 90% if [ "$MIN_QUALITY_INT" -lt 90 ]; then log "⚠️ CRITICAL: Data quality below 90% ($MIN_QUALITY%)" CRITICAL_SUBJECT="🚨 ALERT CRITICO - Warren Multi-Market Qualità Dati" CRITICAL_BODY="ATTENZIONE: La qualità dei dati è scesa sotto il 90% in almeno un mercato!\n\n" CRITICAL_BODY+="📊 Metriche Qualità:\n" CRITICAL_BODY+=" • Qualità minima rilevata: ${MIN_QUALITY}%\n" CRITICAL_BODY+=" • Soglia critica: 90%\n" CRITICAL_BODY+=" • Mercati analizzati: ${MARKETS_SCANNED[*]}\n" if [ "$TOTAL_STALE_DATA" -gt 0 ]; then CRITICAL_BODY+=" • ⏰ Dati vecchi (totale): ${TOTAL_STALE_DATA} ticker\n" fi if [ "$TOTAL_FAILED_DATA" -gt 0 ]; then CRITICAL_BODY+=" • ❌ Errori download (totale): ${TOTAL_FAILED_DATA} ticker\n" fi CRITICAL_BODY+="\n⚠️ Possibili Cause:\n" CRITICAL_BODY+=" • Yahoo Finance down o rate limiting\n" CRITICAL_BODY+=" • Problemi di connessione internet\n" CRITICAL_BODY+=" • Bug nel codice update_daily_prices()\n" CRITICAL_BODY+=" • Ticker delisted o con suffissi errati\n" CRITICAL_BODY+="\n💡 Azioni Immediate:\n" CRITICAL_BODY+=" 1. Controllare se Yahoo Finance è online\n" CRITICAL_BODY+=" 2. Eseguire manualmente: cd $SCRIPT_DIR && source venv/bin/activate && python warren_scan.py --market ALL\n" CRITICAL_BODY+=" 3. Verificare log: $LOG_FILE\n" CRITICAL_BODY+=" 4. Controllare i report HTML allegati per dettagli sui ticker falliti\n" CRITICAL_BODY+=" 5. Se persiste, considerare run manuale domenica prossima\n" CRITICAL_BODY+="\n📎 Report HTML completi allegati all'email principale.\n" # Send critical alert (no attachment, just urgent message) send_notification "$CRITICAL_SUBJECT" "$CRITICAL_BODY" log "Critical quality alert email sent to $EMAIL_TO" fi # Clean up old HTML files (keep last 12 weeks = 90 days) # Now handles all market patterns log "Cleaning up old scan results..." OLD_FILES=$(find "$SCRIPT_DIR" -name "warren_scan_*.html" -type f -mtime +90 | wc -l) if [ "$OLD_FILES" -gt 0 ]; then find "$SCRIPT_DIR" -name "warren_scan_*.html" -type f -mtime +90 -delete log "Deleted $OLD_FILES old HTML files (>90 days)" fi else log "ERROR: Warren Scanner failed with exit code $EXIT_CODE" # Check if any partial reports were generated PARTIAL_HTML_FILES=() for market_code in IT FR DE USA; do MARKET_HTML=$(find "$SCRIPT_DIR" -name "warren_scan_${market_code}_*.html" -type f -mmin -60 | sort -r | head -1) if [ -n "$MARKET_HTML" ] && [ -f "$MARKET_HTML" ]; then PARTIAL_HTML_FILES+=("$MARKET_HTML") fi done # Send failure notification with error details ERROR_LOG=$(tail -30 "$LOG_FILE") FAILURE_MSG="Exit code: $EXIT_CODE\n\n" if [ ${#PARTIAL_HTML_FILES[@]} -gt 0 ]; then FAILURE_MSG+="⚠️ NOTA: Alcuni mercati potrebbero essere stati completati con successo.\n" FAILURE_MSG+="Report parziali trovati: ${#PARTIAL_HTML_FILES[@]}\n\n" fi FAILURE_MSG+="Ultimi 30 righe del log:\n\n$ERROR_LOG" # Send with partial reports if available if [ ${#PARTIAL_HTML_FILES[@]} -gt 0 ]; then send_notification "❌ Warren Multi-Market PARZIALMENTE FALLITO" "$FAILURE_MSG" "${PARTIAL_HTML_FILES[@]}" else send_notification "❌ Warren Multi-Market FALLITO" "$FAILURE_MSG" fi fi log "==========================================" log "Warren Multi-Market Scanner finished (exit code: $EXIT_CODE)" log "==========================================" log "" exit $EXIT_CODE