fix: harden Structural S/R after OHLCV writes and surface cleanup failures
Deploy / lint (push) Successful in 8s
Deploy / test (push) Successful in 1m18s
Deploy / deploy (push) Successful in 40s

Honor custom S/R tolerance as a transient detect, refresh levels after OHLCV
mutations without failing committed price writes, report per-ticker S/R
rebuild failures from admin cleanup, and warn in the admin UI when refresh is partial.
This commit is contained in:
2026-07-18 13:44:34 +02:00
parent b0e33e1606
commit cad4b49e7c
11 changed files with 181 additions and 23 deletions
+38 -4
View File
@@ -318,14 +318,18 @@ async def update_ticker_universe_default(db: AsyncSession, universe: str) -> dic
# Data cleanup
# ---------------------------------------------------------------------------
async def cleanup_data(db: AsyncSession, older_than_days: int) -> dict[str, int]:
async def cleanup_data(db: AsyncSession, older_than_days: int) -> dict:
"""Delete OHLCV, sentiment, and fundamental records older than N days.
Preserves tickers, users, and latest scores.
Returns a dict with counts of deleted records per table.
Preserves tickers, users, and latest scores. After OHLCV pruning, rebuilds
Structural S/R for every ticker so chart levels match the remaining history.
Returns deleted-row counts plus S/R refresh outcomes. A per-ticker S/R
failure rolls the session back (so later tickers still run) and is listed
in ``sr_refresh_failures`` rather than aborting the whole cleanup.
"""
cutoff = datetime.now(timezone.utc) - timedelta(days=older_than_days)
counts: dict[str, int] = {}
counts: dict = {}
# OHLCV — date column is a date, compare with cutoff date
result = await db.execute(
@@ -346,6 +350,36 @@ async def cleanup_data(db: AsyncSession, older_than_days: int) -> dict[str, int]
counts["fundamentals"] = result.rowcount # type: ignore[assignment]
await db.commit()
counts["sr_refresh_ok"] = 0
counts["sr_refresh_failed"] = 0
counts["sr_refresh_failures"] = []
# Structural S/R is derived from OHLCV; recompute after history shrinks.
if counts["ohlcv"]:
from app.services.sr_service import recalculate_sr_levels
symbols = list(
(await db.execute(select(Ticker.symbol).order_by(Ticker.symbol))).scalars().all()
)
for symbol in symbols:
try:
await recalculate_sr_levels(db, symbol)
counts["sr_refresh_ok"] += 1
except Exception as exc:
logger.exception("S/R refresh after cleanup failed for %s", symbol)
try:
await db.rollback()
except Exception:
logger.exception(
"Session rollback after S/R cleanup failure also failed for %s",
symbol,
)
counts["sr_refresh_failed"] += 1
counts["sr_refresh_failures"].append(
{"symbol": symbol, "error": f"{type(exc).__name__}: {exc}"}
)
return counts