From 801df41b4d83597c856b6a6a5f75c99b7e93e8bc Mon Sep 17 00:00:00 2001 From: Dennis Thiessen Date: Sun, 14 Jun 2026 14:59:28 +0200 Subject: [PATCH] report per-ticker R:R scanner progress (sidebar stuck at 0%) scan_rr set the total then called scan_all_tickers as one opaque await, so the runtime snapshot's processed count stayed 0 until the whole scan finished and jumped straight to 100%. scan_all_tickers now takes an optional progress_callback invoked per ticker; the scheduler wires it to _runtime_progress so the sidebar's live indicator advances as tickers are scanned. Co-Authored-By: Claude Opus 4.8 --- app/scheduler.py | 6 ++++++ app/services/rr_scanner_service.py | 16 ++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app/scheduler.py b/app/scheduler.py index 08b6559..6322b4e 100644 --- a/app/scheduler.py +++ b/app/scheduler.py @@ -654,9 +654,15 @@ async def scan_rr() -> None: total = len(symbols) _runtime_progress(job_name, processed=0, total=total) + def _on_progress(done: int, count: int, symbol: str) -> None: + _runtime_progress( + job_name, processed=done, total=count, current_ticker=symbol or None + ) + try: setups = await scan_all_tickers( db, rr_threshold=settings.default_rr_threshold, + progress_callback=_on_progress, ) processed = total or 0 _runtime_finish(job_name, "completed", processed=processed, total=total, message=f"Found {len(setups)} setups") diff --git a/app/services/rr_scanner_service.py b/app/services/rr_scanner_service.py index 0d320a8..0f3dfcd 100644 --- a/app/services/rr_scanner_service.py +++ b/app/services/rr_scanner_service.py @@ -10,6 +10,7 @@ from __future__ import annotations import json import logging +from collections.abc import Callable from datetime import datetime, timezone from sqlalchemy import and_, func, select @@ -237,13 +238,21 @@ async def scan_all_tickers( db: AsyncSession, rr_threshold: float = 1.5, atr_multiplier: float = 1.5, + progress_callback: Callable[[int, int, str], None] | None = None, ) -> list[TradeSetup]: - """Scan all tracked tickers for trade setups.""" + """Scan all tracked tickers for trade setups. + + ``progress_callback(processed, total, current_symbol)`` is invoked as each + ticker is scanned so callers (e.g. the scheduler) can surface live progress. + """ result = await db.execute(select(Ticker).order_by(Ticker.symbol)) tickers = list(result.scalars().all()) + total = len(tickers) all_setups: list[TradeSetup] = [] - for ticker in tickers: + for index, ticker in enumerate(tickers): + if progress_callback is not None: + progress_callback(index, total, ticker.symbol) try: # Refresh scores first so the scheduled scan works off current data. # Nothing else marks scores stale, so without this they'd never @@ -264,6 +273,9 @@ async def scan_all_tickers( except Exception: logger.exception("Error scanning ticker %s", ticker.symbol) + if progress_callback is not None and total: + progress_callback(total, total, "") + return all_setups