import datetime
from typing import Dict, Optional, Union
import numpy as np
import pandas as pd
import pytz
import pyqqq.config as c
from pyqqq.datatypes import FutureOptionMarket
from pyqqq.utils.api_client import send_request
from pyqqq.utils.local_cache import DiskCacheManager
from pyqqq.utils.logger import get_logger
logger = get_logger(__name__)
futuresCache = DiskCacheManager("futures_minute_cache")
[docs]
@futuresCache.memoize()
def get_futures_all_day_data(
date: datetime.date,
codes: Union[list, str],
session: Optional[Union[str, FutureOptionMarket]] = FutureOptionMarket.DAY,
ascending: bool = True,
) -> Union[Dict[str, pd.DataFrame], pd.DataFrame]:
"""
지정된 거래일에 대해 하나 이상의 선물 종목의 1분봉 OHLCV 데이터를 반환합니다.
현재 지수선물(코스피200/미니코스피200/코스닥150) 최근월물 데이터가 제공되며, 주식선물은 추가 예정입니다.
종목코드는 신단축코드 체계를 사용합니다. (예: "A01609")
- date는 거래일 기준입니다. 야간세션(night)은 거래일 18:00 ~ 익일 06:00이며, 자정 이후 봉의 time은 익일 날짜로 표기됩니다.
- session=None 지정 시 주간+야간 세션을 모두 반환합니다. (전일 야간세션의 새벽 봉은 포함되지 않습니다)
이때 cum_volume/cum_value는 세션별로 각각 누적되므로 (주간/야간 시장이 별개), 누적값을 사용할 때는 session 컬럼으로 나누어 사용해야 합니다.
- 거래가 없던 분은 직전 종가로 채워진 허봉(volume/value 0)이 포함되어 있습니다.
Args:
date (datetime.date): 조회할 거래일.
codes (list[str] | str): 조회할 선물 단축코드 리스트 또는 단일 코드. 최대 20개까지 지정할 수 있습니다.
session (str | FutureOptionMarket, optional): 조회할 세션. 'day'(정규/주간) 또는 'night'(야간)를 지정할 수 있습니다.
기본값은 day이며, None 지정 시 주간+야간을 모두 반환합니다.
ascending (bool): 시간 오름차순 여부. 기본값은 True.
Returns:
dict[str, pd.DataFrame] | pd.DataFrame: 종목코드를 키로 하는 딕셔너리. codes를 단일 코드(str)로 지정한 경우 해당 종목의 DataFrame만 반환합니다.
각 DataFrame은 'time' 열이 인덱스로 설정되어 있습니다.
DataFrame의 열은 다음과 같습니다:
- time (datetime.datetime): 시간 (인덱스)
- product (str): 상품 구분
- session (str): 세션 구분 ('day' | 'night')
- open (float): 시가
- high (float): 고가
- low (float): 저가
- close (float): 종가
- volume (int): 거래량
- value (int): 거래대금
- cum_volume (int): 누적거래량
- cum_value (int): 누적거래대금
Raises:
requests.exceptions.RequestException: PYQQQ API로부터 데이터를 검색하는 과정에서 오류가 발생한 경우.
Examples:
>>> df = get_futures_all_day_data(datetime.date(2026, 7, 29), "A01609", session="day")
>>> print(df)
product session open high low close volume value cum_volume cum_value
time
2026-07-29 08:45:00 KOSPI200선물 day 428.75 429.10 428.60 429.00 1234 132456789000 1234 132456789000
... ... ... ... ... ... ... ... ... ... ...
"""
assert type(date) is datetime.date, "date must be a datetime.date object"
assert isinstance(codes, (list, str)), "codes must be a list of strings or single code"
if isinstance(codes, list):
assert all(isinstance(code, str) for code in codes), "codes must be a list of strings"
assert len(codes) > 0, "codes must not be empty"
assert len(codes) <= 20, "codes must not exceed 20"
if session is not None:
session = FutureOptionMarket.validate(session)
tz = pytz.timezone("Asia/Seoul")
target_codes = codes if isinstance(codes, list) else [codes]
url = f"{c.PYQQQ_API_URL}/derivatives/futures/ohlcv/minutes/{date}"
params = {
"codes": ",".join(target_codes),
"current_date": datetime.date.today(),
}
if session is not None:
params["session"] = session.value
r = send_request("GET", url, params=params)
if r.status_code != 200 and r.status_code != 201:
logger.error(f"Failed to get futures day data: {r.text}")
r.raise_for_status()
result = {}
for code in target_codes:
result[code] = pd.DataFrame()
entries = r.json()
cols = entries["cols"]
if len(cols) == 0:
if isinstance(codes, str):
return result[codes]
return result
time_index = cols.index("time")
multirows = entries["rows"]
for code in multirows.keys():
rows = multirows[code]
for row in rows:
time = row[time_index].replace("Z", "+00:00")
time = datetime.datetime.fromisoformat(time).astimezone(tz).replace(tzinfo=None)
row[time_index] = time
rows.reverse()
df = pd.DataFrame(rows, columns=cols)
if not df.empty:
dtypes = df.dtypes
for k in ["volume", "value", "cum_volume", "cum_value"]:
if k in dtypes:
dtypes[k] = np.dtype("int64")
df = df.astype(dtypes)
df = df[[col for col in ["time", "product", "session", "open", "high", "low", "close", "volume", "value", "cum_volume", "cum_value"] if col in df.columns]]
df.set_index("time", inplace=True)
df.sort_index(ascending=ascending, inplace=True)
result[code] = df
if isinstance(codes, str):
return result[codes]
else:
return result