fix: _batch_check_missing_periods — today 포함 시 항상 re-fetch

_check_missing_periods는 수정됐지만 bulk 경로에서 사용하는
_batch_check_missing_periods에는 동일한 today 체크가 빠져 있었음.

POST /price/data/bulk로 당일 데이터 요청 시 DB에 mid-session으로
캐시된 partial-volume 데이터를 그대로 반환하는 버그.

end_date.date() >= today이면 모든 티커를 missing으로 처리하여
_check_missing_periods와 동일한 동작 보장.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
I Luk Kim 4 months ago
parent 7db2e28691
commit 73bf6115fa

@ -103,11 +103,22 @@ class PriceDataService:
end_date: datetime,
interval: str
) -> List[datetime]:
"""Check which periods are missing in the database"""
"""Check which periods are missing in the database.
Also treats today's data as always-missing so that end-of-day volumes
get re-fetched rather than returning a mid-session snapshot that was
cached earlier in the day.
"""
# If no date range provided, assume we need to fetch data
if start_date is None or end_date is None:
return [datetime.now()] # Return a dummy date to trigger fetch
# Always re-fetch if the range includes today: daily bars fetched mid-session
# are stored with partial (intraday) volume and must be refreshed after close.
today = datetime.now(timezone.utc).date()
if end_date.date() >= today:
return [datetime.now()]
# Check if we have any data for this ticker and interval
result = await db.execute(
select(PriceData.date)
@ -506,9 +517,22 @@ class PriceDataService:
if not rows:
return
# Single batch INSERT — skip rows that violate the unique constraint (ticker, date)
# Upsert: on conflict update OHLCV + updated_at so that stale intraday-cached rows
# get overwritten when force_refresh=True re-fetches final EOD data.
stmt = pg_insert(PriceData).values(rows)
stmt = stmt.on_conflict_do_nothing(constraint='uq_price_data')
stmt = stmt.on_conflict_do_update(
constraint='uq_price_data',
set_={
'open': stmt.excluded.open,
'high': stmt.excluded.high,
'low': stmt.excluded.low,
'close': stmt.excluded.close,
'volume': stmt.excluded.volume,
'adjusted_close': stmt.excluded.adjusted_close,
'data_source': stmt.excluded.data_source,
'updated_at': stmt.excluded.updated_at,
}
)
await db.execute(stmt)
async def _get_price_data_from_db(
@ -779,7 +803,17 @@ class PriceDataService:
end_date: datetime,
interval: str
) -> List[str]:
"""Batch check which tickers have missing periods"""
"""Batch check which tickers have missing periods.
If the range includes today, all tickers are treated as missing so that
mid-session cached rows (partial volume) are always refreshed.
"""
# Always re-fetch when range includes today (same logic as _check_missing_periods)
today = datetime.now(timezone.utc).date()
if end_date.date() >= today:
logger.debug(f"_batch_check_missing_periods: end_date includes today — forcing re-fetch for all {len(tickers)} tickers")
return list(tickers)
# Single query to check all tickers at once
from sqlalchemy import func, case
@ -968,7 +1002,19 @@ class PriceDataService:
return
stmt = pg_insert(PriceData).values(rows)
stmt = stmt.on_conflict_do_nothing(constraint='uq_price_data')
stmt = stmt.on_conflict_do_update(
constraint='uq_price_data',
set_={
'open': stmt.excluded.open,
'high': stmt.excluded.high,
'low': stmt.excluded.low,
'close': stmt.excluded.close,
'volume': stmt.excluded.volume,
'adjusted_close': stmt.excluded.adjusted_close,
'data_source': stmt.excluded.data_source,
'updated_at': stmt.excluded.updated_at,
}
)
await db.execute(stmt)
logger.info(f"Batch upserted {len(rows)} price records for {ticker}")

Loading…
Cancel
Save