77 lines
2.9 KiB
Python
77 lines
2.9 KiB
Python
"""
|
|
Periodic Saxo-priced Watchlist refresh + wavelet recompute — mirrors services/saxo_scheduler.py's
|
|
pattern (own thread, own config-driven interval, `while not stop.wait(0)` so the first pass runs
|
|
immediately on startup). Independent of services/auto_cycle.py's once-a-day cycle, which also runs
|
|
this same computation as one of its steps but only at cycle cadence — this lets the Dashboard's
|
|
Wavelets Signal card and Instrument Analysis's cached Wavelet tab (services.wavelet_signals.
|
|
scan_watchlist_wavelet_signals writes both) stay current without waiting for, or manually
|
|
triggering, a full cycle.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import threading
|
|
import uuid
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_thread: threading.Thread | None = None
|
|
_stop = threading.Event()
|
|
|
|
DEFAULT_REFRESH_MINUTES = 15
|
|
|
|
|
|
def get_settings() -> dict:
|
|
from .database import get_config
|
|
enabled = (get_config("wavelet_refresh_enabled") or "true").lower() == "true"
|
|
try:
|
|
minutes = float(get_config("wavelet_refresh_minutes") or str(DEFAULT_REFRESH_MINUTES))
|
|
except (TypeError, ValueError):
|
|
minutes = DEFAULT_REFRESH_MINUTES
|
|
return {"enabled": enabled, "refresh_minutes": minutes}
|
|
|
|
|
|
def set_settings(enabled: bool, refresh_minutes: float) -> None:
|
|
from .database import set_config
|
|
set_config("wavelet_refresh_enabled", "true" if enabled else "false")
|
|
set_config("wavelet_refresh_minutes", str(max(1.0, refresh_minutes)))
|
|
|
|
|
|
def run_refresh_pass() -> int:
|
|
"""One pass over the whole Watchlist: Saxo-first price fetch + wavelet recompute (see
|
|
services.wavelet_signals.scan_watchlist_wavelet_signals) — the same work the daily cycle's
|
|
own wavelet step does, just callable on its own cadence. Shared by the periodic loop and
|
|
the manual 'refresh now' button. Returns the number of signal rows written."""
|
|
from .wavelet_signals import compute_and_save_wavelet_signals
|
|
run_id = f"refresh-{uuid.uuid4().hex[:10]}"
|
|
try:
|
|
results = compute_and_save_wavelet_signals(run_id)
|
|
logger.info(f"[Wavelet Scheduler] Refresh pass complete: {len(results)} signal rows ({run_id})")
|
|
return len(results)
|
|
except Exception as e:
|
|
logger.warning(f"[Wavelet Scheduler] Refresh pass failed: {e}")
|
|
return 0
|
|
|
|
|
|
def _loop(stop: threading.Event):
|
|
while not stop.wait(0):
|
|
settings = get_settings()
|
|
if not settings["enabled"]:
|
|
stop.wait(timeout=300) # re-check periodically in case it gets enabled without a restart
|
|
continue
|
|
run_refresh_pass()
|
|
stop.wait(timeout=settings["refresh_minutes"] * 60)
|
|
|
|
|
|
def start_wavelet_scheduler():
|
|
global _thread
|
|
_stop.clear()
|
|
if not (_thread and _thread.is_alive()):
|
|
_thread = threading.Thread(target=_loop, args=(_stop,), name="wavelet-refresh", daemon=True)
|
|
_thread.start()
|
|
logger.info("[Wavelet Scheduler] Started")
|
|
|
|
|
|
def stop_wavelet_scheduler():
|
|
_stop.set()
|