Problem
backend/app/market.py's _get_ticker_fast (and the equivalent info-fetching function) call yfinance's synchronous, network-blocking API directly. These functions are plain def, not async def, and are invoked directly — without asyncio.to_thread or run_in_executor — from async def route handlers in main.py such as /market/quotes and /market/indices.
Why this matters
FastAPI's async event loop is single-threaded for coroutine execution. A blocking network call inside an async def handler stalls the entire event loop for every other concurrent request — not just the one making the yfinance call — until that call returns or times out. Under real traffic (multiple users hitting the market/watchlist pages, or the periodic WebSocket refresh), this turns an occasional slow yfinance response into a global slowdown for the whole API, including unrelated /verify and /v1/fcg/verify requests.
Acceptance Criteria
Files likely to modify
finverify-terminal/backend/app/market.py
finverify-terminal/backend/app/main.py
Skills required
Python, asyncio, FastAPI, concurrency concepts
Estimated difficulty
3/5
Estimated effort
4–6 hours
Problem
backend/app/market.py's _get_ticker_fast (and the equivalent info-fetching function) call yfinance's synchronous, network-blocking API directly. These functions are plain def, not async def, and are invoked directly — without asyncio.to_thread or run_in_executor — from async def route handlers in main.py such as /market/quotes and /market/indices.
Why this matters
FastAPI's async event loop is single-threaded for coroutine execution. A blocking network call inside an async def handler stalls the entire event loop for every other concurrent request — not just the one making the yfinance call — until that call returns or times out. Under real traffic (multiple users hitting the market/watchlist pages, or the periodic WebSocket refresh), this turns an occasional slow yfinance response into a global slowdown for the whole API, including unrelated /verify and /v1/fcg/verify requests.
Acceptance Criteria
All synchronous
yfinancecalls inmarket.pyare offloaded viaasyncio.to_thread(or an equivalent bounded executor pattern) when called from async route handlersOffloaded provider calls use bounded concurrency (e.g.
asyncio.Semaphoreor an equivalent mechanism) to prevent unbounded parallelyfinancerequests under loadA route/service-level timeout is enforced around the offloaded provider call so slow or hung upstream requests cannot indefinitely occupy worker resources
No change to the existing cache/rate-limit semantics (10-second TTL, stale-fallback behavior)
Blocking provider functions remain synchronous, while async callers use a dedicated async wrapper/adapter that owns the offload, timeout, and concurrency control
A test or benchmark demonstrates that a slow/blocked
yfinancecall no longer prevents a concurrent/healthor/verifyrequest from completing promptlyTests added where appropriate
Documentation updated if necessary — a short comment explaining why the async offload exists, so it isn't accidentally reverted
(Optional) Metrics/logging distinguish cache hits, stale-cache hits, provider successes, provider timeouts, and provider errors
Files likely to modify
finverify-terminal/backend/app/market.py
finverify-terminal/backend/app/main.py
Skills required
Python, asyncio, FastAPI, concurrency concepts
Estimated difficulty
3/5
Estimated effort
4–6 hours