@ -223,14 +223,14 @@ async def get_alpaca_price_data(
@router.get (
" /intraday " ,
response_model = AlpacaMultiBarsResponse ,
summary = " Get intraday bars for multiple tickers via Alpaca " ,
summary = " Get intraday bars for multiple tickers via Alpaca (DB-backed) " ,
description = (
" Fetch intraday OHLCV bars for up to ~500 tickers in one call. \n \n "
" Fetch intraday OHLCV bars for up to ~500 tickers. Results are stored in DB "
" so subsequent calls for the same period skip Alpaca. \n \n "
" - `tickers`: comma-separated list, e.g. `AAPL,MSFT,BF-B` \n "
" - `interval`: `1m`, `5m`, `15m`, `30m`, `1h` (also accepts `5min`, `15min`, etc.) \n "
" - Ticker normalization: `BF-B` → `BF.B` handled automatically; "
" response keys use the original symbol names. \n "
" - **No cache** — always fetches from Alpaca. \n "
" - Ticker normalization: `BF-B` → `BF.B` handled automatically. \n "
" - During market hours, same-day requests always fetch from Alpaca (new bars forming). \n "
" - Requires `ALPACA_API_KEY` / `ALPACA_SECRET_KEY`. "
) ,
)
@ -239,57 +239,47 @@ async def get_alpaca_intraday_multi(
interval : str = Query ( " 5m " , description = " Interval: 1m, 5m, 15m, 30m, 1h (or 5min, 15min, etc.) " ) ,
start_date : Optional [ date ] = Query ( None , description = " Start date (YYYY-MM-DD) " ) ,
end_date : Optional [ date ] = Query ( None , description = " End date (YYYY-MM-DD) " ) ,
limit : int = Query ( 10000 , ge = 1 , le = 50000 , description = " Max bars per symbol " ) ,
force_refresh : bool = Query ( False , description = " Re-fetch from Alpaca even if DB has data " ) ,
db : AsyncSession = Depends ( get_db ) ,
) :
""" Multi-ticker intraday bars via Alpaca (ORB engine interface)."""
""" Multi-ticker intraday bars via Alpaca with DB storage (ORB engine interface)."""
symbols = [ s . strip ( ) . upper ( ) for s in tickers . split ( " , " ) if s . strip ( ) ]
if not symbols :
raise HTTPException ( status_code = 400 , detail = " No tickers provided. " )
if len ( symbols ) > 1000 :
raise HTTPException ( status_code = 400 , detail = " Maximum 1000 tickers per request. " )
client = AlpacaClient ( )
if not client . is_configured ( ) :
raise HTTPException ( status_code = 503 , detail = " Alpaca API keys not configured. " )
# Build reverse-map: normalized_alpaca_symbol → original_input_symbol
reverse_map = { normalize_ticker ( s ) : s for s in symbols }
svc = _require_alpaca ( )
start_str = start_date . isoformat ( ) if start_date else None
end_str = end_date . isoformat ( ) if end_date else None
# Default date range: today
_start = start_date or date . today ( )
_end = end_date or date . today ( )
start_dt = datetime . combine ( _start , datetime . min . time ( ) ) . replace ( tzinfo = timezone . utc )
end_dt = datetime . combine ( _end , datetime . max . time ( ) ) . replace ( tzinfo = timezone . utc )
try :
raw = await client . get_multi_bars (
symbols = symbols ,
timeframe = interval ,
start = start_str ,
end = end_str ,
limit = min ( limit , 10000 ) ,
data = await svc . get_or_fetch_multi_bars (
db , symbols , start_dt , end_dt , interval , force_refresh
)
except Exception as e :
raise HTTPException ( status_code = 502 , detail = f " Alpaca API error: { e } " )
finally :
await client. close ( )
await svc. client. close ( )
# Re-key results back to original input symbols and reshape bar dicts
bars : dict = { }
for alpaca_sym , bar_list in raw . items ( ) :
original_sym = reverse_map . get ( alpaca_sym , alpaca_sym )
bars [ original_sym ] = [
bars = {
ticker : [
{
" timestamp " : b[ " t " ] ,
" open " : b. get ( " o " ) ,
" high " : b. get ( " h " ) ,
" low " : b. get ( " l " ) ,
" close " : b. get ( " c " ) ,
" volume " : b. get ( " v " ) ,
" timestamp " : row . date . isoformat ( ) ,
" open " : row . open ,
" high " : row. high ,
" low " : row. low ,
" close " : row. close ,
" volume " : row. volume ,
}
for b in bar_list
for row in rows
]
# Ensure every requested symbol appears in the response (empty list if no data)
for sym in symbols :
bars . setdefault ( sym , [ ] )
for ticker , rows in data . items ( )
}
return AlpacaMultiBarsResponse (
interval = interval ,