#!/usr/bin/env python3 """ Cron Scheduler Runs periodic maintenance tasks inside the Docker container using the `schedule` library. This replaces a system cron daemon and keeps all scheduling logic in Python. Scheduled tasks: - data_fetcher.py — daily at 02:00 UTC (full historical catch-up) - fetch_history.py — daily at 03:00 UTC (additional history fetch) - gap_detector.py — hourly at :15 (fill missing 1m candles) - backup_runner.py — daily at 04:00 UTC (pg_dump backup) """ import argparse import logging import os import subprocess import sys import time import schedule import signal from logging_utils import setup_logging shutdown_requested = False def handle_shutdown(signum, frame): global shutdown_requested shutdown_requested = True def run_data_fetcher(): try: logging.info("Running data_fetcher.py") subprocess.run([ sys.executable, "data_fetcher.py", "--coins", "BTC", "ETH", "SOL", "BNB", "HYPE", "SUI", "xyz:BRENTOIL", "xyz:CL", "xyz:GOLD", "xyz:SILVER", "mkts:USTECH", "xyz:XYZ100", "--interval", "1m", "--days", "7", "--log-level", "normal" ], check=True) except Exception as e: logging.error(f"Data fetcher failed: {e}") def run_fetch_history(): try: logging.info("Running fetch_history.py") subprocess.run([sys.executable, "fetch_history.py", "--log-level", "normal"], check=True) except Exception as e: logging.error(f"Fetch history failed: {e}") def run_gap_detector(): try: logging.info("Running gap_detector.py") subprocess.run([sys.executable, "scripts/gap_detector.py", "--log-level", "normal"], check=True) except Exception as e: logging.error(f"Gap detector failed: {e}") def run_backup(): try: logging.info("Running backup_runner.py") subprocess.run([sys.executable, "scripts/backup_runner.py", "--log-level", "normal"], check=True) except Exception as e: logging.error(f"Backup failed: {e}") def main(): parser = argparse.ArgumentParser(description="Run periodic maintenance tasks.") parser.add_argument("--log-level", default="normal", choices=['off', 'normal', 'debug']) args = parser.parse_args() signal.signal(signal.SIGTERM, handle_shutdown) signal.signal(signal.SIGINT, handle_shutdown) setup_logging(args.log_level, 'CronScheduler') # Schedule jobs schedule.every().day.at("02:00").do(run_data_fetcher) schedule.every().day.at("03:00").do(run_fetch_history) schedule.every().hour.at(":15").do(run_gap_detector) schedule.every().day.at("04:00").do(run_backup) logging.info("Cron scheduler started") while not shutdown_requested: schedule.run_pending() time.sleep(1) logging.info("Cron scheduler shutting down.") if __name__ == "__main__": main()