report per-ticker R:R scanner progress (sidebar stuck at 0%)
Deploy / lint (push) Successful in 6s
Deploy / test (push) Successful in 34s
Deploy / deploy (push) Successful in 23s

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 <noreply@anthropic.com>
This commit is contained in:
2026-06-14 14:59:28 +02:00
parent 90618d186f
commit 801df41b4d
2 changed files with 20 additions and 2 deletions
+6
View File
@@ -654,9 +654,15 @@ async def scan_rr() -> None:
total = len(symbols) total = len(symbols)
_runtime_progress(job_name, processed=0, total=total) _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: try:
setups = await scan_all_tickers( setups = await scan_all_tickers(
db, rr_threshold=settings.default_rr_threshold, db, rr_threshold=settings.default_rr_threshold,
progress_callback=_on_progress,
) )
processed = total or 0 processed = total or 0
_runtime_finish(job_name, "completed", processed=processed, total=total, message=f"Found {len(setups)} setups") _runtime_finish(job_name, "completed", processed=processed, total=total, message=f"Found {len(setups)} setups")
+14 -2
View File
@@ -10,6 +10,7 @@ from __future__ import annotations
import json import json
import logging import logging
from collections.abc import Callable
from datetime import datetime, timezone from datetime import datetime, timezone
from sqlalchemy import and_, func, select from sqlalchemy import and_, func, select
@@ -237,13 +238,21 @@ async def scan_all_tickers(
db: AsyncSession, db: AsyncSession,
rr_threshold: float = 1.5, rr_threshold: float = 1.5,
atr_multiplier: float = 1.5, atr_multiplier: float = 1.5,
progress_callback: Callable[[int, int, str], None] | None = None,
) -> list[TradeSetup]: ) -> 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)) result = await db.execute(select(Ticker).order_by(Ticker.symbol))
tickers = list(result.scalars().all()) tickers = list(result.scalars().all())
total = len(tickers)
all_setups: list[TradeSetup] = [] 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: try:
# Refresh scores first so the scheduled scan works off current data. # Refresh scores first so the scheduled scan works off current data.
# Nothing else marks scores stale, so without this they'd never # Nothing else marks scores stale, so without this they'd never
@@ -264,6 +273,9 @@ async def scan_all_tickers(
except Exception: except Exception:
logger.exception("Error scanning ticker %s", ticker.symbol) logger.exception("Error scanning ticker %s", ticker.symbol)
if progress_callback is not None and total:
progress_callback(total, total, "")
return all_setups return all_setups