fix: /alpaca/intraday/{ticker} SIP 피드로 변경 + 어제까지 날짜 제한

- fetch_bars_raw()에 feed 파라미터 추가
- /intraday/{ticker}: IEX → SIP, end_date >= 오늘이면 400 에러
- 캐시 TTL: 5분 → 24시간 (과거 데이터는 변하지 않음)
- openapi.json 업데이트

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
I Luk Kim 4 months ago
parent 75b3a356d0
commit 800bb9b4a6

@ -413,36 +413,47 @@ async def get_alpaca_intraday_today(
@router.get(
"/intraday/{ticker}",
response_model=AlpacaIntradayResponse,
summary="Get intraday candles from Alpaca (single ticker, IEX feed)",
summary="Get historical intraday candles from Alpaca (single ticker, SIP feed)",
description=(
"단일 종목의 분봉 데이터를 Alpaca **IEX 피드**로 직접 가져옵니다.\n\n"
"**데이터 소스: IEX 피드**\n\n"
"단일 종목의 과거 분봉 데이터를 Alpaca **SIP 피드**로 가져옵니다.\n\n"
"**데이터 소스: SIP 피드**\n\n"
"| 항목 | 내용 |\n"
"|------|------|\n"
"| 피드 | **IEX** (IEX 거래소 단일) |\n"
"| 지연 | **실시간** (지연 없음) |\n"
"| 거래량 커버리지 | 미국 전체 시장의 약 **2~5%** |\n"
"| 피드 | **SIP** (전체 미국 거래소 통합) |\n"
"| 거래량 커버리지 | **100%** |\n"
"| OHLCV 정확도 | **정확** |\n"
"| 조회 가능 범위 | **2016년~어제** (당일 조회 시 400 에러) |\n"
"| DB 저장 | **없음** — 매 요청 Alpaca 직접 호출 |\n"
"| 캐시 | Redis **5분 TTL** |\n\n"
"멀티 종목이 필요하면 → `GET /api/v1/alpaca/intraday/today` (오늘) 또는 "
"`GET /api/v1/alpaca/intraday` (과거, SIP)\n\n"
"| 캐시 | Redis **24시간 TTL** |\n\n"
"당일 실시간 데이터가 필요하면 → `GET /api/v1/alpaca/intraday/today`\n\n"
"- Requires `ALPACA_API_KEY` / `ALPACA_SECRET_KEY`"
),
)
@with_cache(namespace="alpaca:intraday", ttl=300, key_params=["ticker", "interval", "start_date", "end_date"])
@with_cache(namespace="alpaca:intraday", ttl=86400, key_params=["ticker", "interval", "start_date", "end_date"])
async def get_alpaca_intraday(
ticker: str,
response: Response,
interval: str = Query("1m", description="Interval: 1m, 5m, 15m, 1h"),
start_date: Optional[date] = Query(None, description="Start date"),
end_date: Optional[date] = Query(None, description="End date"),
interval: str = Query("5m", description="Interval: 1m, 5m, 15m, 30m, 1h"),
start_date: Optional[date] = Query(None, description="Start date (YYYY-MM-DD). Default: yesterday"),
end_date: Optional[date] = Query(None, description="End date (YYYY-MM-DD). Must be before today. Default: yesterday"),
limit: int = Query(1000, ge=1, le=10000, description="Max candles"),
force_refresh: bool = Query(False, description="Bypass cache"),
):
yesterday = date.today() - timedelta(days=1)
_end = end_date or yesterday
if _end >= date.today():
raise HTTPException(
status_code=400,
detail="이 엔드포인트는 어제(yesterday)까지의 과거 데이터만 조회 가능합니다. "
"당일 실시간 데이터는 GET /api/v1/alpaca/intraday/today 를 사용하세요.",
)
svc = _require_alpaca()
start_dt = datetime.combine(start_date, datetime.min.time()).replace(tzinfo=timezone.utc) if start_date else None
end_dt = datetime.combine(end_date, datetime.min.time()).replace(tzinfo=timezone.utc) if end_date else None
_start = start_date or yesterday
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:
bars = await svc.fetch_bars_raw(
@ -450,6 +461,7 @@ async def get_alpaca_intraday(
interval=interval,
start_date=start_dt,
end_date=end_dt,
feed="sip",
)
bars = bars[:limit]
body_dict = {

@ -198,6 +198,7 @@ class AlpacaPriceService:
interval: str = "1d",
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None,
feed: Optional[str] = None,
) -> List[Dict]:
"""
Return raw bar dicts without touching the DB (useful for intraday / non-persistent use).
@ -210,6 +211,7 @@ class AlpacaPriceService:
timeframe=interval,
start=start_str,
end=end_str,
feed=feed,
)
return [

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save