RayMelius Claude Sonnet 4.6 commited on
Commit
d185c3e
Β·
1 Parent(s): ec76b2b

Fix scheduler: wrong path + no timezone = never fires

Browse files

Bug 1 β€” Wrong schedule file path:
SCHEDULE_FILE defaulted to /app/shared_data/market_schedule.txt but
the volume/Dockerfile uses /app/data/. File was never found so
load_market_schedule() silently returned {}, scheduler never triggered.
Fixed default path + added ENV in Dockerfile as belt-and-suspenders.

Bug 2 β€” Server in UTC, market in Athens (GMT+2):
datetime.now() returned UTC time; at 11:46 Athens = 09:46 UTC the
scheduler saw 09:46 < 10:00 and did nothing.
Added Timezone field to market_schedule.txt and _local_now(tz) helper
that uses zoneinfo (Python 3.9+) with pytz fallback.

market_schedule.txt now:
Timezone Europe/Athens
Start 10:00
End 17:00

Scheduler logs local time on every tick for observability.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Dockerfile CHANGED
@@ -41,6 +41,8 @@ WORKDIR /app
41
  COPY shared/ /app/shared/
42
  COPY shared_data/securities.txt /app/data/securities.txt
43
  COPY shared_data/market_schedule.txt /app/data/market_schedule.txt
 
 
44
 
45
  # Matcher service
46
  COPY matcher/matcher.py /app/matcher.py
 
41
  COPY shared/ /app/shared/
42
  COPY shared_data/securities.txt /app/data/securities.txt
43
  COPY shared_data/market_schedule.txt /app/data/market_schedule.txt
44
+ # Also expose schedule path via env so dashboard finds it
45
+ ENV SCHEDULE_FILE=/app/data/market_schedule.txt
46
 
47
  # Matcher service
48
  COPY matcher/matcher.py /app/matcher.py
dashboard/dashboard.py CHANGED
@@ -22,7 +22,7 @@ sse_clients_lock = threading.Lock()
22
  # Session state
23
  session_state = {"active": False, "start_time": None, "suspended": False, "mode": "automatic"}
24
 
25
- SCHEDULE_FILE = os.getenv("SCHEDULE_FILE", "/app/shared_data/market_schedule.txt")
26
  FRONTEND_URL = os.getenv("FRONTEND_URL", "")
27
 
28
  # ── OHLCV History ──────────────────────────────────────────────────────────────
@@ -301,11 +301,27 @@ def load_market_schedule():
301
  if not line or line.startswith("#"):
302
  continue
303
  parts = line.split()
304
- if len(parts) == 2:
305
  schedule[parts[0].lower()] = parts[1]
 
 
 
 
 
 
 
 
 
 
306
  except Exception:
307
  pass
308
- return schedule
 
 
 
 
 
 
309
 
310
 
311
  def _do_session_start():
@@ -354,13 +370,15 @@ def schedule_runner():
354
  if session_state.get("mode") == "automatic":
355
  sched = load_market_schedule()
356
  start_str = sched.get("start")
357
- end_str = sched.get("end")
 
358
  if start_str and end_str:
359
- now = datetime.datetime.now()
360
  sh, sm = int(start_str.split(":")[0]), int(start_str.split(":")[1])
361
- eh, em = int(end_str.split(":")[0]), int(end_str.split(":")[1])
362
  start_t = now.replace(hour=sh, minute=sm, second=0, microsecond=0)
363
  end_t = now.replace(hour=eh, minute=em, second=0, microsecond=0)
 
364
  if now >= end_t and session_state["active"]:
365
  print("[Scheduler] Auto end of day")
366
  _do_session_end()
 
22
  # Session state
23
  session_state = {"active": False, "start_time": None, "suspended": False, "mode": "automatic"}
24
 
25
+ SCHEDULE_FILE = os.getenv("SCHEDULE_FILE", "/app/data/market_schedule.txt")
26
  FRONTEND_URL = os.getenv("FRONTEND_URL", "")
27
 
28
  # ── OHLCV History ──────────────────────────────────────────────────────────────
 
301
  if not line or line.startswith("#"):
302
  continue
303
  parts = line.split()
304
+ if len(parts) >= 2:
305
  schedule[parts[0].lower()] = parts[1]
306
+ except Exception as e:
307
+ print(f"[Scheduler] Cannot read schedule file {SCHEDULE_FILE}: {e}")
308
+ return schedule
309
+
310
+
311
+ def _local_now(tz_name):
312
+ """Return current datetime in the given IANA timezone (e.g. 'Europe/Athens')."""
313
+ try:
314
+ from zoneinfo import ZoneInfo
315
+ return datetime.datetime.now(ZoneInfo(tz_name)).replace(tzinfo=None)
316
  except Exception:
317
  pass
318
+ try:
319
+ import pytz
320
+ tz = pytz.timezone(tz_name)
321
+ return datetime.datetime.now(tz).replace(tzinfo=None)
322
+ except Exception:
323
+ pass
324
+ return datetime.datetime.utcnow()
325
 
326
 
327
  def _do_session_start():
 
370
  if session_state.get("mode") == "automatic":
371
  sched = load_market_schedule()
372
  start_str = sched.get("start")
373
+ end_str = sched.get("end")
374
+ tz_name = sched.get("timezone", "UTC")
375
  if start_str and end_str:
376
+ now = _local_now(tz_name)
377
  sh, sm = int(start_str.split(":")[0]), int(start_str.split(":")[1])
378
+ eh, em = int(end_str.split(":")[0]), int(end_str.split(":")[1])
379
  start_t = now.replace(hour=sh, minute=sm, second=0, microsecond=0)
380
  end_t = now.replace(hour=eh, minute=em, second=0, microsecond=0)
381
+ print(f"[Scheduler] Local time ({tz_name}): {now.strftime('%H:%M')} window {start_str}-{end_str}")
382
  if now >= end_t and session_state["active"]:
383
  print("[Scheduler] Auto end of day")
384
  _do_session_end()
shared_data/market_schedule.txt CHANGED
@@ -1,2 +1,3 @@
 
1
  Start 10:00
2
  End 17:00
 
1
+ Timezone Europe/Athens
2
  Start 10:00
3
  End 17:00