forked from TauricResearch/TradingAgents
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun_scheduler.py
More file actions
executable file
·134 lines (115 loc) · 3.59 KB
/
Copy pathrun_scheduler.py
File metadata and controls
executable file
·134 lines (115 loc) · 3.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/env python3
"""
Standalone scheduler daemon that runs independently of Streamlit app.
Usage:
python run_scheduler.py # Run in foreground
python run_scheduler.py & # Run in background
python run_scheduler.py stop # Stop the daemon
"""
import os
import sys
import signal
import time
from datetime import datetime
from scheduler_service import AnalysisScheduler
# PID file to track running process
PID_FILE = "scheduler.pid"
def save_pid():
"""Save current process ID to file."""
with open(PID_FILE, "w") as f:
f.write(str(os.getpid()))
def load_pid():
"""Load process ID from file."""
if not os.path.exists(PID_FILE):
return None
try:
with open(PID_FILE, "r") as f:
return int(f.read().strip())
except:
return None
def remove_pid():
"""Remove PID file."""
if os.path.exists(PID_FILE):
os.remove(PID_FILE)
def is_running():
"""Check if scheduler is already running."""
pid = load_pid()
if pid is None:
return False
try:
os.kill(pid, 0) # Check if process exists
return True
except OSError:
return False
def stop_scheduler():
"""Stop the running scheduler daemon."""
pid = load_pid()
if pid is None:
print("No PID file found. Scheduler may not be running.")
return False
try:
os.kill(pid, signal.SIGTERM)
print(f"Sent SIGTERM to process {pid}")
# Wait for process to stop
for i in range(10):
try:
os.kill(pid, 0)
time.sleep(0.5)
except OSError:
print("Scheduler stopped successfully")
remove_pid()
return True
# Force kill if still running
print("Process did not stop gracefully, forcing...")
os.kill(pid, signal.SIGKILL)
remove_pid()
return True
except OSError as e:
print(f"Error stopping scheduler: {e}")
remove_pid()
return False
def signal_handler(signum, frame):
"""Handle termination signals."""
print(f"\n[{datetime.now()}] Received signal {signum}, shutting down...")
remove_pid()
sys.exit(0)
def run_scheduler():
"""Run the scheduler daemon."""
# Check if already running
if is_running():
print("Scheduler is already running!")
print(f"PID: {load_pid()}")
print("Use 'python run_scheduler.py stop' to stop it first.")
sys.exit(1)
# Save PID
save_pid()
print(f"[{datetime.now()}] Scheduler daemon started (PID: {os.getpid()})")
print(f"PID saved to {PID_FILE}")
# Register signal handlers
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
# Initialize scheduler
scheduler = AnalysisScheduler()
# List current jobs
jobs = scheduler.list_jobs()
print(f"[{datetime.now()}] Found {len(jobs)} scheduled job(s)")
for job in jobs:
print(f" - {job['ticker']}: {job['schedule']} (Next run: {job['next_run']})")
print(f"[{datetime.now()}] Scheduler is running. Press Ctrl+C to stop.")
print(f"[{datetime.now()}] Logs will be saved to logs/ directory")
try:
# Keep running forever
while True:
time.sleep(1)
except KeyboardInterrupt:
print(f"\n[{datetime.now()}] Keyboard interrupt received, shutting down...")
finally:
remove_pid()
def main():
"""Main entry point."""
if len(sys.argv) > 1 and sys.argv[1] == "stop":
stop_scheduler()
else:
run_scheduler()
if __name__ == "__main__":
main()