Source code for pyqqq.data.derivatives

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