From f7e89a782f16cd1ebfe7b5039a4af08edd58250a Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Fri, 26 Dec 2025 12:22:01 +0900 Subject: [PATCH 01/21] Add v2 client template --- jquantsapi/__init__.py | 1 + jquantsapi/client_v2.py | 677 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 678 insertions(+) create mode 100644 jquantsapi/client_v2.py diff --git a/jquantsapi/__init__.py b/jquantsapi/__init__.py index 3e490ba..c77d0ff 100644 --- a/jquantsapi/__init__.py +++ b/jquantsapi/__init__.py @@ -2,4 +2,5 @@ __version__ = "0.0.0" from .client import Client +from .client_v2 import ClientV2 from .enums import MARKET_API_SECTIONS diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py new file mode 100644 index 0000000..7db9738 --- /dev/null +++ b/jquantsapi/client_v2.py @@ -0,0 +1,677 @@ +import os +import platform +from typing import Any, Dict, List, Optional + +import pandas as pd # type: ignore +import requests +from requests.adapters import HTTPAdapter +from urllib3.util import Retry + +from jquantsapi import __version__ + + +class ClientV2: + """ + J-Quants API v2 用のクライアント + + - 認証方式: x-api-key (APIキー) + - ダッシュボードで発行した API キーを `api_key` 引数、もしくは + 環境変数 `JQUANTS_API_KEY` で指定してください。 + - v1 版 `Client` と同様に、各エンドポイントの結果を pandas.DataFrame で返しますが、 + v2 ではレスポンスのフィールド名が変更されているため、列名は v1 と異なります。 + """ + + JQUANTS_API_BASE = "https://api.jquants.com/v2" + USER_AGENT = "jqapi-python-v2" + USER_AGENT_VERSION = __version__ + RAW_ENCODING = "utf-8" + + def __init__(self, api_key: Optional[str] = None) -> None: + """ + Args: + api_key: J-Quants API v2 の API キー + 未指定の場合は環境変数 `JQUANTS_API_KEY` を参照します。 + """ + if api_key is None: + api_key = os.environ.get("JQUANTS_API_KEY", "") + + if not api_key: + raise ValueError( + "api_key is required. Set it via argument or JQUANTS_API_KEY env var." + ) + + self._api_key = api_key + self._session: Optional[requests.Session] = None + + # ------------------------------------------------------------------ + # 内部ユーティリティ + # ------------------------------------------------------------------ + def _request_session( + self, + status_forcelist: Optional[List[int]] = None, + allowed_methods: Optional[List[str]] = None, + ) -> requests.Session: + """ + requests の session を取得し、リトライ設定を行う + """ + if status_forcelist is None: + status_forcelist = [429, 500, 502, 503, 504] + if allowed_methods is None: + allowed_methods = ["HEAD", "GET", "OPTIONS"] + + if self._session is None: + retry_strategy = Retry( + total=3, + status_forcelist=status_forcelist, + allowed_methods=allowed_methods, + ) + adapter = HTTPAdapter( + pool_connections=10, + pool_maxsize=10, + max_retries=retry_strategy, + ) + self._session = requests.Session() + self._session.mount("https://", adapter) + + return self._session + + def _base_headers(self) -> Dict[str, str]: + """ + J-Quants API v2 にアクセスする際の共通ヘッダを生成 + """ + return { + "x-api-key": self._api_key, + "User-Agent": f"{self.USER_AGENT}/{self.USER_AGENT_VERSION} " + f"p/{platform.python_version()}", + } + + def _get(self, url: str, params: Optional[Dict[str, Any]] = None) -> requests.Response: + """ + GET リクエスト用ラッパー + """ + session = self._request_session() + headers = self._base_headers() + resp = session.get(url, params=params, headers=headers, timeout=30) + resp.raise_for_status() + return resp + + def _get_paginated( + self, + path: str, + params: Optional[Dict[str, Any]] = None, + data_key: str = "data", + ) -> List[Dict[str, Any]]: + """ + pagination_key に対応した共通 GET ヘルパー + + Args: + path: ベースURLからのパス (例: \"/equities/master\") + params: クエリパラメータ + data_key: レスポンス中のデータ配列キー (デフォルト: \"data\") + Returns: + List[Dict[str, Any]]: 連結済みのデータ配列 + """ + url = f"{self.JQUANTS_API_BASE}{path}" + all_data: List[Dict[str, Any]] = [] + query: Dict[str, Any] = dict(params or {}) + + while True: + resp = self._get(url, params=query) + payload = resp.json() + batch = payload.get(data_key, []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key") + if not pagination_key: + break + query["pagination_key"] = pagination_key + + return all_data + + # ------------------------------------------------------------------ + # /equities/master (path_old: /listed/info) + # ------------------------------------------------------------------ + def get_listed_info( + self, + code: str = "", + date: str = "", + ) -> pd.DataFrame: + """ + 上場銘柄一覧 (v2: /equities/master) + + v1 の `get_listed_info` と同等の目的ですが、カラム名は v2 仕様 + (例: CoName, CoNameEn, S17 など) になります。 + + Args: + code: 5桁の銘柄コード (例: 27800)。4桁指定も可能。 + date: 基準日 (YYYYMMDD or YYYY-MM-DD) + Returns: + pd.DataFrame: 上場銘柄情報 + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date: + params["date"] = date + + data = self._get_paginated("/equities/master", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + if "Code" in df.columns: + df.sort_values("Code", inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /equities/bars/daily (path_old: /prices/daily_quotes) + # ------------------------------------------------------------------ + def get_prices_daily_quotes( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 株価四本値 (v2: /equities/bars/daily) + + Args: + code: 銘柄コード (5桁 or 4桁) + from_yyyymmdd: 期間開始日 (YYYYMMDD or YYYY-MM-DD) + to_yyyymmdd: 期間終了日 (YYYYMMDD or YYYY-MM-DD) + date_yyyymmdd: 特定日付 (YYYYMMDD or YYYY-MM-DD) + Returns: + pd.DataFrame: 株価データ (v2のフィールド名で返却) + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = self._get_paginated("/equities/bars/daily", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Code", "Date"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /equities/bars/daily/am (path_old: /prices/prices_am) + # ------------------------------------------------------------------ + def get_prices_prices_am(self, code: str = "") -> pd.DataFrame: + """ + 前場四本値 (v2: /equities/bars/daily/am) + + Args: + code: 銘柄コード (5桁 or 4桁)。空文字の場合は全銘柄。 + Returns: + pd.DataFrame: 前場の株価データ + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + + data = self._get_paginated("/equities/bars/daily/am", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + if "Code" in df.columns: + df.sort_values("Code", inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /equities/investor-types (path_old: /markets/trades_spec) + # ------------------------------------------------------------------ + def get_markets_trades_spec( + self, + section: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 投資部門別売買状況 (v2: /equities/investor-types) + + Args: + section: 市場区分 (例: \"TSEPrime\") + from_yyyymmdd: 期間開始日 + to_yyyymmdd: 期間終了日 + Returns: + pd.DataFrame: 投資部門別売買データ + """ + params: Dict[str, Any] = {} + if section: + params["section"] = section + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = self._get_paginated("/equities/investor-types", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "PubDate" in df.columns: + df["PubDate"] = pd.to_datetime(df["PubDate"], errors="coerce") + sort_cols = [c for c in ["PubDate", "Section"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /fins/summary (path_old: /fins/statements) + # ------------------------------------------------------------------ + def get_fins_statements( + self, + code: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 財務情報サマリ (v2: /fins/summary) + + Args: + code: 銘柄コード + date_yyyymmdd: 開示日 (YYYYMMDD or YYYY-MM-DD) + Returns: + pd.DataFrame: 財務情報 (v2のフィールド名で返却) + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + + data = self._get_paginated("/fins/summary", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + for col in ("DiscDate", "CurPerSt", "CurPerEn", "CurFYSt", "CurFYEn", "NxtFYSt", "NxtFYEn"): + if col in df.columns: + df[col] = pd.to_datetime(df[col], errors="coerce") + sort_cols = [c for c in ["DiscDate", "DiscTime", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /fins/details (path_old: /fins/fs_details) + # ------------------------------------------------------------------ + def get_fins_fs_details( + self, + code: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 財務諸表詳細 (v2: /fins/details) + + Args: + code: 銘柄コード + date_yyyymmdd: 開示日 (YYYYMMDD or YYYY-MM-DD) + Returns: + pd.DataFrame: 財務諸表詳細 (FS列に各項目が含まれる) + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + + data = self._get_paginated("/fins/details", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "DiscDate" in df.columns: + df["DiscDate"] = pd.to_datetime(df["DiscDate"], errors="coerce") + sort_cols = [c for c in ["DiscDate", "DiscTime", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /fins/dividend (path_old: /fins/dividend) + # ------------------------------------------------------------------ + def get_fins_dividend( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 配当金情報 (v2: /fins/dividend) + + Args: + code: 銘柄コード + from_yyyymmdd: 期間開始日 + to_yyyymmdd: 期間終了日 + date_yyyymmdd: 特定日付 + Returns: + pd.DataFrame: 配当金データ + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = self._get_paginated("/fins/dividend", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "PubDate" in df.columns: + df["PubDate"] = pd.to_datetime(df["PubDate"], errors="coerce") + sort_cols = [c for c in ["PubDate", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /equities/earnings-calendar (path_old: /fins/announcement) + # ------------------------------------------------------------------ + def get_fins_announcement(self) -> pd.DataFrame: + """ + 決算発表予定日 (v2: /equities/earnings-calendar) + + Returns: + pd.DataFrame: 決算発表予定データ + """ + data = self._get_paginated("/equities/earnings-calendar", params={}) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Date", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /markets/short-ratio (path_old: /markets/short_selling) + # ------------------------------------------------------------------ + def get_markets_short_selling( + self, + sector_33_code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 業種別空売り比率 (v2: /markets/short-ratio) + + Args: + sector_33_code: 33業種コード (例: 0050) + from_yyyymmdd: 期間開始日 + to_yyyymmdd: 期間終了日 + date_yyyymmdd: 特定日付 + Returns: + pd.DataFrame: 業種別空売り比率データ + """ + params: Dict[str, Any] = {} + if sector_33_code: + params["s33"] = sector_33_code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = self._get_paginated("/markets/short-ratio", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Date", "S33"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /markets/short-sale-report (path_old: /markets/short_selling_positions) + # ------------------------------------------------------------------ + def get_markets_short_selling_positions( + self, + code: str = "", + disclosed_date: str = "", + disclosed_date_from: str = "", + disclosed_date_to: str = "", + calculated_date: str = "", + ) -> pd.DataFrame: + """ + 空売り残高報告 (v2: /markets/short-sale-report) + + Args: + code: 銘柄コード + disclosed_date: 開示日 + disclosed_date_from: 開示日(開始) + disclosed_date_to: 開示日(終了) + calculated_date: 算出日 + Returns: + pd.DataFrame: 空売り残高報告データ + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if disclosed_date: + params["disc_date"] = disclosed_date + if disclosed_date_from: + params["disc_date_from"] = disclosed_date_from + if disclosed_date_to: + params["disc_date_to"] = disclosed_date_to + if calculated_date: + params["calc_date"] = calculated_date + + data = self._get_paginated("/markets/short-sale-report", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + for col in ("DiscDate", "CalcDate", "PrevRptDate"): + if col in df.columns: + df[col] = pd.to_datetime(df[col], errors="coerce") + sort_cols = [c for c in ["DiscDate", "CalcDate", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /markets/margin-interest (path_old: /markets/weekly_margin_interest) + # ------------------------------------------------------------------ + def get_markets_weekly_margin_interest( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 信用取引週末残高 (v2: /markets/margin-interest) + + Args: + code: 銘柄コード + from_yyyymmdd: 期間開始日 + to_yyyymmdd: 期間終了日 + date_yyyymmdd: 特定日付 + Returns: + pd.DataFrame: 信用取引週末残高データ + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = self._get_paginated("/markets/margin-interest", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Date", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /markets/margin-alert (path_old: /markets/daily_margin_interest) + # ------------------------------------------------------------------ + def get_markets_daily_margin_interest( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 日々公表信用取引残高 (v2: /markets/margin-alert) + + Args: + code: 銘柄コード + from_yyyymmdd: 期間開始日 + to_yyyymmdd: 期間終了日 + date_yyyymmdd: 特定日付 + Returns: + pd.DataFrame: 日々公表信用取引残高データ + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = self._get_paginated("/markets/margin-alert", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "PubDate" in df.columns: + df["PubDate"] = pd.to_datetime(df["PubDate"], errors="coerce") + sort_cols = [c for c in ["Code", "PubDate"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /markets/breakdown (path_old: /markets/breakdown) + # ------------------------------------------------------------------ + def get_markets_breakdown( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 売買内訳データ (v2: /markets/breakdown) + + Args: + code: 銘柄コード + from_yyyymmdd: 期間開始日 + to_yyyymmdd: 期間終了日 + date_yyyymmdd: 特定日付 + Returns: + pd.DataFrame: 売買内訳データ + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = self._get_paginated("/markets/breakdown", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + if "Code" in df.columns: + df.sort_values(["Code", "Date"], inplace=True) + return df.reset_index(drop=True) + + # ------------------------------------------------------------------ + # /markets/calendar (path_old: /markets/trading_calendar) + # ------------------------------------------------------------------ + def get_markets_trading_calendar( + self, + holiday_division: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 取引カレンダー (v2: /markets/calendar) + + Args: + holiday_division: 休日区分 (HolDiv コード) + from_yyyymmdd: 期間開始日 + to_yyyymmdd: 期間終了日 + Returns: + pd.DataFrame: 取引カレンダーデータ + """ + params: Dict[str, Any] = {} + if holiday_division: + params["hol_div"] = holiday_division + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = self._get_paginated("/markets/calendar", params=params) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + if "Date" in df.columns: + df.sort_values("Date", inplace=True) + return df.reset_index(drop=True) + + From ca87b2e9a3bc9f43bc26edeeed8710f2bf16f915 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Mon, 5 Jan 2026 13:44:18 +0900 Subject: [PATCH 02/21] Delegate to apis/ --- jquantsapi/apis/__init__.py | 11 + jquantsapi/apis/base.py | 47 ++ jquantsapi/apis/v1/__init__.py | 5 + jquantsapi/apis/v1/derivatives.py | 139 ++++ jquantsapi/apis/v1/fins.py | 232 ++++++ jquantsapi/apis/v1/indices.py | 98 +++ jquantsapi/apis/v1/listed.py | 75 ++ jquantsapi/apis/v1/markets.py | 359 ++++++++++ jquantsapi/apis/v1/prices.py | 132 ++++ jquantsapi/apis/v2/__init__.py | 5 + jquantsapi/apis/v2/derivatives.py | 136 ++++ jquantsapi/apis/v2/equities.py | 296 ++++++++ jquantsapi/apis/v2/fins.py | 202 ++++++ jquantsapi/apis/v2/indices.py | 88 +++ jquantsapi/apis/v2/markets.py | 332 +++++++++ jquantsapi/client.py | 1112 +++-------------------------- jquantsapi/client_v2.py | 952 ++++++++++++++++-------- jquantsapi/constants.py | 760 ++++++++++++++++---- 18 files changed, 3559 insertions(+), 1422 deletions(-) create mode 100644 jquantsapi/apis/__init__.py create mode 100644 jquantsapi/apis/base.py create mode 100644 jquantsapi/apis/v1/__init__.py create mode 100644 jquantsapi/apis/v1/derivatives.py create mode 100644 jquantsapi/apis/v1/fins.py create mode 100644 jquantsapi/apis/v1/indices.py create mode 100644 jquantsapi/apis/v1/listed.py create mode 100644 jquantsapi/apis/v1/markets.py create mode 100644 jquantsapi/apis/v1/prices.py create mode 100644 jquantsapi/apis/v2/__init__.py create mode 100644 jquantsapi/apis/v2/derivatives.py create mode 100644 jquantsapi/apis/v2/equities.py create mode 100644 jquantsapi/apis/v2/fins.py create mode 100644 jquantsapi/apis/v2/indices.py create mode 100644 jquantsapi/apis/v2/markets.py diff --git a/jquantsapi/apis/__init__.py b/jquantsapi/apis/__init__.py new file mode 100644 index 0000000..5f83808 --- /dev/null +++ b/jquantsapi/apis/__init__.py @@ -0,0 +1,11 @@ +""" +API ごとの実装クラス群をまとめるパッケージ。 + +- 共通の抽象クラスは `BaseApi` +- v1 向けの実装は `jquantsapi.apis.v1` +- v2 向けの実装は `jquantsapi.apis.v2` +""" + +from .base import BaseApi # noqa: F401 + + diff --git a/jquantsapi/apis/base.py b/jquantsapi/apis/base.py new file mode 100644 index 0000000..e6d2d1f --- /dev/null +++ b/jquantsapi/apis/base.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import Any, Protocol + +import pandas as pd # type: ignore + + +class SupportsRequest(Protocol): + """ + Api クラスが利用するクライアント側の最小インタフェース。 + + - v1 の `Client` + - v2 の `ClientV2` + などがこの Protocol を満たす想定。 + """ + + JQUANTS_API_BASE: str + RAW_ENCODING: str + + def _get(self, url: str, params: dict[str, Any] | None = None): # pragma: no cover - Protocol 定義のみ + ... + + +class BaseApi(ABC): + """ + 各エンドポイント単位の API 実装のための抽象クラス。 + + v1 / v2 の実装で共通の execute インタフェースを提供する。 + """ + + #: 論理名 (例: "listed_info") + name: str + #: バージョン識別子 (例: "v1", "v2") + version: str + + @abstractmethod + def execute(self, client: SupportsRequest, **params: Any) -> pd.DataFrame: + """ + 実際に API を実行し、結果を DataFrame で返す。 + + Args: + client: HTTP リクエストなどを実行するクライアント + **params: API ごとのパラメータ + """ + + diff --git a/jquantsapi/apis/v1/__init__.py b/jquantsapi/apis/v1/__init__.py new file mode 100644 index 0000000..0c46cd0 --- /dev/null +++ b/jquantsapi/apis/v1/__init__.py @@ -0,0 +1,5 @@ +""" +J-Quants API v1 向けの各種 Api 実装。 +""" + + diff --git a/jquantsapi/apis/v1/derivatives.py b/jquantsapi/apis/v1/derivatives.py new file mode 100644 index 0000000..4ffdc12 --- /dev/null +++ b/jquantsapi/apis/v1/derivatives.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +import json +from typing import Any, Dict + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class DerivativesFuturesApiV1(BaseApi): + """ + v1 `/derivatives/futures` のラッパ。 + + 既存の `Client.get_derivatives_futures` のロジックをそのまま移植する。 + """ + + name = "derivatives_futures" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + date_yyyymmdd: str, + category: str = "", + contract_flag: str = "", + ) -> pd.DataFrame: + j = client._get_derivatives_futures_raw( # type: ignore[attr-defined] + category=category, + date_yyyymmdd=date_yyyymmdd, + contract_flag=contract_flag, + ) + d: Dict[str, Any] = json.loads(j) + data = d["futures"] + while "pagination_key" in d: + j = client._get_derivatives_futures_raw( # type: ignore[attr-defined] + category=category, + date_yyyymmdd=date_yyyymmdd, + contract_flag=contract_flag, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["futures"] + + df = pd.DataFrame.from_dict(data) + cols = constants.DERIVATIVES_FUTURES_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Code"], inplace=True) + return df[cols] + + +class DerivativesOptionsApiV1(BaseApi): + """ + v1 `/derivatives/options` のラッパ。 + + 既存の `Client.get_derivatives_options` のロジックをそのまま移植する。 + """ + + name = "derivatives_options" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + date_yyyymmdd: str, + category: str = "", + contract_flag: str = "", + code: str = "", + ) -> pd.DataFrame: + j = client._get_derivatives_options_raw( # type: ignore[attr-defined] + category=category, + date_yyyymmdd=date_yyyymmdd, + contract_flag=contract_flag, + code=code, + ) + d: Dict[str, Any] = json.loads(j) + data = d["options"] + while "pagination_key" in d: + j = client._get_derivatives_options_raw( # type: ignore[attr-defined] + category=category, + date_yyyymmdd=date_yyyymmdd, + contract_flag=contract_flag, + code=code, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["options"] + + df = pd.DataFrame.from_dict(data) + cols = constants.DERIVATIVES_OPTIONS_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Code"], inplace=True) + return df[cols] + + +class OptionIndexOptionApiV1(BaseApi): + """ + v1 `/option/index_option` のラッパ。 + + 既存の `Client.get_option_index_option` のロジックをそのまま移植する。 + """ + + name = "option_index_option" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + date_yyyymmdd: str, + ) -> pd.DataFrame: + j = client._get_option_index_option_raw( # type: ignore[attr-defined] + date_yyyymmdd=date_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + data = d["index_option"] + while "pagination_key" in d: + j = client._get_option_index_option_raw( # type: ignore[attr-defined] + date_yyyymmdd=date_yyyymmdd, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["index_option"] + + df = pd.DataFrame.from_dict(data) + cols = constants.OPTION_INDEX_OPTION_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Code"], inplace=True) + return df[cols] + diff --git a/jquantsapi/apis/v1/fins.py b/jquantsapi/apis/v1/fins.py new file mode 100644 index 0000000..30f9890 --- /dev/null +++ b/jquantsapi/apis/v1/fins.py @@ -0,0 +1,232 @@ +from __future__ import annotations + +import json +from typing import Any, Dict, List + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class FinsStatementsApiV1(BaseApi): + """ + v1 の財務情報 API (`/fins/statements`) を担当するクラス。 + + 既存の `Client.get_fins_statements` のロジックをそのまま移植します。 + """ + + name = "fins_statements" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/fins/statements` を実行し、財務情報を DataFrame で返す。 + """ + url = f"{client.JQUANTS_API_BASE}/fins/statements" # type: ignore[attr-defined] + params: Dict[str, Any] = {"code": code, "date": date_yyyymmdd} + + data: List[Dict[str, Any]] = [] + pagination_key: str = "" + while True: + req_params = dict(params) + if pagination_key != "": + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + d: Dict[str, Any] = json.loads(resp.text) + page = d.get("statements", []) + if isinstance(page, list): + data.extend(page) + + pagination_key = d.get("pagination_key", "") + if not pagination_key: + break + df = pd.DataFrame.from_dict(data) + cols = constants.FINS_STATEMENTS_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["DisclosedDate"] = pd.to_datetime(df["DisclosedDate"], format="%Y-%m-%d") + df["CurrentPeriodStartDate"] = pd.to_datetime( + df["CurrentPeriodStartDate"], format="%Y-%m-%d" + ) + df["CurrentPeriodEndDate"] = pd.to_datetime( + df["CurrentPeriodEndDate"], format="%Y-%m-%d" + ) + df["CurrentFiscalYearStartDate"] = pd.to_datetime( + df["CurrentFiscalYearStartDate"], format="%Y-%m-%d" + ) + df["CurrentFiscalYearEndDate"] = pd.to_datetime( + df["CurrentFiscalYearEndDate"], format="%Y-%m-%d" + ) + df["NextFiscalYearStartDate"] = pd.to_datetime( + df["NextFiscalYearStartDate"], format="%Y-%m-%d" + ) + df["NextFiscalYearEndDate"] = pd.to_datetime( + df["NextFiscalYearEndDate"], format="%Y-%m-%d" + ) + df.sort_values(["DisclosedDate", "DisclosedTime", "LocalCode"], inplace=True) + return df[cols] + + +class FinsFsDetailsApiV1(BaseApi): + """ + v1 の財務諸表(BS/PL) API (`/fins/fs_details`) を担当するクラス。 + + 既存の `Client.get_fins_fs_details` のロジックをそのまま移植します。 + """ + + name = "fins_fs_details" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/fins/fs_details` を実行し、財務諸表(BS/PL)を DataFrame で返す。 + """ + url = f"{client.JQUANTS_API_BASE}/fins/fs_details" # type: ignore[attr-defined] + params: Dict[str, Any] = {"code": code, "date": date_yyyymmdd} + + data: List[Dict[str, Any]] = [] + pagination_key: str = "" + while True: + req_params = dict(params) + if pagination_key != "": + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + d: Dict[str, Any] = json.loads(resp.text) + page = d.get("fs_details", []) + if isinstance(page, list): + data.extend(page) + + pagination_key = d.get("pagination_key", "") + if not pagination_key: + break + df = pd.json_normalize(data=data) + cols = constants.FINS_FS_DETAILS_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["DisclosedDate"] = pd.to_datetime(df["DisclosedDate"], format="%Y-%m-%d") + df.sort_values(["DisclosedDate", "DisclosedTime", "LocalCode"], inplace=True) + return df + + +class FinsDividendApiV1(BaseApi): + """ + v1 の配当金情報 API (`/fins/dividend`) を担当するクラス。 + + 既存の `Client.get_fins_dividend` のロジックをそのまま移植します。 + """ + + name = "fins_dividend" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/fins/dividend` を実行し、配当金情報を DataFrame で返す。 + """ + url = f"{client.JQUANTS_API_BASE}/fins/dividend" # type: ignore[attr-defined] + params: Dict[str, Any] = {"code": code} + if date_yyyymmdd != "": + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + data: List[Dict[str, Any]] = [] + pagination_key: str = "" + while True: + req_params = dict(params) + if pagination_key != "": + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + d: Dict[str, Any] = json.loads(resp.text) + page = d.get("dividend", []) + if isinstance(page, list): + data.extend(page) + + pagination_key = d.get("pagination_key", "") + if not pagination_key: + break + df = pd.DataFrame.from_dict(data) + cols = constants.FINS_DIVIDEND_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["AnnouncementDate"] = pd.to_datetime( + df["AnnouncementDate"], format="%Y-%m-%d" + ) + df.sort_values(["Code"], inplace=True) + return df[cols] + + +class FinsAnnouncementApiV1(BaseApi): + """ + v1 の決算発表予定 API (`/fins/announcement`) を担当するクラス。 + + 既存の `Client.get_fins_announcement` のロジックをそのまま移植します。 + """ + + name = "fins_announcement" + version = "v1" + + def execute( + self, + client: SupportsRequest, + ) -> pd.DataFrame: + """ + `/fins/announcement` を実行し、決算発表予定データを DataFrame で返す。 + """ + url = f"{client.JQUANTS_API_BASE}/fins/announcement" # type: ignore[attr-defined] + params: Dict[str, Any] = {} + + data: List[Dict[str, Any]] = [] + pagination_key: str = "" + while True: + req_params = dict(params) + if pagination_key != "": + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + d: Dict[str, Any] = json.loads(resp.text) + page = d.get("announcement", []) + if isinstance(page, list): + data.extend(page) + + pagination_key = d.get("pagination_key", "") + if not pagination_key: + break + df = pd.DataFrame.from_dict(data) + cols = constants.FINS_ANNOUNCEMENT_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Date", "Code"], inplace=True) + return df[cols] + diff --git a/jquantsapi/apis/v1/indices.py b/jquantsapi/apis/v1/indices.py new file mode 100644 index 0000000..9a4ab3b --- /dev/null +++ b/jquantsapi/apis/v1/indices.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +import json +from typing import Any, Dict + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class IndicesApiV1(BaseApi): + """ + v1 `/indices` のラッパ。 + + 既存の `Client.get_indices` のロジックをそのまま移植する。 + """ + + name = "indices" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + j = client._get_indices_raw( # type: ignore[attr-defined] + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + data = d["indices"] + while "pagination_key" in d: + j = client._get_indices_raw( # type: ignore[attr-defined] + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["indices"] + + df = pd.DataFrame.from_dict(data) + cols = constants.INDICES_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Code", "Date"], inplace=True) + return df[cols] + + +class IndicesTopixApiV1(BaseApi): + """ + v1 `/indices/topix` のラッパ。 + + 既存の `Client.get_indices_topix` のロジックをそのまま移植する。 + """ + + name = "indices_topix" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + j = client._get_indices_topix_raw( # type: ignore[attr-defined] + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + data = d["topix"] + while "pagination_key" in d: + j = client._get_indices_topix_raw( # type: ignore[attr-defined] + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["topix"] + + df = pd.DataFrame.from_dict(data) + cols = constants.INDICES_TOPIX_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Date"], inplace=True) + return df[cols] + diff --git a/jquantsapi/apis/v1/listed.py b/jquantsapi/apis/v1/listed.py new file mode 100644 index 0000000..aa6ec3d --- /dev/null +++ b/jquantsapi/apis/v1/listed.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +import json +from typing import Any, Dict, List + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class ListedInfoApiV1(BaseApi): + """ + v1 の上場銘柄一覧 API (`/listed/info`) を担当するクラス。 + """ + + name = "listed_info" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + v1 `/listed/info` を実行し、上場銘柄情報を DataFrame で返す。 + + Args: + client: v1 `Client` インスタンスを想定 + code: 銘柄コード (任意) + date_yyyymmdd: 基準日 (YYYYMMDD or YYYY-MM-DD, 任意) + """ + url = f"{client.JQUANTS_API_BASE}/listed/info" + + # ページングしながら全件取得 + all_info: List[Dict[str, Any]] = [] + base_params: Dict[str, Any] = {} + if code != "": + base_params["code"] = code + if date_yyyymmdd != "": + base_params["date"] = date_yyyymmdd + + pagination_key = "" + while True: + params = dict(base_params) + if pagination_key != "": + params["pagination_key"] = pagination_key + + resp = client._get(url, params) # type: ignore[arg-type] + resp.encoding = client.RAW_ENCODING + payload = json.loads(resp.text) + + data = payload.get("info", []) + if isinstance(data, list): + all_info.extend(data) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + df = pd.DataFrame.from_dict(all_info) + + standard_premium_flag = "MarginCode" in df.columns + if standard_premium_flag: + cols = constants.LISTED_INFO_STANDARD_PREMIUM_COLUMNS + else: + cols = constants.LISTED_INFO_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values("Code", inplace=True) + return df[cols].reset_index(drop=True) + diff --git a/jquantsapi/apis/v1/markets.py b/jquantsapi/apis/v1/markets.py new file mode 100644 index 0000000..ce676e5 --- /dev/null +++ b/jquantsapi/apis/v1/markets.py @@ -0,0 +1,359 @@ +from __future__ import annotations + +import json +from typing import Any, Dict, List, Union + +import pandas as pd # type: ignore + +from jquantsapi import constants, enums +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class MarketsTradesSpecApiV1(BaseApi): + """ + v1 の投資部門別売買状況 API (`/markets/trades_spec`) を担当するクラス。 + + 既存の `Client._get_markets_trades_spec_raw` と + `Client.get_markets_trades_spec` のロジックをそのまま移植します。 + """ + + name = "markets_trades_spec" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + section: Union[str, enums.MARKET_API_SECTIONS] = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/trades_spec` を実行し、投資部門別売買状況を DataFrame で返す。 + + Args: + client: v1 `Client` インスタンスを想定 + section: section name (e.g. "TSEPrime" or MARKET_API_SECTIONS.TSEPrime) + from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) + to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) + """ + # 元の get_markets_trades_spec と同じ処理をそのまま適用 + j = client._get_markets_trades_spec_raw( # type: ignore[attr-defined] + section=section, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + data: List[Dict[str, Any]] = d["trades_spec"] + while "pagination_key" in d: + j = client._get_markets_trades_spec_raw( # type: ignore[attr-defined] + section=section, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["trades_spec"] + df = pd.DataFrame.from_dict(data) + cols = constants.MARKETS_TRADES_SPEC + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["PublishedDate"] = pd.to_datetime(df["PublishedDate"], format="%Y-%m-%d") + df["StartDate"] = pd.to_datetime(df["StartDate"], format="%Y-%m-%d") + df["EndDate"] = pd.to_datetime(df["EndDate"], format="%Y-%m-%d") + df.sort_values(["PublishedDate", "Section"], inplace=True) + return df[cols] + + +class MarketsWeeklyMarginInterestApiV1(BaseApi): + """ + v1 の信用取引週末残高 API (`/markets/weekly_margin_interest`) を担当するクラス。 + + 既存の `Client.get_markets_weekly_margin_interest` のロジックをそのまま移植します。 + """ + + name = "markets_weekly_margin_interest" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/weekly_margin_interest` を実行し、信用取引週末残高を DataFrame で返す。 + """ + j = client._get_markets_weekly_margin_interest_raw( # type: ignore[attr-defined] + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + data: List[Dict[str, Any]] = d["weekly_margin_interest"] + while "pagination_key" in d: + j = client._get_markets_weekly_margin_interest_raw( # type: ignore[attr-defined] + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["weekly_margin_interest"] + df = pd.DataFrame.from_dict(data) + cols = constants.MARKETS_WEEKLY_MARGIN_INTEREST + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Date", "Code"], inplace=True) + return df[cols] + + +class MarketsTradingCalendarApiV1(BaseApi): + """ + v1 の取引カレンダー API (`/markets/trading_calendar`) を担当するクラス。 + + 既存の `Client.get_markets_trading_calendar` のロジックをそのまま移植します。 + """ + + name = "markets_trading_calendar" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + holiday_division: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/trading_calendar` を実行し、取引カレンダーデータを DataFrame で返す。 + """ + j = client._get_markets_trading_calendar_raw( # type: ignore[attr-defined] + holiday_division=holiday_division, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + df = pd.DataFrame.from_dict(d["trading_calendar"]) + cols = constants.MARKETS_TRADING_CALENDAR + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Date"], inplace=True) + return df[cols] + + +class MarketsShortSellingApiV1(BaseApi): + """ + v1 の業種別空売り比率 API (`/markets/short_selling`) を担当するクラス。 + + 既存の `Client.get_markets_short_selling` のロジックをそのまま移植します。 + """ + + name = "markets_short_selling" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + sector_33_code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/short_selling` を実行し、業種別空売り比率データを DataFrame で返す。 + """ + j = client._get_markets_short_selling_raw( # type: ignore[attr-defined] + sector_33_code=sector_33_code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + data: List[Dict[str, Any]] = d["short_selling"] + while "pagination_key" in d: + j = client._get_markets_short_selling_raw( # type: ignore[attr-defined] + sector_33_code=sector_33_code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["short_selling"] + df = pd.DataFrame.from_dict(data) + cols = constants.MARKET_SHORT_SELLING_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Date", "Sector33Code"], inplace=True) + return df[cols] + + +class MarketsBreakdownApiV1(BaseApi): + """ + v1 の売買内訳 API (`/markets/breakdown`) を担当するクラス。 + + 既存の `Client.get_markets_breakdown` のロジックをそのまま移植します。 + """ + + name = "markets_breakdown" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/breakdown` を実行し、売買内訳データを DataFrame で返す。 + """ + j = client._get_markets_breakdown_raw( # type: ignore[attr-defined] + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + data: List[Dict[str, Any]] = d["breakdown"] + while "pagination_key" in d: + j = client._get_markets_breakdown_raw( # type: ignore[attr-defined] + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["breakdown"] + df = pd.DataFrame.from_dict(data) + cols = constants.MARKETS_BREAKDOWN_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Code"], inplace=True) + return df[cols] + + +class MarketsShortSellingPositionsApiV1(BaseApi): + """ + v1 の空売り残高報告 API (`/markets/short_selling_positions`) を担当するクラス。 + + 既存の `Client.get_markets_short_selling_positions` のロジックをそのまま移植します。 + """ + + name = "markets_short_selling_positions" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + disclosed_date: str = "", + disclosed_date_from: str = "", + disclosed_date_to: str = "", + calculated_date: str = "", + ) -> pd.DataFrame: + """ + `/markets/short_selling_positions` を実行し、空売り残高報告データを DataFrame で返す。 + """ + j = client._get_markets_short_selling_positions_raw( # type: ignore[attr-defined] + code=code, + disclosed_date=disclosed_date, + disclosed_date_from=disclosed_date_from, + disclosed_date_to=disclosed_date_to, + calculated_date=calculated_date, + ) + d: Dict[str, Any] = json.loads(j) + data: List[Dict[str, Any]] = d["short_selling_positions"] + while "pagination_key" in d: + j = client._get_markets_short_selling_positions_raw( # type: ignore[attr-defined] + code=code, + disclosed_date=disclosed_date, + disclosed_date_from=disclosed_date_from, + disclosed_date_to=disclosed_date_to, + calculated_date=calculated_date, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["short_selling_positions"] + df = pd.DataFrame.from_dict(data) + cols = constants.SHORT_SELLING_POSITIONS_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["DisclosedDate"] = pd.to_datetime( + df["DisclosedDate"], format="%Y-%m-%d", errors="coerce" + ) + df["CalculatedDate"] = pd.to_datetime( + df["CalculatedDate"], format="%Y-%m-%d", errors="coerce" + ) + df["CalculationInPreviousReportingDate"] = pd.to_datetime( + df["CalculationInPreviousReportingDate"], format="%Y-%m-%d", errors="coerce" + ) + df.sort_values(["DisclosedDate", "CalculatedDate", "Code"], inplace=True) + return df[cols] + + +class MarketsDailyMarginInterestApiV1(BaseApi): + """ + v1 の日々公表信用取引残高 API (`/markets/daily_margin_interest`) を担当するクラス。 + + 既存の `Client.get_markets_daily_margin_interest` のロジックをそのまま移植します。 + """ + + name = "markets_daily_margin_interest" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/daily_margin_interest` を実行し、日々公表信用取引残高を DataFrame で返す。 + """ + j = client._get_markets_daily_margin_interest_raw( # type: ignore[attr-defined] + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + d: Dict[str, Any] = json.loads(j) + data: List[Dict[str, Any]] = d["daily_margin_interest"] + while "pagination_key" in d: + j = client._get_markets_daily_margin_interest_raw( # type: ignore[attr-defined] + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + pagination_key=d["pagination_key"], + ) + d = json.loads(j) + data += d["daily_margin_interest"] + df = pd.json_normalize(data=data) + cols = constants.DAILY_MARGIN_INTEREST_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["PublishedDate"] = pd.to_datetime(df["PublishedDate"], format="%Y-%m-%d") + df["ApplicationDate"] = pd.to_datetime(df["ApplicationDate"], format="%Y-%m-%d") + df.sort_values(["Code", "PublishedDate"], inplace=True) + return df[cols] + diff --git a/jquantsapi/apis/v1/prices.py b/jquantsapi/apis/v1/prices.py new file mode 100644 index 0000000..0736535 --- /dev/null +++ b/jquantsapi/apis/v1/prices.py @@ -0,0 +1,132 @@ +from __future__ import annotations + +import json +from typing import Any, Dict, List + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class PricesDailyQuotesApiV1(BaseApi): + """ + v1 の株価四本値 API (`/prices/daily_quotes`) を担当するクラス。 + + 既存の `Client._get_prices_daily_quotes_raw` と + `Client.get_prices_daily_quotes` のロジックをそのまま移植します。 + """ + + name = "prices_daily_quotes" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/prices/daily_quotes` を実行し、株価情報を DataFrame で返す。 + + Args: + client: v1 `Client` インスタンスを想定 + code: 銘柄コード + from_yyyymmdd: 取得開始日 + to_yyyymmdd: 取得終了日 + date_yyyymmdd: 取得日 + """ + url = f"{client.JQUANTS_API_BASE}/prices/daily_quotes" # type: ignore[attr-defined] + params: Dict[str, Any] = {"code": code} + if date_yyyymmdd != "": + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + data: List[Dict[str, Any]] = [] + pagination_key: str = "" + while True: + req_params = dict(params) + if pagination_key != "": + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + d: Dict[str, Any] = json.loads(resp.text) + page = d.get("daily_quotes", []) + if isinstance(page, list): + data.extend(page) + + pagination_key = d.get("pagination_key", "") + if not pagination_key: + break + + df = pd.DataFrame.from_dict(data) + + premium_flag = "MorningClose" in df.columns + if premium_flag: + cols = constants.PRICES_DAILY_QUOTES_PREMIUM_COLUMNS + else: + cols = constants.PRICES_DAILY_QUOTES_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Code", "Date"], inplace=True) + return df[cols] + + +class PricesPricesAmApiV1(BaseApi): + """ + v1 の前場四本値 API (`/prices/prices_am`) を担当するクラス。 + + 既存の `Client._get_prices_prices_am_raw` と + `Client.get_prices_prices_am` のロジックをそのまま移植します。 + """ + + name = "prices_prices_am" + version = "v1" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + ) -> pd.DataFrame: + """ + `/prices/prices_am` を実行し、前場四本値を DataFrame で返す。 + + Args: + client: v1 `Client` インスタンスを想定 + code: issue code (e.g. 27800 or 2780) + """ + url = f"{client.JQUANTS_API_BASE}/prices/prices_am" # type: ignore[attr-defined] + params: Dict[str, Any] = {"code": code} + + resp = client._get(url, params) # type: ignore[arg-type] + resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + d: Dict[str, Any] = json.loads(resp.text) + if d.get("message"): + return d["message"] # type: ignore[return-value] + data: List[Dict[str, Any]] = d.get("prices_am", []) + while "pagination_key" in d: + req_params = dict(params) + req_params["pagination_key"] = d["pagination_key"] + resp = client._get(url, req_params) # type: ignore[arg-type] + resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + d = json.loads(resp.text) + data += d.get("prices_am", []) + df = pd.DataFrame.from_dict(data) + cols = constants.PRICES_PRICES_AM_COLUMNS + if len(df) == 0: + return pd.DataFrame([], columns=cols) + df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") + df.sort_values(["Code"], inplace=True) + return df[cols] + diff --git a/jquantsapi/apis/v2/__init__.py b/jquantsapi/apis/v2/__init__.py new file mode 100644 index 0000000..1f8d3a5 --- /dev/null +++ b/jquantsapi/apis/v2/__init__.py @@ -0,0 +1,5 @@ +""" +v2 用 API クラス +""" + + diff --git a/jquantsapi/apis/v2/derivatives.py b/jquantsapi/apis/v2/derivatives.py new file mode 100644 index 0000000..f3d8a5e --- /dev/null +++ b/jquantsapi/apis/v2/derivatives.py @@ -0,0 +1,136 @@ +from __future__ import annotations + +from typing import Any, Dict, List + +import pandas as pd # type: ignore + +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class DrvBarsDailyFutApiV2(BaseApi): + """ + v2 `/derivatives/bars/daily/futures` のラッパ。 + + v1 `/derivatives/futures` に対応する先物四本値エンドポイント。 + """ + + name = "drv-bars-daily-fut" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + date_yyyymmdd: str, + category: str = "", + contract_flag: str = "", + ) -> pd.DataFrame: + params: Dict[str, Any] = {"date": date_yyyymmdd} + if category: + params["category"] = category + if contract_flag: + params["contract_flag"] = contract_flag + + data = client._get_paginated( # type: ignore[attr-defined] + "/derivatives/bars/daily/futures", params=params + ) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols: List[str] = [] + if "Code" in df.columns: + sort_cols.append("Code") + if "Date" in df.columns: + sort_cols.append("Date") + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + +class DrvBarsDailyOptApiV2(BaseApi): + """ + v2 `/derivatives/bars/daily/options` のラッパ。 + + v1 `/derivatives/options` に対応するオプション四本値エンドポイント。 + """ + + name = "drv-bars-daily-opt" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + date_yyyymmdd: str, + category: str = "", + contract_flag: str = "", + code: str = "", + ) -> pd.DataFrame: + params: Dict[str, Any] = {"date": date_yyyymmdd} + if category: + params["category"] = category + if contract_flag: + params["contract_flag"] = contract_flag + if code: + params["code"] = code + + data = client._get_paginated( # type: ignore[attr-defined] + "/derivatives/bars/daily/options", + params=params, + ) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols: List[str] = [] + if "Code" in df.columns: + sort_cols.append("Code") + if "Date" in df.columns: + sort_cols.append("Date") + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + +class DrvBarsDailyOpt225ApiV2(BaseApi): + """ + v2 `/derivatives/bars/daily/options/225` のラッパ。 + + v1 `/option/index_option` に対応する日経225オプション四本値エンドポイント。 + """ + + name = "drv-bars-daily-opt-225" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + date_yyyymmdd: str, + ) -> pd.DataFrame: + params: Dict[str, Any] = {"date": date_yyyymmdd} + + data = client._get_paginated( # type: ignore[attr-defined] + "/derivatives/bars/daily/options/225", + params=params, + ) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols: List[str] = [] + if "Code" in df.columns: + sort_cols.append("Code") + if "Date" in df.columns: + sort_cols.append("Date") + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + diff --git a/jquantsapi/apis/v2/equities.py b/jquantsapi/apis/v2/equities.py new file mode 100644 index 0000000..1c70916 --- /dev/null +++ b/jquantsapi/apis/v2/equities.py @@ -0,0 +1,296 @@ +from __future__ import annotations + +from typing import Any, Dict, List + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class EqMasterApiV2(BaseApi): + """ + v2: eq-master (/equities/master) + + 上場銘柄一覧 (v2) を取得します。 + """ + + name = "eq_master" + version = "v2" + + def execute(self, client: Any, code: str = "", date: str = "") -> pd.DataFrame: + params: Dict[str, str] = {} + if code: + params["code"] = code + if date: + params["date"] = date + + # ClientV2 の _get_paginated を利用する + data: List[Dict[str, Any]] = client._get_paginated( # type: ignore[attr-defined] + "/equities/master", params=params, data_key="data" + ) + cols = constants.EQ_MASTER_COLUMNS_V2 + if not data: + return pd.DataFrame(columns=cols) + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + if "Code" in df.columns: + df.sort_values("Code", inplace=True) + + return df[cols].reset_index(drop=True) + + +class EqBarsDailyApiV2(BaseApi): + """ + v2 の株価四本値 API (`/equities/bars/daily`) を担当するクラス。 + """ + + name = "eq_bars_daily" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + v2 `/equities/bars/daily` を実行し、株価四本値を DataFrame で返す。 + + Args: + client: v2 `ClientV2` インスタンスを想定 + code: 銘柄コード + from_yyyymmdd: 取得開始日 + to_yyyymmdd: 取得終了日 + date_yyyymmdd: 取得日 + """ + url = f"{client.JQUANTS_API_BASE}/equities/bars/daily" + + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + + sort_cols = [c for c in ["Code", "Date"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + + return df.reset_index(drop=True) + + +class EqBarsDailyAmApiV2(BaseApi): + """ + v2 の前場四本値 API (`/equities/bars/daily/am`) を担当するクラス。 + """ + + name = "eq_bars_daily_am" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + ) -> pd.DataFrame: + """ + v2 `/equities/bars/daily/am` を実行し、前場四本値を DataFrame で返す。 + + Args: + client: v2 `ClientV2` インスタンスを想定 + code: 銘柄コード (5桁 or 4桁)。空文字の場合は全銘柄。 + """ + url = f"{client.JQUANTS_API_BASE}/equities/bars/daily/am" + + params: Dict[str, Any] = {} + if code: + params["code"] = code + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + if "Code" in df.columns: + df.sort_values(["Code", "Date"], inplace=True) + + # v1 `/prices/prices_am` と同様に、前場四本値に対応する列のみを返す + cols = constants.PRICES_PRICES_AM_COLUMNS_V2 + return df[cols].reset_index(drop=True) + + +class EqInvestorTypesApiV2(BaseApi): + """ + v2 の投資部門別売買状況 API (`/equities/investor-types`) を担当するクラス。 + """ + + name = "eq_investor_types" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + section: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + v2 `/equities/investor-types` を実行し、投資部門別売買状況を DataFrame で返す。 + + Args: + client: v2 `ClientV2` インスタンスを想定 + section: 市場区分 (例: "TSEPrime") + from_yyyymmdd: 期間開始日 + to_yyyymmdd: 期間終了日 + """ + url = f"{client.JQUANTS_API_BASE}/equities/investor-types" + + params: Dict[str, Any] = {} + if section: + params["section"] = section + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + if "PubDate" in df.columns: + df["PubDate"] = pd.to_datetime(df["PubDate"], errors="coerce") + sort_cols = [c for c in ["PubDate", "Section"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + + # v1 `/markets/trades_spec` と同様に、定義済みカラムの順序で返す + cols = constants.EQ_INVESTOR_TYPES_COLUMNS_V2 + return df[cols].reset_index(drop=True) + + +class EqEarningsCalApiV2(BaseApi): + """ + v2 の決算発表予定日 API (`/equities/earnings-calendar`) を担当するクラス。 + + 既存の `ClientV2.get_fins_announcement` のロジックをそのまま移植します。 + """ + + name = "eq_earnings_cal" + version = "v2" + + def execute( + self, + client: SupportsRequest, + ) -> pd.DataFrame: + """ + `/equities/earnings-calendar` を実行し、決算発表予定データを DataFrame で返す。 + """ + url = f"{client.JQUANTS_API_BASE}/equities/earnings-calendar" + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + params: Dict[str, Any] = {} + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Date", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + diff --git a/jquantsapi/apis/v2/fins.py b/jquantsapi/apis/v2/fins.py new file mode 100644 index 0000000..bda51c6 --- /dev/null +++ b/jquantsapi/apis/v2/fins.py @@ -0,0 +1,202 @@ +from __future__ import annotations + +from typing import Any, Dict, List + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class FinSummaryApiV2(BaseApi): + """ + v2 の財務情報サマリ API (`/fins/summary`) を担当するクラス。 + + 既存の `ClientV2.get_fins_statements` のロジックをそのまま移植します。 + """ + + name = "fin_summary" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/fins/summary` を実行し、財務情報サマリを DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + url = f"{client.JQUANTS_API_BASE}/fins/summary" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + for col in ( + "DiscDate", + "CurPerSt", + "CurPerEn", + "CurFYSt", + "CurFYEn", + "NxtFYSt", + "NxtFYEn", + ): + if col in df.columns: + df[col] = pd.to_datetime(df[col], errors="coerce") + sort_cols = [c for c in ["DiscDate", "DiscTime", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + + # v1 `/fins/statements` と同様に、定義済みカラムの順序で返す + cols = constants.FIN_SUMMARY_COLUMNS_V2 + return df[cols].reset_index(drop=True) + + +class FinDetailsApiV2(BaseApi): + """ + v2 の財務諸表詳細 API (`/fins/details`) を担当するクラス。 + + 既存の `ClientV2.get_fins_fs_details` のロジックをそのまま移植します。 + """ + + name = "fin_details" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/fins/details` を実行し、財務諸表詳細を DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + url = f"{client.JQUANTS_API_BASE}/fins/details" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + if "DiscDate" in df.columns: + df["DiscDate"] = pd.to_datetime(df["DiscDate"], errors="coerce") + sort_cols = [c for c in ["DiscDate", "DiscTime", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + +class FinDividendApiV2(BaseApi): + """ + v2 の配当金情報 API (`/fins/dividend`) を担当するクラス。 + + 既存の `ClientV2.get_fins_dividend` のロジックをそのまま移植します。 + """ + + name = "fin_dividend" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/fins/dividend` を実行し、配当金情報を DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + url = f"{client.JQUANTS_API_BASE}/fins/dividend" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + if "PubDate" in df.columns: + df["PubDate"] = pd.to_datetime(df["PubDate"], errors="coerce") + sort_cols = [c for c in ["PubDate", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + diff --git a/jquantsapi/apis/v2/indices.py b/jquantsapi/apis/v2/indices.py new file mode 100644 index 0000000..376b68f --- /dev/null +++ b/jquantsapi/apis/v2/indices.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +from typing import Any, Dict, List + +import pandas as pd # type: ignore + +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class IdxBarsDailyApiV2(BaseApi): + """ + v2 `/indices/bars/daily` のラッパ。 + + v1 `/indices` に対応する指数四本値エンドポイント。 + """ + + name = "idx-bars-daily" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + params: Dict[str, Any] = {} + if code: + params["code"] = code + # v1 と同様: date があれば date 優先、なければ from/to + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = client._get_paginated("/indices/bars/daily", params=params) # type: ignore[attr-defined] + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols: List[str] = [] + if "Code" in df.columns: + sort_cols.append("Code") + sort_cols.append("Date") + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + +class IdxBarsDailyTopixApiV2(BaseApi): + """ + v2 `/indices/bars/daily/topix` のラッパ。 + + v1 `/indices/topix` に対応する TOPIX 指数四本値エンドポイント。 + """ + + name = "idx-bars-daily-topix" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + params: Dict[str, Any] = {} + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = client._get_paginated("/indices/bars/daily/topix", params=params) # type: ignore[attr-defined] + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + df.sort_values("Date", inplace=True) + return df.reset_index(drop=True) + diff --git a/jquantsapi/apis/v2/markets.py b/jquantsapi/apis/v2/markets.py new file mode 100644 index 0000000..2f8b606 --- /dev/null +++ b/jquantsapi/apis/v2/markets.py @@ -0,0 +1,332 @@ +from __future__ import annotations + +from typing import Any, Dict, List + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest + + +class MktShortRatioApiV2(BaseApi): + """ + v2 の業種別空売り比率 API (`/markets/short-ratio`) を担当するクラス。 + + 既存の `ClientV2.get_markets_short_selling` のロジックをそのまま移植します。 + """ + + name = "mkt_short_ratio" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + sector_33_code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/short-ratio` を実行し、業種別空売り比率データを DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if sector_33_code: + params["s33"] = sector_33_code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + url = f"{client.JQUANTS_API_BASE}/markets/short-ratio" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Date", "S33"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + + # v1 `/markets/short_selling` と同様に、定義済みカラムの順序で返す + cols = constants.MKT_SHORT_RATIO_COLUMNS_V2 + return df[cols].reset_index(drop=True) + + +class MktShortSaleReportApiV2(BaseApi): + """ + v2 の空売り残高報告 API (`/markets/short-sale-report`) を担当するクラス。 + + 既存の `ClientV2.get_markets_short_selling_positions` のロジックをそのまま移植します。 + """ + + name = "mkt_short_sale_report" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + disclosed_date: str = "", + disclosed_date_from: str = "", + disclosed_date_to: str = "", + calculated_date: str = "", + ) -> pd.DataFrame: + """ + `/markets/short-sale-report` を実行し、空売り残高報告データを DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if disclosed_date: + params["disc_date"] = disclosed_date + if disclosed_date_from: + params["disc_date_from"] = disclosed_date_from + if disclosed_date_to: + params["disc_date_to"] = disclosed_date_to + if calculated_date: + params["calc_date"] = calculated_date + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + url = f"{client.JQUANTS_API_BASE}/markets/short-sale-report" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + if not all_data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(all_data) + for col in ("DiscDate", "CalcDate", "PrevRptDate"): + if col in df.columns: + df[col] = pd.to_datetime(df[col], errors="coerce") + sort_cols = [c for c in ["DiscDate", "CalcDate", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + +class MktMarginInterestApiV2(BaseApi): + """ + v2 の信用取引週末残高 API (`/markets/margin-interest`) を担当するクラス。 + """ + + name = "mkt_margin_interest" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + date_yyyymmdd: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/margin-interest` を実行し、信用取引週末残高を DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = client._get_paginated( # type: ignore[attr-defined] + "/markets/margin-interest", + params=params, + ) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Date", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + +class MktBreakdownApiV2(BaseApi): + """ + v2 の売買内訳 API (`/markets/breakdown`) を担当するクラス。 + """ + + name = "mkt_breakdown" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/breakdown` を実行し、売買内訳データを DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = client._get_paginated( # type: ignore[attr-defined] + "/markets/breakdown", + params=params, + ) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Code", "Date"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + + # v1 `/markets/breakdown` と同様に、定義済みカラムの順序で返す + cols = constants.MKT_BREAKDOWN_COLUMNS_V2 + return df[cols].reset_index(drop=True) + + +class MktMarginAlertApiV2(BaseApi): + """ + v2 の日々公表信用取引残高 API (`/markets/margin-alert`) を担当するクラス。 + """ + + name = "mkt_margin_alert" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + date_yyyymmdd: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/margin-alert` を実行し、日々公表信用取引残高を DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = client._get_paginated( # type: ignore[attr-defined] + "/markets/margin-alert", + params=params, + ) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + sort_cols = [c for c in ["Date", "Code"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + return df.reset_index(drop=True) + + +class MktCalendarApiV2(BaseApi): + """ + v2 の取引カレンダー API (`/markets/calendar`) を担当するクラス。 + + 既存の `ClientV2.get_markets_trading_calendar` のロジックをそのまま移植します。 + """ + + name = "markets_trading_calendar" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + holiday_division: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + `/markets/calendar` を実行し、取引カレンダーデータを DataFrame で返す。 + """ + params: Dict[str, Any] = {} + if holiday_division: + params["hol_div"] = holiday_division + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + data = client._get_paginated( # type: ignore[attr-defined] + "/markets/calendar", + params=params, + ) + if not data: + return pd.DataFrame() + + df = pd.DataFrame.from_records(data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + df.sort_values("Date", inplace=True) + return df.reset_index(drop=True) + diff --git a/jquantsapi/client.py b/jquantsapi/client.py index 9f4231f..9ee4242 100644 --- a/jquantsapi/client.py +++ b/jquantsapi/client.py @@ -20,6 +20,29 @@ from urllib3.util import Retry from jquantsapi import __version__, constants, enums +from jquantsapi.apis.v1.listed import ListedInfoApiV1 +from jquantsapi.apis.v1.prices import PricesDailyQuotesApiV1, PricesPricesAmApiV1 +from jquantsapi.apis.v1.markets import ( + MarketsTradesSpecApiV1, + MarketsWeeklyMarginInterestApiV1, + MarketsTradingCalendarApiV1, + MarketsShortSellingApiV1, + MarketsBreakdownApiV1, + MarketsShortSellingPositionsApiV1, + MarketsDailyMarginInterestApiV1, +) +from jquantsapi.apis.v1.indices import IndicesApiV1, IndicesTopixApiV1 +from jquantsapi.apis.v1.derivatives import ( + DerivativesFuturesApiV1, + DerivativesOptionsApiV1, + OptionIndexOptionApiV1, +) +from jquantsapi.apis.v1.fins import ( + FinsAnnouncementApiV1, + FinsDividendApiV1, + FinsFsDetailsApiV1, + FinsStatementsApiV1, +) if sys.version_info >= (3, 11): import tomllib @@ -85,6 +108,28 @@ def __init__( self._id_token_expire = pd.Timestamp.utcnow() self._session: Optional[requests.Session] = None + # API 実装 (v1) + self._listed_info_api = ListedInfoApiV1() + self._prices_daily_quotes_api = PricesDailyQuotesApiV1() + self._prices_prices_am_api = PricesPricesAmApiV1() + self._markets_trades_spec_api = MarketsTradesSpecApiV1() + self._markets_weekly_margin_interest_api = MarketsWeeklyMarginInterestApiV1() + self._indices_api = IndicesApiV1() + self._indices_topix_api = IndicesTopixApiV1() + self._derivatives_futures_api = DerivativesFuturesApiV1() + self._derivatives_options_api = DerivativesOptionsApiV1() + self._option_index_option_api = OptionIndexOptionApiV1() + self._markets_weekly_margin_interest_api = MarketsWeeklyMarginInterestApiV1() + self._markets_short_selling_api = MarketsShortSellingApiV1() + self._markets_breakdown_api = MarketsBreakdownApiV1() + self._markets_short_selling_positions_api = MarketsShortSellingPositionsApiV1() + self._markets_daily_margin_interest_api = MarketsDailyMarginInterestApiV1() + self._markets_trading_calendar_api = MarketsTradingCalendarApiV1() + self._fins_statements_api = FinsStatementsApiV1() + self._fins_fs_details_api = FinsFsDetailsApiV1() + self._fins_dividend_api = FinsDividendApiV1() + self._fins_announcement_api = FinsAnnouncementApiV1() + if ((self._mail_address == "") or (self._password == "")) and ( self._refresh_token == "" ): @@ -355,33 +400,6 @@ def get_id_token(self, refresh_token: Optional[str] = None) -> str: self._id_token_expire = pd.Timestamp.utcnow() + pd.Timedelta(23, unit="hour") return self._id_token - # /listed - def _get_listed_info_raw( - self, code: str = "", date_yyyymmdd: str = "", pagination_key: str = "" - ) -> str: - """ - Get listed companies raw API returns - - Args: - code: Issue code (Optional) - date: YYYYMMDD or YYYY-MM-DD (Optional) - pagination_key: ページングキー - - Returns: - str: listed companies raw json string - """ - url = f"{self.JQUANTS_API_BASE}/listed/info" - params = {} - if code != "": - params["code"] = code - if date_yyyymmdd != "": - params["date"] = date_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_listed_info(self, code: str = "", date_yyyymmdd: str = "") -> pd.DataFrame: """ Get listed companies @@ -393,29 +411,11 @@ def get_listed_info(self, code: str = "", date_yyyymmdd: str = "") -> pd.DataFra Returns: pd.DataFrame: listed companies (sorted by Code) """ - j = self._get_listed_info_raw(code=code, date_yyyymmdd=date_yyyymmdd) - d = json.loads(j) - data = d["info"] - while "pagination_key" in d: - j = self._get_listed_info_raw( - code=code, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["info"] - df = pd.DataFrame.from_dict(data) - - standard_premium_flag = "MarginCode" in df.columns - if standard_premium_flag: - cols = constants.LISTED_INFO_STANDARD_PREMIUM_COLUMNS - else: - cols = constants.LISTED_INFO_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values("Code", inplace=True) - return df[cols] + return self._listed_info_api.execute( + self, + code=code, + date_yyyymmdd=date_yyyymmdd, + ) @staticmethod def get_market_segments() -> pd.DataFrame: @@ -493,45 +493,6 @@ def get_list(self, code: str = "", date_yyyymmdd: str = "") -> pd.DataFrame: df_list.sort_values("Code", inplace=True) return df_list - # /prices - def _get_prices_daily_quotes_raw( - self, - code: str = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - date_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - get daily quotes raw API returns - - Args: - code: 銘柄コード - from_yyyymmdd: 取得開始日 - to_yyyymmdd: 取得終了日 - date_yyyymmdd: 取得日 - pagination_key: ページングキー - - Returns: - str: daily quotes - """ - url = f"{self.JQUANTS_API_BASE}/prices/daily_quotes" - params = { - "code": code, - } - if date_yyyymmdd != "": - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_prices_daily_quotes( self, code: str = "", @@ -551,35 +512,13 @@ def get_prices_daily_quotes( Returns: pd.DataFrame: 株価情報 (Code, Date列でソートされています) """ - j = self._get_prices_daily_quotes_raw( + return self._prices_daily_quotes_api.execute( + self, code=code, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) - d = json.loads(j) - data = d["daily_quotes"] - while "pagination_key" in d: - j = self._get_prices_daily_quotes_raw( - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["daily_quotes"] - df = pd.DataFrame.from_dict(data) - premium_flag = "MorningClose" in df.columns - if premium_flag: - cols = constants.PRICES_DAILY_QUOTES_PREMIUM_COLUMNS - else: - cols = constants.PRICES_DAILY_QUOTES_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Code", "Date"], inplace=True) - return df[cols] def get_price_range( self, @@ -612,33 +551,6 @@ def get_price_range( buff.append(df) return pd.concat(buff).sort_values(["Code", "Date"]) - def _get_prices_prices_am_raw( - self, - code: str = "", - pagination_key: str = "", - ) -> str: - """ - get the morning session's high, low, opening, and closing prices for individual stocks raw API returns - - Args: - code: issue code (e.g. 27800 or 2780) - If a 4-character issue code is specified, only the data of common stock will be obtained - for the issue on which both common and preferred stocks are listed. - pagination_key: ページングキー - - Returns: - str: the morning session's OHLC data - """ - url = f"{self.JQUANTS_API_BASE}/prices/prices_am" - params = { - "code": code, - } - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_prices_prices_am( self, code: str = "", @@ -652,62 +564,9 @@ def get_prices_prices_am( for the issue on which both common and preferred stocks are listed. Returns: pd.DataFrame: the morning session's OHLC data """ - j = self._get_prices_prices_am_raw( - code=code, - ) - d = json.loads(j) - if d.get("message"): - return d["message"] - data = d["prices_am"] - while "pagination_key" in d: - j = self._get_prices_prices_am_raw( - code=code, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["prices_am"] - df = pd.DataFrame.from_dict(data) - cols = constants.PRICES_PRICES_AM_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Code"], inplace=True) - return df[cols] + return self._prices_prices_am_api.execute(self, code=code) # /markets - def _get_markets_trades_spec_raw( - self, - section: Union[str, enums.MARKET_API_SECTIONS] = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - Weekly Trading by Type of Investors raw API returns - - Args: - section: section name (e.g. "TSEPrime" or MARKET_API_SECTIONS.TSEPrime) - from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) - to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - - Returns: - str: Weekly Trading by Type of Investors - """ - url = f"{self.JQUANTS_API_BASE}/markets/trades_spec" - params = {} - if section != "": - params["section"] = section - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_markets_trades_spec( self, section: Union[str, enums.MARKET_API_SECTIONS] = "", @@ -724,68 +583,12 @@ def get_markets_trades_spec( Returns: pd.DataFrame: Weekly Trading by Type of Investors (Sorted by "PublishedDate" and "Section" columns) """ - j = self._get_markets_trades_spec_raw( - section=section, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd + return self._markets_trades_spec_api.execute( + self, + section=section, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, ) - d = json.loads(j) - data = d["trades_spec"] - while "pagination_key" in d: - j = self._get_markets_trades_spec_raw( - section=section, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["trades_spec"] - df = pd.DataFrame.from_dict(data) - cols = constants.MARKETS_TRADES_SPEC - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["PublishedDate"] = pd.to_datetime(df["PublishedDate"], format="%Y-%m-%d") - df["StartDate"] = pd.to_datetime(df["StartDate"], format="%Y-%m-%d") - df["EndDate"] = pd.to_datetime(df["EndDate"], format="%Y-%m-%d") - df.sort_values(["PublishedDate", "Section"], inplace=True) - return df[cols] - - def _get_markets_weekly_margin_interest_raw( - self, - code: str = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - date_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - get weekly margin interest raw API returns - - Args: - code: issue code (e.g. 27800 or 2780) - If a 4-character issue code is specified, only the data of common stock will be obtained - for the issue on which both common and preferred stocks are listed. - from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) - to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) - date_yyyymmdd: date of data (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - Returns: - str: weekly margin interest - """ - url = f"{self.JQUANTS_API_BASE}/markets/weekly_margin_interest" - params = { - "code": code, - } - if date_yyyymmdd != "": - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text def get_markets_weekly_margin_interest( self, @@ -807,31 +610,13 @@ def get_markets_weekly_margin_interest( Returns: pd.DataFrame: weekly margin interest (Sorted by "Date" and "Code" columns) """ - j = self._get_markets_weekly_margin_interest_raw( + return self._markets_weekly_margin_interest_api.execute( + self, code=code, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) - d = json.loads(j) - data = d["weekly_margin_interest"] - while "pagination_key" in d: - j = self._get_markets_weekly_margin_interest_raw( - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["weekly_margin_interest"] - df = pd.DataFrame.from_dict(data) - cols = constants.MARKETS_WEEKLY_MARGIN_INTEREST - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Date", "Code"], inplace=True) - return df[cols] def get_weekly_margin_range( self, @@ -865,43 +650,6 @@ def get_weekly_margin_range( buff.append(df) return pd.concat(buff).sort_values(["Code", "Date"]) - def _get_markets_short_selling_raw( - self, - sector_33_code: str = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - date_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - get daily short sale ratios and trading value by industry (sector) raw API returns - - Args: - sector_33_code: 33-sector code (e.g. 0050 or 8050) - from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) - to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) - date_yyyymmdd: date of data (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - Returns: - str: daily short sale ratios and trading value by industry - """ - url = f"{self.JQUANTS_API_BASE}/markets/short_selling" - params = { - "sector33code": sector_33_code, - } - if date_yyyymmdd != "": - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = date_yyyymmdd - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_markets_short_selling( self, sector_33_code: str = "", @@ -921,33 +669,14 @@ def get_markets_short_selling( pd.DataFrame: daily short sale ratios and trading value by industry (Sorted by "Date" and "Sector33Code" columns) """ - j = self._get_markets_short_selling_raw( + return self._markets_short_selling_api.execute( + self, sector_33_code=sector_33_code, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) - d = json.loads(j) - data = d["short_selling"] - while "pagination_key" in d: - j = self._get_markets_short_selling_raw( - sector_33_code=sector_33_code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["short_selling"] - df = pd.DataFrame.from_dict(data) - cols = constants.MARKET_SHORT_SELLING_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Date", "Sector33Code"], inplace=True) - return df[cols] - def get_short_selling_range( self, start_dt: DatetimeLike = "20170101", @@ -979,45 +708,6 @@ def get_short_selling_range( buff.append(df) return pd.concat(buff).sort_values(["Sector33Code", "Date"]) - def _get_markets_breakdown_raw( - self, - code: str = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - date_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - get detail breakdown trading data raw API returns - - Args: - code: issue code (e.g. 27800 or 2780) - If a 4-character issue code is specified, only the data of common stock will be obtained - for the issue on which both common and preferred stocks are listed. - from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) - to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) - date_yyyymmdd: date of data (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - Returns: - str: detail breakdown trading data - """ - url = f"{self.JQUANTS_API_BASE}/markets/breakdown" - params = { - "code": code, - } - if date_yyyymmdd != "": - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_markets_breakdown( self, code: str = "", @@ -1038,31 +728,13 @@ def get_markets_breakdown( Returns: pd.DataFrame: detail breakdown trading data (Sorted by "Code") """ - j = self._get_markets_breakdown_raw( + return self._markets_breakdown_api.execute( + self, code=code, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) - d = json.loads(j) - data = d["breakdown"] - while "pagination_key" in d: - j = self._get_markets_breakdown_raw( - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["breakdown"] - df = pd.DataFrame.from_dict(data) - cols = constants.MARKETS_BREAKDOWN_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Code"], inplace=True) - return df[cols] def get_breakdown_range( self, @@ -1097,43 +769,6 @@ def get_breakdown_range( # /indices - def _get_indices_raw( - self, - code: str = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - date_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - Indices Daily OHLC raw API returns - - Args: - code: 指数コード - from_yyyymmdd: 取得開始日 - to_yyyymmdd: 取得終了日 - date_yyyymmdd: 取得日 - pagination_key: ページングキー - Returns: - str: Indices Daily OHLC - """ - url = f"{self.JQUANTS_API_BASE}/indices" - params = { - "code": code, - } - if date_yyyymmdd != "": - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_indices( self, code: str = "", @@ -1152,60 +787,13 @@ def get_indices( Returns: pd.DataFrame: Indices Daily OHLC (Sorted by "Code", "Date" column) """ - j = self._get_indices_raw( + return self._indices_api.execute( + self, code=code, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) - d = json.loads(j) - data = d["indices"] - while "pagination_key" in d: - j = self._get_indices_raw( - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["indices"] - df = pd.DataFrame.from_dict(data) - cols = constants.INDICES_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Code", "Date"], inplace=True) - return df[cols] - - def _get_indices_topix_raw( - self, - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - TOPIX Daily OHLC raw API returns - - Args: - from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) - to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - Returns: - str: TOPIX Daily OHLC - """ - url = f"{self.JQUANTS_API_BASE}/indices/topix" - params = {} - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_indices_topix( self, from_yyyymmdd: str = "", @@ -1220,53 +808,11 @@ def get_indices_topix( Returns: pd.DataFrame: TOPIX Daily OHLC (Sorted by "Date" column) """ - j = self._get_indices_topix_raw( - from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd + return self._indices_topix_api.execute( + self, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, ) - d = json.loads(j) - data = d["topix"] - while "pagination_key" in d: - j = self._get_indices_topix_raw( - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["topix"] - df = pd.DataFrame.from_dict(data) - cols = constants.INDICES_TOPIX_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Date"], inplace=True) - return df[cols] - - # /fins - def _get_fins_statements_raw( - self, code: str = "", date_yyyymmdd: str = "", pagination_key: str = "" - ) -> str: - """ - get fins statements raw API return - - Args: - code: 銘柄コード - date_yyyymmdd: 日付(YYYYMMDD or YYYY-MM-DD) - pagination_key: ページングキー - - Returns: - str: fins statements - """ - url = f"{self.JQUANTS_API_BASE}/fins/statements" - params = { - "code": code, - "date": date_yyyymmdd, - } - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - - return ret.text def get_fins_statements( self, code: str = "", date_yyyymmdd: str = "" @@ -1281,42 +827,11 @@ def get_fins_statements( Returns: pd.DataFrame: 財務情報 (DisclosedDate, DisclosedTime, 及びLocalCode列でソートされています) """ - j = self._get_fins_statements_raw(code=code, date_yyyymmdd=date_yyyymmdd) - d = json.loads(j) - data = d["statements"] - while "pagination_key" in d: - j = self._get_fins_statements_raw( - code=code, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["statements"] - df = pd.DataFrame.from_dict(data) - cols = constants.FINS_STATEMENTS_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["DisclosedDate"] = pd.to_datetime(df["DisclosedDate"], format="%Y-%m-%d") - df["CurrentPeriodStartDate"] = pd.to_datetime( - df["CurrentPeriodStartDate"], format="%Y-%m-%d" - ) - df["CurrentPeriodEndDate"] = pd.to_datetime( - df["CurrentPeriodEndDate"], format="%Y-%m-%d" - ) - df["CurrentFiscalYearStartDate"] = pd.to_datetime( - df["CurrentFiscalYearStartDate"], format="%Y-%m-%d" - ) - df["CurrentFiscalYearEndDate"] = pd.to_datetime( - df["CurrentFiscalYearEndDate"], format="%Y-%m-%d" - ) - df["NextFiscalYearStartDate"] = pd.to_datetime( - df["NextFiscalYearStartDate"], format="%Y-%m-%d" - ) - df["NextFiscalYearEndDate"] = pd.to_datetime( - df["NextFiscalYearEndDate"], format="%Y-%m-%d" + return self._fins_statements_api.execute( + self, + code=code, + date_yyyymmdd=date_yyyymmdd, ) - df.sort_values(["DisclosedDate", "DisclosedTime", "LocalCode"], inplace=True) - return df[cols] def get_statements_range( self, @@ -1394,32 +909,6 @@ def get_statements_range( ["DisclosedDate", "DisclosedTime", "LocalCode"] ) - def _get_fins_fs_details_raw( - self, code: str = "", date_yyyymmdd: str = "", pagination_key: str = "" - ) -> str: - """ - get fins fs_details raw API return - - Args: - code: 銘柄コード - date_yyyymmdd: 開示日(YYYYMMDD or YYYY-MM-DD) - pagination_key: ページングキー - - Returns: - str: fins fs_details - """ - url = f"{self.JQUANTS_API_BASE}/fins/fs_details" - params = { - "code": code, - "date": date_yyyymmdd, - } - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - - return ret.text - def get_fins_fs_details( self, code: str = "", date_yyyymmdd: str = "" ) -> pd.DataFrame: @@ -1433,24 +922,11 @@ def get_fins_fs_details( Returns: pd.DataFrame: 財務諸表(BS/PL) (DisclosedDate, DisclosedTime, 及びLocalCode列でソートされています) """ - j = self._get_fins_fs_details_raw(code=code, date_yyyymmdd=date_yyyymmdd) - d = json.loads(j) - data = d["fs_details"] - while "pagination_key" in d: - j = self._get_fins_fs_details_raw( - code=code, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["fs_details"] - df = pd.json_normalize(data=data) - cols = constants.FINS_FS_DETAILS_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["DisclosedDate"] = pd.to_datetime(df["DisclosedDate"], format="%Y-%m-%d") - df.sort_values(["DisclosedDate", "DisclosedTime", "LocalCode"], inplace=True) - return df + return self._fins_fs_details_api.execute( + self, + code=code, + date_yyyymmdd=date_yyyymmdd, + ) def get_fs_details_range( self, @@ -1510,45 +986,6 @@ def get_fs_details_range( ["DisclosedDate", "DisclosedTime", "LocalCode"] ) - def _get_fins_dividend_raw( - self, - code: str = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - date_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - get information on dividends (determined and forecast) per share of listed companies etc.. raw API returns - - Args: - code: issue code (e.g. 27800 or 2780) - If a 4-character issue code is specified, only the data of common stock will be obtained - for the issue on which both common and preferred stocks are listed. - from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) - to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) - date_yyyymmdd: date of data (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - Returns: - str: information on dividends data - """ - url = f"{self.JQUANTS_API_BASE}/fins/dividend" - params = { - "code": code, - } - if date_yyyymmdd != "": - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_fins_dividend( self, code: str = "", @@ -1569,33 +1006,13 @@ def get_fins_dividend( Returns: pd.DataFrame: information on dividends data (Sorted by "Code") """ - j = self._get_fins_dividend_raw( + return self._fins_dividend_api.execute( + self, code=code, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) - d = json.loads(j) - data = d["dividend"] - while "pagination_key" in d: - j = self._get_fins_dividend_raw( - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["dividend"] - df = pd.DataFrame.from_dict(data) - cols = constants.FINS_DIVIDEND_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["AnnouncementDate"] = pd.to_datetime( - df["AnnouncementDate"], format="%Y-%m-%d" - ) - df.sort_values(["Code"], inplace=True) - return df[cols] def get_dividend_range( self, @@ -1630,27 +1047,6 @@ def get_dividend_range( ["AnnouncementDate", "AnnouncementTime", "Code"] ) - def _get_fins_announcement_raw( - self, - pagination_key: str = "", - ) -> str: - """ - get fin announcement raw API returns - - Args: - pagination_key: ページングキー - - Returns: - str: Schedule of financial announcement - """ - url = f"{self.JQUANTS_API_BASE}/fins/announcement" - params = {} - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_fins_announcement(self) -> pd.DataFrame: """ get fin announcement @@ -1661,45 +1057,7 @@ def get_fins_announcement(self) -> pd.DataFrame: Returns: pd.DataFrame: Schedule of financial announcement """ - j = self._get_fins_announcement_raw() - d = json.loads(j) - data = d["announcement"] - while "pagination_key" in d: - j = self._get_fins_announcement_raw(pagination_key=d["pagination_key"]) - d = json.loads(j) - data += d["announcement"] - df = pd.DataFrame.from_dict(data) - cols = constants.FINS_ANNOUNCEMENT_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Date", "Code"], inplace=True) - return df[cols] - - # /option - def _get_option_index_option_raw( - self, - date_yyyymmdd, - pagination_key: str = "", - ) -> str: - """ - get information on the OHLC etc. of Nikkei 225 raw API returns - - Args: - date_yyyymmdd: date of data (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - Returns: - str: Nikkei 225 Options' OHLC etc. - """ - url = f"{self.JQUANTS_API_BASE}/option/index_option" - params = { - "date": date_yyyymmdd, - } - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text + return self._fins_announcement_api.execute(self) def get_option_index_option( self, @@ -1714,25 +1072,10 @@ def get_option_index_option( pd.DataFrame: Nikkei 225 Options' OHLC etc. (Sorted by "Code") """ - j = self._get_option_index_option_raw( + return self._option_index_option_api.execute( + self, date_yyyymmdd=date_yyyymmdd, ) - d = json.loads(j) - data = d["index_option"] - while "pagination_key" in d: - j = self._get_option_index_option_raw( - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["index_option"] - df = pd.DataFrame.from_dict(data) - cols = constants.OPTION_INDEX_OPTION_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Code"], inplace=True) - return df[cols] def get_index_option_range( self, @@ -1765,36 +1108,6 @@ def get_index_option_range( buff.append(df) return pd.concat(buff).sort_values(["Code", "Date"]) - # /trading_calendar - def _get_markets_trading_calendar_raw( - self, - holiday_division: str = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - ) -> str: - """ - get trading calendar raw API returns - - Args: - holiday_division: 休日区分 - from_yyyymmdd: 取得開始日 - to_yyyymmdd: 取得終了日 - - Returns: - str: trading calendar - """ - url = f"{self.JQUANTS_API_BASE}/markets/trading_calendar" - params = {} - if holiday_division != "": - params["holidaydivision"] = holiday_division - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_markets_trading_calendar( self, holiday_division: str = "", @@ -1812,48 +1125,12 @@ def get_markets_trading_calendar( Returns: pd.DataFrame: 取り引きカレンダー (Date列でソートされています) """ - j = self._get_markets_trading_calendar_raw( + return self._markets_trading_calendar_api.execute( + self, holiday_division=holiday_division, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd, ) - d = json.loads(j) - df = pd.DataFrame.from_dict(d["trading_calendar"]) - cols = constants.MARKETS_TRADING_CALENDAR - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Date"], inplace=True) - return df[cols] - - # /derivatives - def _get_derivatives_futures_raw( - self, - date_yyyymmdd, - category, - contract_flag, - pagination_key: str = "", - ) -> str: - """ - get information on the OHLC etc. of futures raw API returns - - Args: - date_yyyymmdd: date of data (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - Returns: - str: Futures' OHLC etc. - """ - url = f"{self.JQUANTS_API_BASE}/derivatives/futures" - params = { - "category": category, - "date": date_yyyymmdd, - "contract_flag": contract_flag, - } - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text def get_derivatives_futures( self, @@ -1870,29 +1147,12 @@ def get_derivatives_futures( pd.DataFrame: Futures' OHLC etc. (Sorted by "Code") """ - j = self._get_derivatives_futures_raw( - category=category, + return self._derivatives_futures_api.execute( + self, date_yyyymmdd=date_yyyymmdd, + category=category, contract_flag=contract_flag, ) - d = json.loads(j) - data = d["futures"] - while "pagination_key" in d: - j = self._get_derivatives_futures_raw( - category=category, - date_yyyymmdd=date_yyyymmdd, - contract_flag=contract_flag, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["futures"] - df = pd.DataFrame.from_dict(data) - cols = constants.DERIVATIVES_FUTURES_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Code"], inplace=True) - return df[cols] def get_derivatives_futures_range( self, @@ -1930,36 +1190,6 @@ def get_derivatives_futures_range( buff.append(df) return pd.concat(buff).sort_values(["Code", "Date"]) - def _get_derivatives_options_raw( - self, - date_yyyymmdd, - category, - contract_flag, - code, - pagination_key: str = "", - ) -> str: - """ - get information on the OHLC etc. of options raw API returns - - Args: - date_yyyymmdd: date of data (e.g. 20210907 or 2021-09-07) - pagination_key: ページングキー - Returns: - str: Options' OHLC etc. - """ - url = f"{self.JQUANTS_API_BASE}/derivatives/options" - params = { - "category": category, - "date": date_yyyymmdd, - "contract_flag": contract_flag, - "code": code, - } - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_derivatives_options( self, date_yyyymmdd: str, @@ -1976,32 +1206,13 @@ def get_derivatives_options( pd.DataFrame: Futures' OHLC etc. (Sorted by "Code") """ - j = self._get_derivatives_options_raw( - category=category, + return self._derivatives_options_api.execute( + self, date_yyyymmdd=date_yyyymmdd, + category=category, contract_flag=contract_flag, code=code, ) - d = json.loads(j) - data = d["options"] - while "pagination_key" in d: - j = self._get_derivatives_options_raw( - category=category, - date_yyyymmdd=date_yyyymmdd, - contract_flag=contract_flag, - code=code, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["options"] - df = pd.DataFrame.from_dict(data) - cols = constants.DERIVATIVES_OPTIONS_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") - df.sort_values(["Code"], inplace=True) - return df[cols] - def get_derivatives_options_range( self, start_dt: DatetimeLike = "20170101", @@ -2040,49 +1251,6 @@ def get_derivatives_options_range( buff.append(df) return pd.concat(buff).sort_values(["Code", "Date"]) - def _get_markets_short_selling_positions_raw( - self, - code: str = "", - disclosed_date: str = "", - disclosed_date_from: str = "", - disclosed_date_to: str = "", - calculated_date: str = "", - pagination_key: str = "", - ) -> str: - """ - get short selling positions raw API returns - - Args: - code: issue code (e.g. 27800 or 2780) - If a 4-character issue code is specified, only the data of common stock - will be obtained for the issue on which both common and preferred stocks - are listed. - disclosed_date: disclosed date (e.g. 20240301 or 2024-03-01) - disclosed_date_from: disclosed date from (e.g. 20240301 or 2024-03-01) - disclosed_date_to: disclosed date to (e.g. 20240301 or 2024-03-01) - calculated_date: calculated date (e.g. 20240301 or 2024-03-01) - pagination_key: pagination key - Returns: - str: short selling positions - """ - url = f"{self.JQUANTS_API_BASE}/markets/short_selling_positions" - params = {} - if code != "": - params["code"] = code - if disclosed_date != "": - params["disclosed_date"] = disclosed_date - if disclosed_date_from != "": - params["disclosed_date_from"] = disclosed_date_from - if disclosed_date_to != "": - params["disclosed_date_to"] = disclosed_date_to - if calculated_date != "": - params["calculated_date"] = calculated_date - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_markets_short_selling_positions( self, code: str = "", @@ -2107,41 +1275,14 @@ def get_markets_short_selling_positions( pd.DataFrame: short selling positions (Sorted by "DisclosedDate", "CalculatedDate", and "Code" columns) """ - j = self._get_markets_short_selling_positions_raw( + return self._markets_short_selling_positions_api.execute( + self, code=code, disclosed_date=disclosed_date, disclosed_date_from=disclosed_date_from, disclosed_date_to=disclosed_date_to, calculated_date=calculated_date, ) - d = json.loads(j) - data = d["short_selling_positions"] - while "pagination_key" in d: - j = self._get_markets_short_selling_positions_raw( - code=code, - disclosed_date=disclosed_date, - disclosed_date_from=disclosed_date_from, - disclosed_date_to=disclosed_date_to, - calculated_date=calculated_date, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["short_selling_positions"] - df = pd.DataFrame.from_dict(data) - cols = constants.SHORT_SELLING_POSITIONS_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["DisclosedDate"] = pd.to_datetime( - df["DisclosedDate"], format="%Y-%m-%d", errors="coerce" - ) - df["CalculatedDate"] = pd.to_datetime( - df["CalculatedDate"], format="%Y-%m-%d", errors="coerce" - ) - df["CalculationInPreviousReportingDate"] = pd.to_datetime( - df["CalculationInPreviousReportingDate"], format="%Y-%m-%d", errors="coerce" - ) - df.sort_values(["DisclosedDate", "CalculatedDate", "Code"], inplace=True) - return df[cols] def get_markets_short_selling_positions_range( self, @@ -2176,47 +1317,6 @@ def get_markets_short_selling_positions_range( buff.append(df) return pd.concat(buff).sort_values(["DisclosedDate", "CalculatedDate", "Code"]) - # /markets/daily_margin_interest - def _get_markets_daily_margin_interest_raw( - self, - code: str = "", - from_yyyymmdd: str = "", - to_yyyymmdd: str = "", - date_yyyymmdd: str = "", - pagination_key: str = "", - ) -> str: - """ - get daily margin interest raw API returns - - Args: - code: issue code (e.g. 27800 or 2780) - If a 4-character issue code is specified, only the data of common stock - will be obtained for the issue on which both common and preferred stocks - are listed. - from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) - to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) - date_yyyymmdd: date of data (e.g. 20210907 or 2021-09-07) - pagination_key: pagination key - Returns: - str: daily margin interest - """ - url = f"{self.JQUANTS_API_BASE}/markets/daily_margin_interest" - params = {} - if code != "": - params["code"] = code - if date_yyyymmdd != "": - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd != "": - params["from"] = from_yyyymmdd - if to_yyyymmdd != "": - params["to"] = to_yyyymmdd - if pagination_key != "": - params["pagination_key"] = pagination_key - ret = self._get(url, params) - ret.encoding = self.RAW_ENCODING - return ret.text - def get_markets_daily_margin_interest( self, code: str = "", @@ -2238,32 +1338,13 @@ def get_markets_daily_margin_interest( Returns: pd.DataFrame: daily margin interest (Sorted by "Code" and "PublishedDate" columns) """ - j = self._get_markets_daily_margin_interest_raw( + return self._markets_daily_margin_interest_api.execute( + self, code=code, from_yyyymmdd=from_yyyymmdd, to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) - d = json.loads(j) - data = d["daily_margin_interest"] - while "pagination_key" in d: - j = self._get_markets_daily_margin_interest_raw( - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) - d = json.loads(j) - data += d["daily_margin_interest"] - df = pd.json_normalize(data=data) - cols = constants.DAILY_MARGIN_INTEREST_COLUMNS - if len(df) == 0: - return pd.DataFrame([], columns=cols) - df["PublishedDate"] = pd.to_datetime(df["PublishedDate"], format="%Y-%m-%d") - df["ApplicationDate"] = pd.to_datetime(df["ApplicationDate"], format="%Y-%m-%d") - df.sort_values(["Code", "PublishedDate"], inplace=True) - return df[cols] def get_daily_margin_interest_range( self, @@ -2296,3 +1377,4 @@ def get_daily_margin_interest_range( df = future.result() buff.append(df) return pd.concat(buff).sort_values(["Code", "PublishedDate"]) + diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py index 7db9738..aac05d3 100644 --- a/jquantsapi/client_v2.py +++ b/jquantsapi/client_v2.py @@ -1,14 +1,40 @@ import os import platform -from typing import Any, Dict, List, Optional +from concurrent.futures import ThreadPoolExecutor, as_completed +from datetime import datetime +from typing import Any, Dict, List, Optional, Union import pandas as pd # type: ignore import requests from requests.adapters import HTTPAdapter from urllib3.util import Retry -from jquantsapi import __version__ - +from jquantsapi import __version__, constants +from jquantsapi.apis.v2.equities import ( + EqBarsDailyAmApiV2, + EqBarsDailyApiV2, + EqEarningsCalApiV2, + EqInvestorTypesApiV2, + EqMasterApiV2, +) +from jquantsapi.apis.v2.fins import FinDetailsApiV2, FinDividendApiV2, FinSummaryApiV2 +from jquantsapi.apis.v2.markets import ( + MktBreakdownApiV2, + MktCalendarApiV2, + MktMarginAlertApiV2, + MktMarginInterestApiV2, + MktShortRatioApiV2, + MktShortSaleReportApiV2, +) +from jquantsapi.apis.v2.indices import IdxBarsDailyApiV2, IdxBarsDailyTopixApiV2 +from jquantsapi.apis.v2.derivatives import ( + DrvBarsDailyFutApiV2, + DrvBarsDailyOptApiV2, + DrvBarsDailyOpt225ApiV2, +) + + +DatetimeLike = Union[datetime, pd.Timestamp, str] class ClientV2: """ @@ -25,6 +51,7 @@ class ClientV2: USER_AGENT = "jqapi-python-v2" USER_AGENT_VERSION = __version__ RAW_ENCODING = "utf-8" + MAX_WORKERS = 5 def __init__(self, api_key: Optional[str] = None) -> None: """ @@ -43,6 +70,27 @@ def __init__(self, api_key: Optional[str] = None) -> None: self._api_key = api_key self._session: Optional[requests.Session] = None + # API 実装 (v2) + self._eq_master_api = EqMasterApiV2() + self._eq_bars_daily_api = EqBarsDailyApiV2() + self._eq_bars_daily_am_api = EqBarsDailyAmApiV2() + self._eq_investor_types_api = EqInvestorTypesApiV2() + self._fin_summary_api = FinSummaryApiV2() + self._fin_details_api = FinDetailsApiV2() + self._fin_dividend_api = FinDividendApiV2() + self._eq_earnings_cal_api = EqEarningsCalApiV2() + self._mkt_short_ratio_api = MktShortRatioApiV2() + self._mkt_margin_interest_api = MktMarginInterestApiV2() + self._mkt_breakdown_api = MktBreakdownApiV2() + self._mkt_calendar_api = MktCalendarApiV2() + self._mkt_short_sale_report_api = MktShortSaleReportApiV2() + self._mkt_margin_alert_api = MktMarginAlertApiV2() + self._idx_bars_daily_api = IdxBarsDailyApiV2() + self._idx_bars_daily_topix_api = IdxBarsDailyTopixApiV2() + self._drv_bars_daily_fut_api = DrvBarsDailyFutApiV2() + self._drv_bars_daily_opt_api = DrvBarsDailyOptApiV2() + self._drv_bars_daily_opt_225_api = DrvBarsDailyOpt225ApiV2() + # ------------------------------------------------------------------ # 内部ユーティリティ # ------------------------------------------------------------------ @@ -66,8 +114,8 @@ def _request_session( allowed_methods=allowed_methods, ) adapter = HTTPAdapter( - pool_connections=10, - pool_maxsize=10, + pool_connections=self.MAX_WORKERS + 10, + pool_maxsize=self.MAX_WORKERS + 10, max_retries=retry_strategy, ) self._session = requests.Session() @@ -130,18 +178,15 @@ def _get_paginated( return all_data # ------------------------------------------------------------------ - # /equities/master (path_old: /listed/info) + # eq-master (/equities/master) # ------------------------------------------------------------------ - def get_listed_info( + def get_eq_master( self, code: str = "", date: str = "", ) -> pd.DataFrame: """ - 上場銘柄一覧 (v2: /equities/master) - - v1 の `get_listed_info` と同等の目的ですが、カラム名は v2 仕様 - (例: CoName, CoNameEn, S17 など) になります。 + eq-master: 上場銘柄一覧 (v2: /equities/master) Args: code: 5桁の銘柄コード (例: 27800)。4桁指定も可能。 @@ -149,27 +194,78 @@ def get_listed_info( Returns: pd.DataFrame: 上場銘柄情報 """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if date: - params["date"] = date + return self._eq_master_api.execute(self, code=code, date=date) - data = self._get_paginated("/equities/master", params=params) - if not data: - return pd.DataFrame() + # ------------------------------------------------------------------ + # ユーティリティ: 業種・市場区分マスタ (v1 と同様のローカル定義) + # ------------------------------------------------------------------ + def get_market_segments() -> pd.DataFrame: + """ + 市場区分コードと名称 (v2 でも v1 と同様にローカル定義を利用) + """ + df = pd.DataFrame( + constants.MARKET_SEGMENT_DATA, columns=constants.MARKET_SEGMENT_COLUMNS + ) + df.sort_values(constants.MARKET_SEGMENT_COLUMNS[0], inplace=True) + return df - df = pd.DataFrame.from_records(data) - if "Date" in df.columns: - df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - if "Code" in df.columns: - df.sort_values("Code", inplace=True) - return df.reset_index(drop=True) + def get_17_sectors(self) -> pd.DataFrame: + """ + 17 業種コードと名称 + """ + df = pd.DataFrame(constants.SECTOR_17_DATA, columns=constants.SECTOR_17_COLUMNS) + df.sort_values(constants.SECTOR_17_COLUMNS[0], inplace=True) + return df + + def get_33_sectors(self) -> pd.DataFrame: + """ + 33 業種コードと名称 + """ + df = pd.DataFrame(constants.SECTOR_33_DATA, columns=constants.SECTOR_33_COLUMNS) + df.sort_values(constants.SECTOR_33_COLUMNS[0], inplace=True) + return df # ------------------------------------------------------------------ - # /equities/bars/daily (path_old: /prices/daily_quotes) + # get_list (v1 と同名のユーティリティ, eq-master ベース) # ------------------------------------------------------------------ - def get_prices_daily_quotes( + def get_list(self, code: str = "", date_yyyymmdd: str = "") -> pd.DataFrame: + """ + 上場銘柄一覧 (業種・市場区分の英語名を付与したユーティリティ) + + v2 の eq-master を利用し、v2 フィールド名で返却します。 + + Args: + code: 銘柄コード (任意) + date_yyyymmdd: 基準日 (YYYYMMDD or YYYY-MM-DD, 任意) + Returns: + pd.DataFrame: 上場銘柄情報 (v2 フィールド名) + """ + df_list = self.get_eq_master(code=code, date=date_yyyymmdd) + if df_list.empty: + return pd.DataFrame([], columns=constants.EQ_MASTER_COLUMNS_V2) + + # 17/33 業種 & 市場区分の英語名を付与 + df_17_sectors = self.get_17_sectors()[ + ["S17", "S17En"] + ] + df_33_sectors = self.get_33_sectors()[ + ["S33", "S33En"] + ] + df_segments = self.get_market_segments()[ + ["Mkt", "MktEn"] + ] + + df_list = pd.merge(df_list, df_17_sectors, how="left", on=["S17"]) + df_list = pd.merge(df_list, df_33_sectors, how="left", on=["S33"]) + df_list = pd.merge(df_list, df_segments, how="left", on=["Mkt"]) + + df_list.sort_values("Code", inplace=True) + return df_list + + # ------------------------------------------------------------------ + # eq-bars-daily (/equities/bars/daily) + # ------------------------------------------------------------------ + def get_eq_bars_daily( self, code: str = "", from_yyyymmdd: str = "", @@ -177,7 +273,7 @@ def get_prices_daily_quotes( date_yyyymmdd: str = "", ) -> pd.DataFrame: """ - 株価四本値 (v2: /equities/bars/daily) + eq-bars-daily: 株価四本値 (v2: /equities/bars/daily) Args: code: 銘柄コード (5桁 or 4桁) @@ -187,67 +283,70 @@ def get_prices_daily_quotes( Returns: pd.DataFrame: 株価データ (v2のフィールド名で返却) """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if date_yyyymmdd: - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd: - params["from"] = from_yyyymmdd - if to_yyyymmdd: - params["to"] = to_yyyymmdd - - data = self._get_paginated("/equities/bars/daily", params=params) - if not data: - return pd.DataFrame() + return self._eq_bars_daily_api.execute( + self, + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_eq_bars_daily_range( + self, + start_dt: DatetimeLike = "20170101", + end_dt: DatetimeLike = datetime.now(), + ) -> pd.DataFrame: + """ + 全銘柄の株価四本値を日付範囲指定して取得 (v2: /equities/bars/daily) - df = pd.DataFrame.from_records(data) - if "Date" in df.columns: - df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - sort_cols = [c for c in ["Code", "Date"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + Args: + start_dt: 取得開始日 + end_dt: 取得終了日 + Returns: + pd.DataFrame: 株価データ (Code, Date 列でソート) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + futures = [ + executor.submit( + self.get_eq_bars_daily, date_yyyymmdd=s.strftime("%Y-%m-%d") + ) + for s in dates + ] + for future in as_completed(futures): + df = future.result() + if not df.empty: + buff.append(df) + if not buff: + return pd.DataFrame() + return pd.concat(buff).sort_values(["Code", "Date"]).reset_index(drop=True) # ------------------------------------------------------------------ - # /equities/bars/daily/am (path_old: /prices/prices_am) + # eq-bars-daily-am (/equities/bars/daily/am) # ------------------------------------------------------------------ - def get_prices_prices_am(self, code: str = "") -> pd.DataFrame: + def get_eq_bars_daily_am(self, code: str = "") -> pd.DataFrame: """ - 前場四本値 (v2: /equities/bars/daily/am) + eq-bars-daily-am: 前場四本値 (v2: /equities/bars/daily/am) Args: code: 銘柄コード (5桁 or 4桁)。空文字の場合は全銘柄。 Returns: pd.DataFrame: 前場の株価データ """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - - data = self._get_paginated("/equities/bars/daily/am", params=params) - if not data: - return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - if "Date" in df.columns: - df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - if "Code" in df.columns: - df.sort_values("Code", inplace=True) - return df.reset_index(drop=True) + return self._eq_bars_daily_am_api.execute(self, code=code) # ------------------------------------------------------------------ - # /equities/investor-types (path_old: /markets/trades_spec) + # eq-investor-types (/equities/investor-types) # ------------------------------------------------------------------ - def get_markets_trades_spec( + def get_eq_investor_types( self, section: str = "", from_yyyymmdd: str = "", to_yyyymmdd: str = "", ) -> pd.DataFrame: """ - 投資部門別売買状況 (v2: /equities/investor-types) + eq-investor-types: 投資部門別売買状況 (v2: /equities/investor-types) Args: section: 市場区分 (例: \"TSEPrime\") @@ -256,30 +355,17 @@ def get_markets_trades_spec( Returns: pd.DataFrame: 投資部門別売買データ """ - params: Dict[str, Any] = {} - if section: - params["section"] = section - if from_yyyymmdd: - params["from"] = from_yyyymmdd - if to_yyyymmdd: - params["to"] = to_yyyymmdd - - data = self._get_paginated("/equities/investor-types", params=params) - if not data: - return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - if "PubDate" in df.columns: - df["PubDate"] = pd.to_datetime(df["PubDate"], errors="coerce") - sort_cols = [c for c in ["PubDate", "Section"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return self._eq_investor_types_api.execute( + self, + section=section, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + ) # ------------------------------------------------------------------ # /fins/summary (path_old: /fins/statements) # ------------------------------------------------------------------ - def get_fins_statements( + def get_fin_summary( self, code: str = "", date_yyyymmdd: str = "", @@ -293,29 +379,82 @@ def get_fins_statements( Returns: pd.DataFrame: 財務情報 (v2のフィールド名で返却) """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if date_yyyymmdd: - params["date"] = date_yyyymmdd + return self._fin_summary_api.execute( + self, + code=code, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_fin_summary_range( + self, + start_dt: DatetimeLike = "20080707", + end_dt: DatetimeLike = datetime.now(), + cache_dir: str = "", + ) -> pd.DataFrame: + """ + 財務情報サマリを日付範囲指定して取得 (v2: /fins/summary) - data = self._get_paginated("/fins/summary", params=params) - if not data: + Args: + start_dt: 取得開始日 + end_dt: 取得終了日 + cache_dir: キャッシュディレクトリ (未指定時は ~/.jquants/cache/v2 を使用) + """ + if not cache_dir: + cache_dir = os.path.expanduser("~/.jquants/cache/v2") + + buff: list[pd.DataFrame] = [] + futures: dict[Any, str] = {} + dates = pd.date_range(start_dt, end_dt, freq="D") + + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + for s in dates: + yyyymmdd = s.strftime("%Y%m%d") + yyyy = yyyymmdd[:4] + cache_file = f"fin_summary_{yyyymmdd}.csv.gz" + cache_path = os.path.join(cache_dir, yyyy, cache_file) + + if os.path.isfile(cache_path): + df = pd.read_csv(cache_path, dtype=str) + # 日付カラムをdatetimeに変換 + date_cols = [ + "DiscDate", + "CurPerSt", + "CurPerEn", + "CurFYSt", + "CurFYEn", + "NxtFYSt", + "NxtFYEn", + ] + for col in date_cols: + if col in df.columns: + df[col] = pd.to_datetime(df[col], errors="coerce") + buff.append(df) + else: + future = executor.submit( + self.get_fin_summary, date_yyyymmdd=yyyymmdd + ) + futures[future] = cache_path + + for future in as_completed(futures): + df = future.result() + if df.empty: + continue + buff.append(df) + cache_path = futures[future] + os.makedirs(os.path.dirname(cache_path), exist_ok=True) + df.to_csv(cache_path, index=False) + + if not buff: return pd.DataFrame() - df = pd.DataFrame.from_records(data) - for col in ("DiscDate", "CurPerSt", "CurPerEn", "CurFYSt", "CurFYEn", "NxtFYSt", "NxtFYEn"): - if col in df.columns: - df[col] = pd.to_datetime(df[col], errors="coerce") - sort_cols = [c for c in ["DiscDate", "DiscTime", "Code"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return pd.concat(buff).sort_values( + ["DiscDate", "DiscTime", "Code"] + ).reset_index(drop=True) # ------------------------------------------------------------------ # /fins/details (path_old: /fins/fs_details) # ------------------------------------------------------------------ - def get_fins_fs_details( + def get_fin_details( self, code: str = "", date_yyyymmdd: str = "", @@ -329,28 +468,71 @@ def get_fins_fs_details( Returns: pd.DataFrame: 財務諸表詳細 (FS列に各項目が含まれる) """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if date_yyyymmdd: - params["date"] = date_yyyymmdd + return self._fin_details_api.execute( + self, + code=code, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_fin_details_range( + self, + start_dt: DatetimeLike = "20080707", + end_dt: DatetimeLike = datetime.now(), + cache_dir: str = "", + ) -> pd.DataFrame: + """ + 財務諸表詳細を日付範囲指定して取得 (v2: /fins/details) - data = self._get_paginated("/fins/details", params=params) - if not data: + Args: + start_dt: 取得開始日 + end_dt: 取得終了日 + cache_dir: キャッシュディレクトリ (未指定時は ~/.jquants/cache/v2 を使用) + """ + if not cache_dir: + cache_dir = os.path.expanduser("~/.jquants/cache/v2") + + buff: list[pd.DataFrame] = [] + futures: dict[Any, str] = {} + dates = pd.date_range(start_dt, end_dt, freq="D") + + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + for s in dates: + yyyymmdd = s.strftime("%Y%m%d") + yyyy = yyyymmdd[:4] + cache_file = f"fin_details_{yyyymmdd}.csv.gz" + cache_path = os.path.join(cache_dir, yyyy, cache_file) + + if os.path.isfile(cache_path): + df = pd.read_csv(cache_path, dtype=str) + if "DiscDate" in df.columns: + df["DiscDate"] = pd.to_datetime(df["DiscDate"], errors="coerce") + buff.append(df) + else: + future = executor.submit( + self.get_fin_details, date_yyyymmdd=yyyymmdd + ) + futures[future] = cache_path + + for future in as_completed(futures): + df = future.result() + if df.empty: + continue + buff.append(df) + cache_path = futures[future] + os.makedirs(os.path.dirname(cache_path), exist_ok=True) + df.to_csv(cache_path, index=False) + + if not buff: return pd.DataFrame() - df = pd.DataFrame.from_records(data) - if "DiscDate" in df.columns: - df["DiscDate"] = pd.to_datetime(df["DiscDate"], errors="coerce") - sort_cols = [c for c in ["DiscDate", "DiscTime", "Code"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return pd.concat(buff).sort_values( + ["DiscDate", "DiscTime", "Code"] + ).reset_index(drop=True) # ------------------------------------------------------------------ # /fins/dividend (path_old: /fins/dividend) # ------------------------------------------------------------------ - def get_fins_dividend( + def get_fin_dividend( self, code: str = "", from_yyyymmdd: str = "", @@ -368,55 +550,30 @@ def get_fins_dividend( Returns: pd.DataFrame: 配当金データ """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if date_yyyymmdd: - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd: - params["from"] = from_yyyymmdd - if to_yyyymmdd: - params["to"] = to_yyyymmdd - - data = self._get_paginated("/fins/dividend", params=params) - if not data: - return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - if "PubDate" in df.columns: - df["PubDate"] = pd.to_datetime(df["PubDate"], errors="coerce") - sort_cols = [c for c in ["PubDate", "Code"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return self._fin_dividend_api.execute( + self, + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) # ------------------------------------------------------------------ # /equities/earnings-calendar (path_old: /fins/announcement) # ------------------------------------------------------------------ - def get_fins_announcement(self) -> pd.DataFrame: + def get_eq_earnings_cal(self) -> pd.DataFrame: """ 決算発表予定日 (v2: /equities/earnings-calendar) Returns: pd.DataFrame: 決算発表予定データ """ - data = self._get_paginated("/equities/earnings-calendar", params={}) - if not data: - return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - if "Date" in df.columns: - df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - sort_cols = [c for c in ["Date", "Code"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return self._eq_earnings_cal_api.execute(self) # ------------------------------------------------------------------ # /markets/short-ratio (path_old: /markets/short_selling) # ------------------------------------------------------------------ - def get_markets_short_selling( + def get_mkt_short_ratio( self, sector_33_code: str = "", from_yyyymmdd: str = "", @@ -434,33 +591,45 @@ def get_markets_short_selling( Returns: pd.DataFrame: 業種別空売り比率データ """ - params: Dict[str, Any] = {} - if sector_33_code: - params["s33"] = sector_33_code - if date_yyyymmdd: - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd: - params["from"] = from_yyyymmdd - if to_yyyymmdd: - params["to"] = to_yyyymmdd - - data = self._get_paginated("/markets/short-ratio", params=params) - if not data: + return self._mkt_short_ratio_api.execute( + self, + sector_33_code=sector_33_code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_mkt_short_ratio_range( + self, + start_dt: DatetimeLike = "20170101", + end_dt: DatetimeLike = datetime.now(), + ) -> pd.DataFrame: + """ + 全33業種の空売り比率データを日付範囲指定して取得 (v2: /markets/short-ratio) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + futures = [ + executor.submit( + self.get_mkt_short_ratio, date_yyyymmdd=s.strftime("%Y-%m-%d") + ) + for s in dates + ] + for future in as_completed(futures): + df = future.result() + if not df.empty: + buff.append(df) + if not buff: return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - if "Date" in df.columns: - df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - sort_cols = [c for c in ["Date", "S33"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return pd.concat(buff).sort_values( + ["Date", "S33"] + ).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/short-sale-report (path_old: /markets/short_selling_positions) # ------------------------------------------------------------------ - def get_markets_short_selling_positions( + def get_mkt_short_sale_report( self, code: str = "", disclosed_date: str = "", @@ -480,35 +649,47 @@ def get_markets_short_selling_positions( Returns: pd.DataFrame: 空売り残高報告データ """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if disclosed_date: - params["disc_date"] = disclosed_date - if disclosed_date_from: - params["disc_date_from"] = disclosed_date_from - if disclosed_date_to: - params["disc_date_to"] = disclosed_date_to - if calculated_date: - params["calc_date"] = calculated_date - - data = self._get_paginated("/markets/short-sale-report", params=params) - if not data: + return self._mkt_short_sale_report_api.execute( + self, + code=code, + disclosed_date=disclosed_date, + disclosed_date_from=disclosed_date_from, + disclosed_date_to=disclosed_date_to, + calculated_date=calculated_date, + ) + + def get_mkt_short_sale_report_range( + self, + start_dt: DatetimeLike = "20131107", + end_dt: DatetimeLike = datetime.now(), + ) -> pd.DataFrame: + """ + 空売り残高報告データを日付範囲指定して取得 (v2: /markets/short-sale-report) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + futures = [ + executor.submit( + self.get_mkt_short_sale_report, + disclosed_date=s.strftime("%Y-%m-%d"), + ) + for s in dates + ] + for future in as_completed(futures): + df = future.result() + if not df.empty: + buff.append(df) + if not buff: return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - for col in ("DiscDate", "CalcDate", "PrevRptDate"): - if col in df.columns: - df[col] = pd.to_datetime(df[col], errors="coerce") - sort_cols = [c for c in ["DiscDate", "CalcDate", "Code"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return pd.concat(buff).sort_values( + ["DiscDate", "CalcDate", "Code"] + ).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/margin-interest (path_old: /markets/weekly_margin_interest) # ------------------------------------------------------------------ - def get_markets_weekly_margin_interest( + def get_mkt_margin_interest( self, code: str = "", from_yyyymmdd: str = "", @@ -526,33 +707,46 @@ def get_markets_weekly_margin_interest( Returns: pd.DataFrame: 信用取引週末残高データ """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if date_yyyymmdd: - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd: - params["from"] = from_yyyymmdd - if to_yyyymmdd: - params["to"] = to_yyyymmdd - - data = self._get_paginated("/markets/margin-interest", params=params) - if not data: + return self._mkt_margin_interest_api.execute( + self, + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_mkt_margin_interest_range( + self, + start_dt: DatetimeLike = "20170101", + end_dt: DatetimeLike = datetime.now(), + ) -> pd.DataFrame: + """ + 信用取引週末残高を日付範囲指定して取得 (v2: /markets/margin-interest) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + futures = [ + executor.submit( + self.get_mkt_margin_interest, + date_yyyymmdd=s.strftime("%Y-%m-%d"), + ) + for s in dates + ] + for future in as_completed(futures): + df = future.result() + if not df.empty: + buff.append(df) + if not buff: return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - if "Date" in df.columns: - df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - sort_cols = [c for c in ["Date", "Code"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return pd.concat(buff).sort_values( + ["Date", "Code"] + ).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/margin-alert (path_old: /markets/daily_margin_interest) # ------------------------------------------------------------------ - def get_markets_daily_margin_interest( + def get_mkt_margin_alert( self, code: str = "", from_yyyymmdd: str = "", @@ -570,33 +764,46 @@ def get_markets_daily_margin_interest( Returns: pd.DataFrame: 日々公表信用取引残高データ """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if date_yyyymmdd: - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd: - params["from"] = from_yyyymmdd - if to_yyyymmdd: - params["to"] = to_yyyymmdd - - data = self._get_paginated("/markets/margin-alert", params=params) - if not data: + return self._mkt_margin_alert_api.execute( + self, + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_mkt_margin_alert_range( + self, + start_dt: DatetimeLike = "20170101", + end_dt: DatetimeLike = datetime.now(), + ) -> pd.DataFrame: + """ + 日々公表信用取引残高を日付範囲指定して取得 (v2: /markets/margin-alert) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + futures = [ + executor.submit( + self.get_mkt_margin_alert, + date_yyyymmdd=s.strftime("%Y-%m-%d"), + ) + for s in dates + ] + for future in as_completed(futures): + df = future.result() + if not df.empty: + buff.append(df) + if not buff: return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - if "PubDate" in df.columns: - df["PubDate"] = pd.to_datetime(df["PubDate"], errors="coerce") - sort_cols = [c for c in ["Code", "PubDate"] if c in df.columns] - if sort_cols: - df.sort_values(sort_cols, inplace=True) - return df.reset_index(drop=True) + return pd.concat(buff).sort_values( + ["Date", "Code"] + ).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/breakdown (path_old: /markets/breakdown) # ------------------------------------------------------------------ - def get_markets_breakdown( + def get_mkt_breakdown( self, code: str = "", from_yyyymmdd: str = "", @@ -614,32 +821,45 @@ def get_markets_breakdown( Returns: pd.DataFrame: 売買内訳データ """ - params: Dict[str, Any] = {} - if code: - params["code"] = code - if date_yyyymmdd: - params["date"] = date_yyyymmdd - else: - if from_yyyymmdd: - params["from"] = from_yyyymmdd - if to_yyyymmdd: - params["to"] = to_yyyymmdd - - data = self._get_paginated("/markets/breakdown", params=params) - if not data: + return self._mkt_breakdown_api.execute( + self, + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_mkt_breakdown_range( + self, + start_dt: DatetimeLike = "20170101", + end_dt: DatetimeLike = datetime.now(), + ) -> pd.DataFrame: + """ + 売買内訳データを日付範囲指定して取得 (v2: /markets/breakdown) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + futures = [ + executor.submit( + self.get_mkt_breakdown, date_yyyymmdd=s.strftime("%Y-%m-%d") + ) + for s in dates + ] + for future in as_completed(futures): + df = future.result() + if not df.empty: + buff.append(df) + if not buff: return pd.DataFrame() - - df = pd.DataFrame.from_records(data) - if "Date" in df.columns: - df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - if "Code" in df.columns: - df.sort_values(["Code", "Date"], inplace=True) - return df.reset_index(drop=True) + return pd.concat(buff).sort_values( + ["Code", "Date"] + ).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/calendar (path_old: /markets/trading_calendar) # ------------------------------------------------------------------ - def get_markets_trading_calendar( + def get_mkt_calendar( self, holiday_division: str = "", from_yyyymmdd: str = "", @@ -655,23 +875,193 @@ def get_markets_trading_calendar( Returns: pd.DataFrame: 取引カレンダーデータ """ - params: Dict[str, Any] = {} - if holiday_division: - params["hol_div"] = holiday_division - if from_yyyymmdd: - params["from"] = from_yyyymmdd - if to_yyyymmdd: - params["to"] = to_yyyymmdd + return self._mkt_calendar_api.execute( + self, + holiday_division=holiday_division, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + ) + + # ------------------------------------------------------------------ + # indices (v2: /indices/bars/daily, /indices/bars/daily/topix) + # ------------------------------------------------------------------ + def get_idx_bars_daily( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + 指数四本値 (v2: /indices/bars/daily) + + Args: + code: 指数コード + from_yyyymmdd: 取得開始日 + to_yyyymmdd: 取得終了日 + date_yyyymmdd: 取得日 + """ + return self._idx_bars_daily_api.execute( + self, + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_idx_bars_daily_topix( + self, + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + TOPIX 指数四本値 (v2: /indices/bars/daily/topix) + + Args: + from_yyyymmdd: 取得開始日 + to_yyyymmdd: 取得終了日 + """ + return self._idx_bars_daily_topix_api.execute( + self, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + ) - data = self._get_paginated("/markets/calendar", params=params) - if not data: + # ------------------------------------------------------------------ + # derivatives (v2: /derivatives/bars/daily/*) + # ------------------------------------------------------------------ + def get_drv_bars_daily_fut( + self, + date_yyyymmdd: str, + category: str = "", + contract_flag: str = "", + ) -> pd.DataFrame: + """ + 先物四本値 (v2: /derivatives/bars/daily/futures) + """ + return self._drv_bars_daily_fut_api.execute( + self, + date_yyyymmdd=date_yyyymmdd, + category=category, + contract_flag=contract_flag, + ) + + def get_drv_bars_daily_opt( + self, + date_yyyymmdd: str, + category: str = "", + contract_flag: str = "", + code: str = "", + ) -> pd.DataFrame: + """ + オプション四本値 (v2: /derivatives/bars/daily/options) + """ + return self._drv_bars_daily_opt_api.execute( + self, + date_yyyymmdd=date_yyyymmdd, + category=category, + contract_flag=contract_flag, + code=code, + ) + + def get_drv_bars_daily_opt_225( + self, + date_yyyymmdd: str, + ) -> pd.DataFrame: + """ + 日経225オプション四本値 (v2: /derivatives/bars/daily/options/225) + """ + return self._drv_bars_daily_opt_225_api.execute( + self, + date_yyyymmdd=date_yyyymmdd, + ) + + def get_drv_bars_daily_fut_range( + self, + start_dt: DatetimeLike = "20170101", + end_dt: DatetimeLike = datetime.now(), + category: str = "", + contract_flag: str = "", + ) -> pd.DataFrame: + """ + 先物四本値を日付範囲指定して取得 (v2: /derivatives/bars/daily/futures) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + futures = [ + executor.submit( + self.get_drv_bars_daily_fut, + date_yyyymmdd=s.strftime("%Y-%m-%d"), + category=category, + contract_flag=contract_flag, + ) + for s in dates + ] + for future in as_completed(futures): + df = future.result() + if not df.empty: + buff.append(df) + if not buff: + return pd.DataFrame() + return pd.concat(buff).sort_values(["Code", "Date"]).reset_index(drop=True) + + def get_drv_bars_daily_opt_range( + self, + start_dt: DatetimeLike = "20170101", + end_dt: DatetimeLike = datetime.now(), + category: str = "", + contract_flag: str = "", + code: str = "", + ) -> pd.DataFrame: + """ + オプション四本値を日付範囲指定して取得 (v2: /derivatives/bars/daily/options) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + options = [ + executor.submit( + self.get_drv_bars_daily_opt, + date_yyyymmdd=s.strftime("%Y-%m-%d"), + category=category, + contract_flag=contract_flag, + code=code, + ) + for s in dates + ] + for option in as_completed(options): + df = option.result() + if not df.empty: + buff.append(df) + if not buff: return pd.DataFrame() + return pd.concat(buff).sort_values(["Code", "Date"]).reset_index(drop=True) - df = pd.DataFrame.from_records(data) - if "Date" in df.columns: - df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - if "Date" in df.columns: - df.sort_values("Date", inplace=True) - return df.reset_index(drop=True) + def get_drv_bars_daily_opt_225_range( + self, + start_dt: DatetimeLike = "20170101", + end_dt: DatetimeLike = datetime.now(), + ) -> pd.DataFrame: + """ + 日経225オプション四本値を日付範囲指定して取得 (v2: /derivatives/bars/daily/options/225) + """ + buff: list[pd.DataFrame] = [] + dates = pd.date_range(start_dt, end_dt, freq="D") + with ThreadPoolExecutor(max_workers=self.MAX_WORKERS) as executor: + futures = [ + executor.submit( + self.get_drv_bars_daily_opt_225, + date_yyyymmdd=s.strftime("%Y-%m-%d"), + ) + for s in dates + ] + for future in as_completed(futures): + df = future.result() + if not df.empty: + buff.append(df) + if not buff: + return pd.DataFrame() + return pd.concat(buff).sort_values(["Code", "Date"]).reset_index(drop=True) diff --git a/jquantsapi/constants.py b/jquantsapi/constants.py index 887dbb8..d5925b0 100644 --- a/jquantsapi/constants.py +++ b/jquantsapi/constants.py @@ -1,34 +1,6 @@ -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info -LISTED_INFO_COLUMNS = [ - "Date", - "Code", - "CompanyName", - "CompanyNameEnglish", - "Sector17Code", - "Sector17CodeName", - "Sector33Code", - "Sector33CodeName", - "ScaleCategory", - "MarketCode", - "MarketCodeName", -] - -LISTED_INFO_STANDARD_PREMIUM_COLUMNS = [ - "Date", - "Code", - "CompanyName", - "CompanyNameEnglish", - "Sector17Code", - "Sector17CodeName", - "Sector33Code", - "Sector33CodeName", - "ScaleCategory", - "MarketCode", - "MarketCodeName", - "MarginCode", - "MarginCodeName", -] +# ============================================================================ +# 共通データ(V1/V2両方で使用) +# ============================================================================ # ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/sector17code # ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/sector17code @@ -100,6 +72,63 @@ ("9999", "その他", "Other", "99"), ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/marketcode +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/marketcode +MARKET_SEGMENT_COLUMNS = [ + "MarketCode", + "MarketCodeName", + "MarketCodeNameEnglish", +] +MARKET_SEGMENT_DATA = [ + ("0101", "東証一部", "1st Section"), + ("0102", "東証二部", "2nd Section"), + ("0104", "マザーズ", "Mothers"), + ("0105", "TOKYO PRO MARKET", "TOKYO PRO MARKET"), + ("0106", "JASDAQ スタンダード", "JASDAQ Standard"), + ("0107", "JASDAQ グロース", "JASDAQ Growth"), + ("0109", "その他", "Others"), + ("0111", "プライム", "Prime"), + ("0112", "スタンダード", "Standard"), + ("0113", "グロース", "Growth"), +] + + +# ============================================================================ +# V1 API用カラム定義 +# ============================================================================ + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info +LISTED_INFO_COLUMNS = [ + "Date", + "Code", + "CompanyName", + "CompanyNameEnglish", + "Sector17Code", + "Sector17CodeName", + "Sector33Code", + "Sector33CodeName", + "ScaleCategory", + "MarketCode", + "MarketCodeName", +] + +LISTED_INFO_STANDARD_PREMIUM_COLUMNS = [ + "Date", + "Code", + "CompanyName", + "CompanyNameEnglish", + "Sector17Code", + "Sector17CodeName", + "Sector33Code", + "Sector33CodeName", + "ScaleCategory", + "MarketCode", + "MarketCodeName", + "MarginCode", + "MarginCodeName", +] + # ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/daily_quotes # ref. en https://jpx.gitbook.io/j-quants-en/api-reference/daily_quotes PRICES_DAILY_QUOTES_COLUMNS = [ @@ -166,6 +195,17 @@ "AfternoonAdjustmentVolume", ] +PRICES_PRICES_AM_COLUMNS = [ + "Date", + "Code", + "MorningOpen", + "MorningHigh", + "MorningLow", + "MorningClose", + "MorningVolume", + "MorningTurnoverValue", +] + # ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/indices # ref. en https://jpx.gitbook.io/j-quants-en/api-reference/indices INDICES_COLUMNS = [ @@ -262,24 +302,38 @@ "IssueType", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/marketcode -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/marketcode -MARKET_SEGMENT_COLUMNS = [ - "MarketCode", - "MarketCodeName", - "MarketCodeNameEnglish", +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/short_selling +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/short_selling +MARKET_SHORT_SELLING_COLUMNS = [ + "Date", + "Sector33Code", + "SellingExcludingShortSellingTurnoverValue", + "ShortSellingWithRestrictionsTurnoverValue", + "ShortSellingWithoutRestrictionsTurnoverValue", ] -MARKET_SEGMENT_DATA = [ - ("0101", "東証一部", "1st Section"), - ("0102", "東証二部", "2nd Section"), - ("0104", "マザーズ", "Mothers"), - ("0105", "TOKYO PRO MARKET", "TOKYO PRO MARKET"), - ("0106", "JASDAQ スタンダード", "JASDAQ Standard"), - ("0107", "JASDAQ グロース", "JASDAQ Growth"), - ("0109", "その他", "Others"), - ("0111", "プライム", "Prime"), - ("0112", "スタンダード", "Standard"), - ("0113", "グロース", "Growth"), + +MARKETS_BREAKDOWN_COLUMNS = [ + "Date", + "Code", + "LongSellValue", + "ShortSellWithoutMarginValue", + "MarginSellNewValue", + "MarginSellCloseValue", + "LongBuyValue", + "MarginBuyNewValue", + "MarginBuyCloseValue", + "LongSellVolume", + "ShortSellWithoutMarginVolume", + "MarginSellNewVolume", + "MarginSellCloseVolume", + "LongBuyVolume", + "MarginBuyNewVolume", + "MarginBuyCloseVolume", +] + +MARKETS_TRADING_CALENDAR = [ + "Date", + "HolidayDivision", ] # ref ja https://jpx.gitbook.io/j-quants-ja/api-reference/statements @@ -394,6 +448,40 @@ "NextYearForecastNonConsolidatedEarningsPerShare", ] +FINS_FS_DETAILS_COLUMNS = [ + "DisclosedDate", + "DisclosedTime", + "LocalCode", + "DisclosureNumber", + "TypeOfDocument", +] + +FINS_DIVIDEND_COLUMNS = [ + "AnnouncementDate", + "AnnouncementTime", + "Code", + "ReferenceNumber", + "StatusCode", + "BoardMeetingDate", + "InterimFinalCode", + "ForecastResultCode", + "InterimFinalTerm", + "GrossDividendRate", + "RecordDate", + "ExDate", + "ActualRecordDate", + "PayableDate", + "CAReferenceNumber", + "DistributionAmount", + "RetainedEarnings", + "DeemedDividend", + "DeemedCapitalGains", + "NetAssetDecreaseRatio", + "CommemorativeSpecialCode", + "CommemorativeDividendRate", + "SpecialDividendRate", +] + # ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/announcement # ref. en https://jpx.gitbook.io/j-quants-en/api-reference/announcement FINS_ANNOUNCEMENT_COLUMNS = [ @@ -406,15 +494,6 @@ "Section", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/short_selling -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/short_selling -MARKET_SHORT_SELLING_COLUMNS = [ - "Date", - "Sector33Code", - "SellingExcludingShortSellingTurnoverValue", - "ShortSellingWithRestrictionsTurnoverValue", - "ShortSellingWithoutRestrictionsTurnoverValue", -] # ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/index_option # ref. en https://jpx.gitbook.io/j-quants-en/api-reference/index_option OPTION_INDEX_OPTION_COLUMNS = [ @@ -450,75 +529,6 @@ "InterestRate", ] -MARKETS_BREAKDOWN_COLUMNS = [ - "Date", - "Code", - "LongSellValue", - "ShortSellWithoutMarginValue", - "MarginSellNewValue", - "MarginSellCloseValue", - "LongBuyValue", - "MarginBuyNewValue", - "MarginBuyCloseValue", - "LongSellVolume", - "ShortSellWithoutMarginVolume", - "MarginSellNewVolume", - "MarginSellCloseVolume", - "LongBuyVolume", - "MarginBuyNewVolume", - "MarginBuyCloseVolume", -] - -FINS_DIVIDEND_COLUMNS = [ - "AnnouncementDate", - "AnnouncementTime", - "Code", - "ReferenceNumber", - "StatusCode", - "BoardMeetingDate", - "InterimFinalCode", - "ForecastResultCode", - "InterimFinalTerm", - "GrossDividendRate", - "RecordDate", - "ExDate", - "ActualRecordDate", - "PayableDate", - "CAReferenceNumber", - "DistributionAmount", - "RetainedEarnings", - "DeemedDividend", - "DeemedCapitalGains", - "NetAssetDecreaseRatio", - "CommemorativeSpecialCode", - "CommemorativeDividendRate", - "SpecialDividendRate", -] - -PRICES_PRICES_AM_COLUMNS = [ - "Date", - "Code", - "MorningOpen", - "MorningHigh", - "MorningLow", - "MorningClose", - "MorningVolume", - "MorningTurnoverValue", -] - -MARKETS_TRADING_CALENDAR = [ - "Date", - "HolidayDivision", -] - -FINS_FS_DETAILS_COLUMNS = [ - "DisclosedDate", - "DisclosedTime", - "LocalCode", - "DisclosureNumber", - "TypeOfDocument", -] - DERIVATIVES_FUTURES_COLUMNS = [ "Date", "Code", @@ -637,3 +647,501 @@ "DailyChangeLongStandardizedMarginOutstanding", "TSEMarginBorrowingAndLendingRegulationClassification", ] + + +# ============================================================================ +# V2 API用カラム定義 +# ============================================================================ + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/eq-master +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/eq-master +EQ_MASTER_COLUMNS_V2 = [ + "Date", + "Code", + "CoName", + "CoNameEn", + "S17", + "S17Nm", + "S33", + "S33Nm", + "ScaleCat", + "Mkt", + "MktNm", + "Mrgn", + "MrgnNm", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/eq-bars-daily +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/eq-bars-daily +EQ_BARS_DAILY_COLUMNS_V2 = [ + "Date", + "Code", + "O", + "H", + "L", + "C", + "UL", + "LL", + "Vo", + "Va", + "AdjFactor", + "AdjO", + "AdjH", + "AdjL", + "AdjC", + "AdjVo", + "MO", + "MH", + "ML", + "MC", + "MUL", + "MLL", + "MVo", + "MVa", + "MAdjO", + "MAdjH", + "MAdjL", + "MAdjC", + "MAdjVo", + "AO", + "AH", + "AL", + "AC", + "AUL", + "ALL", + "AVo", + "AVa", + "AAdjO", + "AAdjH", + "AAdjL", + "AAdjC", + "AAdjVo", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/eq-bars-daily +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/eq-bars-daily +PRICES_PRICES_AM_COLUMNS_V2 = [ + "Date", + "Code", + "MO", + "MH", + "ML", + "MC", + "MVo", + "MVa", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/indices-bars-daily +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/indices-bars-daily +INDICES_COLUMNS_V2 = [ + "Date", + "Code", + "O", + "H", + "L", + "C", +] + +INDICES_TOPIX_COLUMNS_V2 = [ + "Date", + "O", + "H", + "L", + "C", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/eq-investor-types +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/eq-investor-types +EQ_INVESTOR_TYPES_COLUMNS_V2 = [ + "PubDate", + "StDate", + "EnDate", + "Section", + "PropSell", + "PropBuy", + "PropTot", + "PropBal", + "BrkSell", + "BrkBuy", + "BrkTot", + "BrkBal", + "TotSell", + "TotBuy", + "TotTot", + "TotBal", + "IndSell", + "IndBuy", + "IndTot", + "IndBal", + "FrgnSell", + "FrgnBuy", + "FrgnTot", + "FrgnBal", + "SecCoSell", + "SecCoBuy", + "SecCoTot", + "SecCoBal", + "InvTrSell", + "InvTrBuy", + "InvTrTot", + "InvTrBal", + "BusCoSell", + "BusCoBuy", + "BusCoTot", + "BusCoBal", + "OthCoSell", + "OthCoBuy", + "OthCoTot", + "OthCoBal", + "InsCoSell", + "InsCoBuy", + "InsCoTot", + "InsCoBal", + "BankSell", + "BankBuy", + "BankTot", + "BankBal", + "TrstBnkSell", + "TrstBnkBuy", + "TrstBnkTot", + "TrstBnkBal", + "OthFinSell", + "OthFinBuy", + "OthFinTot", + "OthFinBal", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/markets/margin-interest +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/markets/margin-interest +MARKETS_WEEKLY_MARGIN_INTEREST_COLUMNS_V2 = [ + "Date", + "Code", + "ShrtVol", + "LongVol", + "ShrtNegVol", + "LongNegVol", + "ShrtStdVol", + "LongStdVol", + "IssType", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/mkt-short-ratio +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/mkt-short-ratio +MKT_SHORT_RATIO_COLUMNS_V2 = [ + "Date", + "S33", + "SellExShortVa", + "ShrtWithResVa", + "ShrtNoResVa", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/mkt-breakdown +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/mkt-breakdown +MKT_BREAKDOWN_COLUMNS_V2 = [ + "Date", + "Code", + "LongSellVa", + "ShrtNoMrgnVa", + "MrgnSellNewVa", + "MrgnSellCloseVa", + "LongBuyVa", + "MrgnBuyNewVa", + "MrgnBuyCloseVa", + "LongSellVo", + "ShrtNoMrgnVo", + "MrgnSellNewVo", + "MrgnSellCloseVo", + "LongBuyVo", + "MrgnBuyNewVo", + "MrgnBuyCloseVo", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/markets/calendar +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/markets/calendar +MARKETS_TRADING_CALENDAR_COLUMNS_V2 = [ + "Date", + "HolDiv", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/fin-summary +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/fin-summary +FIN_SUMMARY_COLUMNS_V2 = [ + "DiscDate", + "DiscTime", + "Code", + "DiscNo", + "DocType", + "CurPerType", + "CurPerSt", + "CurPerEn", + "CurFYSt", + "CurFYEn", + "NxtFYSt", + "NxtFYEn", + "Sales", + "OP", + "OdP", + "NP", + "EPS", + "DEPS", + "TA", + "Eq", + "EqAR", + "BPS", + "CFO", + "CFI", + "CFF", + "CashEq", + "Div1Q", + "Div2Q", + "Div3Q", + "DivFY", + "DivAnn", + "DivUnit", + "DivTotalAnn", + "PayoutRatioAnn", + "FDiv1Q", + "FDiv2Q", + "FDiv3Q", + "FDivFY", + "FDivAnn", + "FDivUnit", + "FDivTotalAnn", + "FPayoutRatioAnn", + "NxFDiv1Q", + "NxFDiv2Q", + "NxFDiv3Q", + "NxFDivFY", + "NxFDivAnn", + "NxFDivUnit", + "NxFPayoutRatioAnn", + "FSales2Q", + "FOP2Q", + "FOdP2Q", + "FNP2Q", + "FEPS2Q", + "NxFSales2Q", + "NxFOP2Q", + "NxFOdP2Q", + "NxFNp2Q", + "NxFEPS2Q", + "FSales", + "FOP", + "FOdP", + "FNP", + "FEPS", + "NxFSales", + "NxFOP", + "NxFOdP", + "NxFNp", + "NxFEPS", + "MatChgSub", + "SigChgInC", + "ChgByASRev", + "ChgNoASRev", + "ChgAcEst", + "RetroRst", + "ShOutFY", + "TrShFY", + "AvgSh", + "NCSales", + "NCOP", + "NCOdP", + "NCNP", + "NCEPS", + "NCTA", + "NCEq", + "NCEqAR", + "NCBPS", + "FNCSales2Q", + "FNCOP2Q", + "FNCOdP2Q", + "FNCNP2Q", + "FNCEPS2Q", + "NxFNCSales2Q", + "NxFNCOP2Q", + "NxFNCOdP2Q", + "NxFNCNP2Q", + "NxFNCEPS2Q", + "FNCSales", + "FNCOP", + "FNCOdP", + "FNCNP", + "FNCEPS", + "NxFNCSales", + "NxFNCOP", + "NxFNCOdP", + "NxFNCNP", + "NxFNCEPS", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/fins/details +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/fins/details +FINS_FS_DETAILS_COLUMNS_V2 = [ + "DiscDate", + "DiscTime", + "Code", + "DiscNo", + "DocType", + "FS", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/fins/dividend +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/fins/dividend +FINS_DIVIDEND_COLUMNS_V2 = [ + "PubDate", + "PubTime", + "Code", + "RefNo", + "StatCode", + "BoardDate", + "IFCode", + "FRCode", + "IFTerm", + "DivRate", + "RecDate", + "ExDate", + "ActRecDate", + "PayDate", + "CARefNo", + "DistAmt", + "RetEarn", + "DeemDiv", + "DeemCapGains", + "NetAssetDecRatio", + "CommSpecCode", + "CommDivRate", + "SpecDivRate", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/equities/earnings-calendar +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/equities/earnings-calendar +FINS_ANNOUNCEMENT_COLUMNS_V2 = [ + "Date", + "Code", + "CoName", + "FY", + "SectorNm", + "FQ", + "Section", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/derivatives/bars/daily/futures +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/derivatives/bars/daily/futures +DERIVATIVES_FUTURES_COLUMNS_V2 = [ + "Code", + "ProdCat", + "Date", + "O", + "H", + "L", + "C", + "MO", + "MH", + "ML", + "MC", + "EO", + "EH", + "EL", + "EC", + "AO", + "AH", + "AL", + "AC", + "Vo", + "OI", + "Va", + "CM", + "VoOA", + "EmMrgnTrgDiv", + "LTD", + "SQD", + "Settle", + "CCMFlag", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/derivatives/bars/daily/options +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/derivatives/bars/daily/options +DERIVATIVES_OPTIONS_COLUMNS_V2 = [ + "Code", + "ProdCat", + "UndSSO", + "Date", + "O", + "H", + "L", + "C", + "MO", + "MH", + "ML", + "MC", + "EO", + "EH", + "EL", + "EC", + "AO", + "AH", + "AL", + "AC", + "Vo", + "OI", + "Va", + "CM", + "Strike", + "VoOA", + "EmMrgnTrgDiv", + "PCDiv", + "LTD", + "SQD", + "Settle", + "Theo", + "BaseVol", + "UnderPx", + "IV", + "IR", + "CCMFlag", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/markets/short-sale-report +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/markets/short-sale-report +SHORT_SELLING_POSITIONS_COLUMNS_V2 = [ + "DiscDate", + "CalcDate", + "Code", + "SSName", + "SSAddr", + "DICName", + "DICAddr", + "FundName", + "ShrtPosToSO", + "ShrtPosShares", + "ShrtPosUnits", + "PrevRptDate", + "PrevRptRatio", + "Notes", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/markets/margin-alert +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/markets/margin-alert +DAILY_MARGIN_INTEREST_COLUMNS_V2 = [ + "PubDate", + "Code", + "AppDate", + "PubReason", + "ShrtOut", + "ShrtOutChg", + "ShrtOutRatio", + "LongOut", + "LongOutChg", + "LongOutRatio", + "SLRatio", + "ShrtNegOut", + "ShrtNegOutChg", + "ShrtStdOut", + "ShrtStdOutChg", + "LongNegOut", + "LongNegOutChg", + "LongStdOut", + "LongStdOutChg", + "TSEMrgnRegCls", +] From 5df4a832b1577b58a86cb73137693bce3a558110 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Tue, 6 Jan 2026 08:55:46 +0900 Subject: [PATCH 03/21] Add minute and tick apis and deprecate V1 Client --- README.md | 6 +- jquantsapi/__init__.py | 2 +- jquantsapi/apis/v2/bulk.py | 92 ++++++++++++++++++ jquantsapi/apis/v2/equities.py | 74 +++++++++++++++ jquantsapi/client.py | 9 ++ jquantsapi/client_v2.py | 166 +++++++++++++++++++++++++++++++++ jquantsapi/constants.py | 127 ++++++++++++++++--------- jquantsapi/enums.py | 51 +++++++++- pyproject.toml | 1 + 9 files changed, 479 insertions(+), 49 deletions(-) create mode 100644 jquantsapi/apis/v2/bulk.py diff --git a/README.md b/README.md index 4250774..a01c5fb 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![PyPI version](https://badge.fury.io/py/jquants-api-client.svg)](https://badge.fury.io/py/jquants-api-client) -個人投資家向けデータ API 配信サービス「 [J-Quants API](https://jpx-jquants.com/#jquants-api) 」の Python クライアントライブラリです。 +個人投資家向けデータ API 配信サービス「 [J-Quants API](https://jpx-jquants.com/) 」の Python クライアントライブラリです。 J-Quants や API 仕様についての詳細を知りたい方は [公式ウェブサイト](https://jpx-jquants.com/) をご参照ください。 現在、J-Quants API は有償版サービスとして提供されています。 @@ -16,13 +16,13 @@ pip install jquants-api-client ### J-Quants API の利用 -To use J-Quants API, you need to "Applications for J-Quants API" from [J-Quants API Web site](https://jpx-jquants.com/?lang=en) and to select a plan. +To use J-Quants API, you need to "Applications for J-Quants API" from [J-Quants API Web site](https://jpx-jquants.com/) and to select a plan. J-Quants API を利用するためには[J-Quants API の Web サイト](https://jpx-jquants.com/) から「J-Quants API 申し込み」及び利用プランの選択が必要になります。 jquants-api-client-python を使用するためには「J-Quants API ログインページで使用するメールアドレスおよびパスワード」または「J-Quants API メニューページから取得したリフレッシュトークン」が必要になります。必要に応じて下記の Web サイトより取得してください。 -[J-Quants API ログインページ](https://jpx-jquants.com/auth/signin/) +[J-Quants API ログインページ](https://jpx-jquants.com/login) ### サンプルコード diff --git a/jquantsapi/__init__.py b/jquantsapi/__init__.py index c77d0ff..7b23914 100644 --- a/jquantsapi/__init__.py +++ b/jquantsapi/__init__.py @@ -3,4 +3,4 @@ from .client import Client from .client_v2 import ClientV2 -from .enums import MARKET_API_SECTIONS +from .enums import MARKET_API_SECTIONS, BulkEndpoint diff --git a/jquantsapi/apis/v2/bulk.py b/jquantsapi/apis/v2/bulk.py new file mode 100644 index 0000000..11dc209 --- /dev/null +++ b/jquantsapi/apis/v2/bulk.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +from typing import Any, Dict, Union + +import pandas as pd # type: ignore + +from jquantsapi import constants +from jquantsapi.apis.base import BaseApi, SupportsRequest +from jquantsapi.enums import BulkEndpoint + + +class BulkListApiV2(BaseApi): + """ + v2 の Bulk List API (`/bulk/list`) を担当するクラス。 + + 指定したエンドポイントで取得可能なデータ一覧を取得します。 + """ + + name = "bulk_list" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + endpoint: Union[str, BulkEndpoint], + ) -> pd.DataFrame: + """ + v2 `/bulk/list` を実行し、取得可能なデータ一覧を DataFrame で返す。 + + Args: + client: v2 `ClientV2` インスタンスを想定 + endpoint: 取得したいデータのエンドポイント (例: "/equities/master") + """ + url = f"{client.JQUANTS_API_BASE}/bulk/list" + + # BulkEndpointの場合はvalue(str)を取得 + endpoint_str = endpoint.value if isinstance(endpoint, BulkEndpoint) else endpoint + + params: Dict[str, Any] = {"endpoint": endpoint_str} + + resp = client._get(url, params) # type: ignore[arg-type] + payload = resp.json() + + data = payload.get("data", []) + cols = constants.BULK_LIST_COLUMNS_V2 + + if not data: + return pd.DataFrame(columns=cols) + + df = pd.DataFrame.from_records(data) + if "LastModified" in df.columns: + df["LastModified"] = pd.to_datetime(df["LastModified"], errors="coerce") + + return df[cols].reset_index(drop=True) + + +class BulkGetApiV2(BaseApi): + """ + v2 の Bulk Get API (`/bulk/get`) を担当するクラス。 + + 指定したキーのデータをダウンロードするためのURLを取得します。 + """ + + name = "bulk_get" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + key: str, + ) -> str: + """ + v2 `/bulk/get` を実行し、ダウンロードURLを取得する。 + + Args: + client: v2 `ClientV2` インスタンスを想定 + key: BulkListで取得したKey + + Returns: + str: ダウンロードURL + """ + url = f"{client.JQUANTS_API_BASE}/bulk/get" + + params: Dict[str, Any] = {"key": key} + + resp = client._get(url, params) # type: ignore[arg-type] + payload = resp.json() + + return payload.get("url", "") + diff --git a/jquantsapi/apis/v2/equities.py b/jquantsapi/apis/v2/equities.py index 1c70916..fe3df7c 100644 --- a/jquantsapi/apis/v2/equities.py +++ b/jquantsapi/apis/v2/equities.py @@ -175,6 +175,80 @@ def execute( return df[cols].reset_index(drop=True) +class EqBarsMinuteApiV2(BaseApi): + """ + v2 の分足 API (`/equities/bars/minute`) を担当するクラス。 + """ + + name = "eq_bars_minute" + version = "v2" + + def execute( + self, + client: SupportsRequest, + *, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + v2 `/equities/bars/minute` を実行し、分足を DataFrame で返す。 + + Args: + client: v2 `ClientV2` インスタンスを想定 + code: 銘柄コード (5桁 or 4桁) + from_yyyymmdd: 取得開始日 + to_yyyymmdd: 取得終了日 + date_yyyymmdd: 取得日 + """ + url = f"{client.JQUANTS_API_BASE}/equities/bars/minute" + + params: Dict[str, Any] = {} + if code: + params["code"] = code + if date_yyyymmdd: + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd: + params["from"] = from_yyyymmdd + if to_yyyymmdd: + params["to"] = to_yyyymmdd + + all_data: List[Dict[str, Any]] = [] + pagination_key = "" + + while True: + req_params = dict(params) + if pagination_key: + req_params["pagination_key"] = pagination_key + + resp = client._get(url, req_params) # type: ignore[arg-type] + payload = resp.json() + + batch = payload.get("data", []) + if isinstance(batch, list): + all_data.extend(batch) + + pagination_key = payload.get("pagination_key", "") + if not pagination_key: + break + + cols = constants.EQ_BARS_MINUTE_COLUMNS_V2 + if not all_data: + return pd.DataFrame(columns=cols) + + df = pd.DataFrame.from_records(all_data) + if "Date" in df.columns: + df["Date"] = pd.to_datetime(df["Date"], errors="coerce") + + sort_cols = [c for c in ["Code", "Date", "Time"] if c in df.columns] + if sort_cols: + df.sort_values(sort_cols, inplace=True) + + return df[cols].reset_index(drop=True) + + class EqInvestorTypesApiV2(BaseApi): """ v2 の投資部門別売買状況 API (`/equities/investor-types`) を担当するクラス。 diff --git a/jquantsapi/client.py b/jquantsapi/client.py index 9ee4242..6bb4776 100644 --- a/jquantsapi/client.py +++ b/jquantsapi/client.py @@ -49,6 +49,11 @@ else: import tomli as tomllib +if sys.version_info >= (3, 13): + from warnings import deprecated +else: + from typing_extensions import deprecated + DatetimeLike = Union[datetime, pd.Timestamp, str] _Data = Union[str, Mapping[str, Any]] @@ -58,10 +63,14 @@ class TokenAuthRefreshBadRequestException(Exception): pass +@deprecated("Client (V1) is deprecated and will be removed in a future version. Please use ClientV2 instead.") class Client: """ J-Quants API からデータを取得する ref. https://jpx.gitbook.io/j-quants-ja/ + + .. deprecated:: + This class is deprecated. Use :class:`ClientV2` instead. """ JQUANTS_API_BASE = "https://api.jquants.com/v1" diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py index aac05d3..d29743f 100644 --- a/jquantsapi/client_v2.py +++ b/jquantsapi/client_v2.py @@ -13,6 +13,7 @@ from jquantsapi.apis.v2.equities import ( EqBarsDailyAmApiV2, EqBarsDailyApiV2, + EqBarsMinuteApiV2, EqEarningsCalApiV2, EqInvestorTypesApiV2, EqMasterApiV2, @@ -32,6 +33,8 @@ DrvBarsDailyOptApiV2, DrvBarsDailyOpt225ApiV2, ) +from jquantsapi.apis.v2.bulk import BulkGetApiV2, BulkListApiV2 +from jquantsapi.enums import BulkEndpoint DatetimeLike = Union[datetime, pd.Timestamp, str] @@ -74,6 +77,7 @@ def __init__(self, api_key: Optional[str] = None) -> None: self._eq_master_api = EqMasterApiV2() self._eq_bars_daily_api = EqBarsDailyApiV2() self._eq_bars_daily_am_api = EqBarsDailyAmApiV2() + self._eq_bars_minute_api = EqBarsMinuteApiV2() self._eq_investor_types_api = EqInvestorTypesApiV2() self._fin_summary_api = FinSummaryApiV2() self._fin_details_api = FinDetailsApiV2() @@ -90,6 +94,8 @@ def __init__(self, api_key: Optional[str] = None) -> None: self._drv_bars_daily_fut_api = DrvBarsDailyFutApiV2() self._drv_bars_daily_opt_api = DrvBarsDailyOptApiV2() self._drv_bars_daily_opt_225_api = DrvBarsDailyOpt225ApiV2() + self._bulk_list_api = BulkListApiV2() + self._bulk_get_api = BulkGetApiV2() # ------------------------------------------------------------------ # 内部ユーティリティ @@ -336,6 +342,138 @@ def get_eq_bars_daily_am(self, code: str = "") -> pd.DataFrame: """ return self._eq_bars_daily_am_api.execute(self, code=code) + # ------------------------------------------------------------------ + # eq-bars-minute (/equities/bars/minute) + # ------------------------------------------------------------------ + def get_eq_bars_minute( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + eq-bars-minute: 分足 (v2: /equities/bars/minute) + + Args: + code: 銘柄コード (5桁 or 4桁) + from_yyyymmdd: 期間開始日 (YYYYMMDD or YYYY-MM-DD) + to_yyyymmdd: 期間終了日 (YYYYMMDD or YYYY-MM-DD) + date_yyyymmdd: 特定日付 (YYYYMMDD or YYYY-MM-DD) + Returns: + pd.DataFrame: 1分足データ (v2のフィールド名で返却) + """ + return self._eq_bars_minute_api.execute( + self, + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + + def _aggregate_bars_n_minute( + self, + df: pd.DataFrame, + n: int = 5, + ) -> pd.DataFrame: + """ + 1分足データをn分足に集約する (private) + + Args: + df: 1分足データ (Date, Time, Code, O, H, L, C, Vo, Va を含む) + n: 集約する分数 (デフォルト: 5) + Returns: + pd.DataFrame: n分足データ + """ + if df.empty: + return df.copy() + + df = df.copy() + + # DateTime列を作成 (Date + Time) + df["DateTime"] = pd.to_datetime( + df["Date"].astype(str) + " " + df["Time"].astype(str), + errors="coerce", + ) + + # n分間隔でグループ化するためのキーを作成 + df["TimeGroup"] = df["DateTime"].dt.floor(f"{n}min") + + # 銘柄ごと・n分間隔ごとに集約 + agg_funcs = { + "Date": "first", + "Time": "first", + "Code": "first", + "O": "first", # 始値: 最初の値 + "H": "max", # 高値: 最大値 + "L": "min", # 安値: 最小値 + "C": "last", # 終値: 最後の値 + "Vo": "sum", # 出来高: 合計 + "Va": "sum", # 売買代金: 合計 + } + + result = ( + df.groupby(["Code", "TimeGroup"], as_index=False) + .agg(agg_funcs) + .drop(columns=["TimeGroup"]) + ) + + # ソートして返却 + result.sort_values(["Code", "Date", "Time"], inplace=True) + return result.reset_index(drop=True) + + def get_eq_bars_5minute( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + eq-bars-minute から5分足データを算出して取得 + + Args: + code: 銘柄コード (5桁 or 4桁) + from_yyyymmdd: 期間開始日 (YYYYMMDD or YYYY-MM-DD) + to_yyyymmdd: 期間終了日 (YYYYMMDD or YYYY-MM-DD) + date_yyyymmdd: 特定日付 (YYYYMMDD or YYYY-MM-DD) + Returns: + pd.DataFrame: 5分足データ + """ + df_1min = self.get_eq_bars_minute( + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + return self._aggregate_bars_n_minute(df_1min, n=5) + + def get_eq_bars_15minute( + self, + code: str = "", + from_yyyymmdd: str = "", + to_yyyymmdd: str = "", + date_yyyymmdd: str = "", + ) -> pd.DataFrame: + """ + eq-bars-minute から15分足データを算出して取得 + + Args: + code: 銘柄コード (5桁 or 4桁) + from_yyyymmdd: 期間開始日 (YYYYMMDD or YYYY-MM-DD) + to_yyyymmdd: 期間終了日 (YYYYMMDD or YYYY-MM-DD) + date_yyyymmdd: 特定日付 (YYYYMMDD or YYYY-MM-DD) + Returns: + pd.DataFrame: 15分足データ + """ + df_1min = self.get_eq_bars_minute( + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + return self._aggregate_bars_n_minute(df_1min, n=15) + # ------------------------------------------------------------------ # eq-investor-types (/equities/investor-types) # ------------------------------------------------------------------ @@ -1064,4 +1202,32 @@ def get_drv_bars_daily_opt_225_range( return pd.DataFrame() return pd.concat(buff).sort_values(["Code", "Date"]).reset_index(drop=True) + # ------------------------------------------------------------------ + # Bulk API + # ------------------------------------------------------------------ + def get_bulk_list( + self, + endpoint: Union[str, BulkEndpoint], + ) -> pd.DataFrame: + """ + bulk-list: 取得可能なデータ一覧 (v2: /bulk/list) + + Args: + endpoint: 取得したいデータのエンドポイント + (例: BulkEndpoint.EQ_MASTER または "/equities/master") + Returns: + pd.DataFrame: データ一覧 (Key, Size, LastModified) + """ + return self._bulk_list_api.execute(self, endpoint=endpoint) + + def get_bulk(self, key: str) -> str: + """ + bulk-get: データダウンロードURL取得 (v2: /bulk/get) + + Args: + key: get_bulk_listで取得したKey + Returns: + str: ダウンロードURL + """ + return self._bulk_get_api.execute(self, key=key) diff --git a/jquantsapi/constants.py b/jquantsapi/constants.py index d5925b0..eec7071 100644 --- a/jquantsapi/constants.py +++ b/jquantsapi/constants.py @@ -2,8 +2,8 @@ # 共通データ(V1/V2両方で使用) # ============================================================================ -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/sector17code -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/sector17code +# ref. ja https://jpx-jquants.com/ja/spec/eq-master/sector17code +# ref. en https://jpx-jquants.com/en/spec/eq-master/sector17code SECTOR_17_COLUMNS = ["Sector17Code", "Sector17CodeName", "Sector17CodeNameEnglish"] SECTOR_17_DATA = [ ("1", "食品", "FOODS"), @@ -26,8 +26,8 @@ ("99", "その他", "OTHER"), ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/sector33code -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/sector33code +# ref. ja https://jpx-jquants.com/ja/spec/eq-master/sector33code +# ref. en https://jpx-jquants.com/en/spec/eq-master/sector33code # ref. 33-17 mapping https://www.jpx.co.jp/markets/indices/line-up/files/fac_13_sector.pdf SECTOR_33_COLUMNS = [ "Sector33Code", @@ -72,8 +72,8 @@ ("9999", "その他", "Other", "99"), ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/marketcode -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/marketcode +# ref. ja https://jpx-jquants.com/ja/spec/eq-master/marketcode +# ref. en https://jpx-jquants.com/en/spec/eq-master/marketcode MARKET_SEGMENT_COLUMNS = [ "MarketCode", "MarketCodeName", @@ -112,7 +112,6 @@ "MarketCode", "MarketCodeName", ] - LISTED_INFO_STANDARD_PREMIUM_COLUMNS = [ "Date", "Code", @@ -150,6 +149,8 @@ "AdjustmentVolume", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/daily_quotes +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/daily_quotes PRICES_DAILY_QUOTES_PREMIUM_COLUMNS = [ "Date", "Code", @@ -195,6 +196,8 @@ "AfternoonAdjustmentVolume", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/prices_am +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/prices_am PRICES_PRICES_AM_COLUMNS = [ "Date", "Code", @@ -312,6 +315,8 @@ "ShortSellingWithoutRestrictionsTurnoverValue", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/breakdown +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/breakdown MARKETS_BREAKDOWN_COLUMNS = [ "Date", "Code", @@ -331,13 +336,15 @@ "MarginBuyCloseVolume", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/trading_calendar +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/trading_calendar MARKETS_TRADING_CALENDAR = [ "Date", "HolidayDivision", ] -# ref ja https://jpx.gitbook.io/j-quants-ja/api-reference/statements -# ref en https://jpx.gitbook.io/j-quants-en/api-reference/statements +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/statements +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/statements FINS_STATEMENTS_COLUMNS = [ "DisclosedDate", "DisclosedTime", @@ -448,6 +455,8 @@ "NextYearForecastNonConsolidatedEarningsPerShare", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/statements-1 +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/statements-1 FINS_FS_DETAILS_COLUMNS = [ "DisclosedDate", "DisclosedTime", @@ -456,6 +465,8 @@ "TypeOfDocument", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/dividend +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/dividend FINS_DIVIDEND_COLUMNS = [ "AnnouncementDate", "AnnouncementTime", @@ -529,6 +540,8 @@ "InterestRate", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/futures +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/futures DERIVATIVES_FUTURES_COLUMNS = [ "Date", "Code", @@ -561,6 +574,8 @@ "CentralContractMonthFlag", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/options +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/options DERIVATIVES_OPTIONS_COLUMNS = [ "Date", "Code", @@ -601,6 +616,8 @@ "CentralContractMonthFlag", ] +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/short_selling_positions +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/short_selling_positions SHORT_SELLING_POSITIONS_COLUMNS = [ "DisclosedDate", "CalculatedDate", @@ -653,8 +670,8 @@ # V2 API用カラム定義 # ============================================================================ -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/eq-master -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/eq-master +# ref. ja https://jpx-jquants.com/ja/spec/eq-master +# ref. en https://jpx-jquants.com/en/spec/eq-master EQ_MASTER_COLUMNS_V2 = [ "Date", "Code", @@ -671,8 +688,8 @@ "MrgnNm", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/eq-bars-daily -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/eq-bars-daily +# ref. ja https://jpx-jquants.com/ja/spec/eq-bars-daily +# ref. en https://jpx-jquants.com/en/spec/eq-bars-daily EQ_BARS_DAILY_COLUMNS_V2 = [ "Date", "Code", @@ -718,8 +735,22 @@ "AAdjVo", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/eq-bars-daily -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/eq-bars-daily +# ref. ja https://jpx-jquants.com/ja/spec/eq-bars-minute +# ref. en https://jpx-jquants.com/en/spec/eq-bars-minute +EQ_BARS_MINUTE_COLUMNS_V2 = [ + "Date", + "Time", + "Code", + "O", + "H", + "L", + "C", + "Vo", + "Va", +] + +# ref. ja https://jpx-jquants.com/ja/spec/eq-bars-daily-am +# ref. en https://jpx-jquants.com/en/spec/eq-bars-daily-am PRICES_PRICES_AM_COLUMNS_V2 = [ "Date", "Code", @@ -731,8 +762,8 @@ "MVa", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/indices-bars-daily -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/indices-bars-daily +# ref. ja https://jpx-jquants.com/ja/spec/idx-bars-daily +# ref. en https://jpx-jquants.com/en/spec/idx-bars-daily INDICES_COLUMNS_V2 = [ "Date", "Code", @@ -742,6 +773,8 @@ "C", ] +# ref. ja https://jpx-jquants.com/ja/spec/idx-bars-daily-topix +# ref. en https://jpx-jquants.com/en/spec/idx-bars-daily-topix INDICES_TOPIX_COLUMNS_V2 = [ "Date", "O", @@ -750,8 +783,8 @@ "C", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/eq-investor-types -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/eq-investor-types +# ref. ja https://jpx-jquants.com/ja/spec/eq-investor-types +# ref. en https://jpx-jquants.com/en/spec/eq-investor-types EQ_INVESTOR_TYPES_COLUMNS_V2 = [ "PubDate", "StDate", @@ -811,8 +844,8 @@ "OthFinBal", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/markets/margin-interest -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/markets/margin-interest +# ref. ja https://jpx-jquants.com/ja/spec/mkt-margin-int +# ref. en https://jpx-jquants.com/en/spec/mkt-margin-int MARKETS_WEEKLY_MARGIN_INTEREST_COLUMNS_V2 = [ "Date", "Code", @@ -825,8 +858,8 @@ "IssType", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/mkt-short-ratio -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/mkt-short-ratio +# ref. ja https://jpx-jquants.com/ja/spec/mkt-short-ratio +# ref. en https://jpx-jquants.com/en/spec/mkt-short-ratio MKT_SHORT_RATIO_COLUMNS_V2 = [ "Date", "S33", @@ -835,8 +868,8 @@ "ShrtNoResVa", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/mkt-breakdown -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/mkt-breakdown +# ref. ja https://jpx-jquants.com/ja/spec/mkt-breakdown +# ref. en https://jpx-jquants.com/en/spec/mkt-breakdown MKT_BREAKDOWN_COLUMNS_V2 = [ "Date", "Code", @@ -856,15 +889,15 @@ "MrgnBuyCloseVo", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/markets/calendar -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/markets/calendar +# ref. ja https://jpx-jquants.com/ja/spec/mkt-cal +# ref. en https://jpx-jquants.com/en/spec/mkt-cal MARKETS_TRADING_CALENDAR_COLUMNS_V2 = [ "Date", "HolDiv", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/fin-summary -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/fin-summary +# ref. ja https://jpx-jquants.com/ja/spec/fin-summary +# ref. en https://jpx-jquants.com/en/spec/fin-summary FIN_SUMMARY_COLUMNS_V2 = [ "DiscDate", "DiscTime", @@ -975,8 +1008,8 @@ "NxFNCEPS", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/fins/details -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/fins/details +# ref. ja https://jpx-jquants.com/ja/spec/fin-details +# ref. en https://jpx-jquants.com/en/spec/fin-details FINS_FS_DETAILS_COLUMNS_V2 = [ "DiscDate", "DiscTime", @@ -986,8 +1019,8 @@ "FS", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/fins/dividend -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/fins/dividend +# ref. ja https://jpx-jquants.com/ja/spec/fin-dividend +# ref. en https://jpx-jquants.com/en/spec/fin-dividend FINS_DIVIDEND_COLUMNS_V2 = [ "PubDate", "PubTime", @@ -1014,8 +1047,8 @@ "SpecDivRate", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/equities/earnings-calendar -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/equities/earnings-calendar +# ref. ja https://jpx-jquants.com/ja/spec/eq-earnings-cal +# ref. en https://jpx-jquants.com/en/spec/eq-earnings-cal FINS_ANNOUNCEMENT_COLUMNS_V2 = [ "Date", "Code", @@ -1026,8 +1059,8 @@ "Section", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/derivatives/bars/daily/futures -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/derivatives/bars/daily/futures +# ref. ja https://jpx-jquants.com/ja/spec/drv-bars-daily-fut +# ref. en https://jpx-jquants.com/en/spec/drv-bars-daily-fut DERIVATIVES_FUTURES_COLUMNS_V2 = [ "Code", "ProdCat", @@ -1060,8 +1093,8 @@ "CCMFlag", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/derivatives/bars/daily/options -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/derivatives/bars/daily/options +# ref. ja https://jpx-jquants.com/ja/spec/drv-bars-daily-opt +# ref. en https://jpx-jquants.com/en/spec/drv-bars-daily-opt DERIVATIVES_OPTIONS_COLUMNS_V2 = [ "Code", "ProdCat", @@ -1102,8 +1135,8 @@ "CCMFlag", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/markets/short-sale-report -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/markets/short-sale-report +# ref. ja https://jpx-jquants.com/ja/spec/mkt-short-sale +# ref. en https://jpx-jquants.com/en/spec/mkt-short-sale SHORT_SELLING_POSITIONS_COLUMNS_V2 = [ "DiscDate", "CalcDate", @@ -1121,8 +1154,8 @@ "Notes", ] -# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/markets/margin-alert -# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/markets/margin-alert +# ref. ja https://jpx-jquants.com/ja/spec/mkt-margin-alert +# ref. en https://jpx-jquants.com/en/spec/mkt-margin-alert DAILY_MARGIN_INTEREST_COLUMNS_V2 = [ "PubDate", "Code", @@ -1145,3 +1178,11 @@ "LongStdOutChg", "TSEMrgnRegCls", ] + +# ref. ja https://jpx-jquants.com/ja/spec/bulk-list +# ref. en https://jpx-jquants.com/en/spec/bulk-list +BULK_LIST_COLUMNS_V2 = [ + "Key", + "Size", + "LastModified", +] diff --git a/jquantsapi/enums.py b/jquantsapi/enums.py index 50a3307..1fada35 100644 --- a/jquantsapi/enums.py +++ b/jquantsapi/enums.py @@ -5,8 +5,8 @@ class MARKET_API_SECTIONS(str, Enum): """ values of sections for market api - ref. (en) https://jpx.gitbook.io/j-quants-api-en/api-reference/markets-api/section-code - ref. (ja) https://jpx.gitbook.io/j-quants-api/api-reference/market-api/sector_name + ref. (ja) https://jpx-jquants.com/ja/spec/eq-investor-types/section + ref. (en) https://jpx-jquants.com/en/spec/eq-investor-types/section """ TSE1st = "TSE1st" @@ -17,3 +17,50 @@ class MARKET_API_SECTIONS(str, Enum): TSEStandard = "TSEStandard" TSEGrowth = "TSEGrowth" TokyoNagoya = "TokyoNagoya" + + +class BulkEndpoint(str, Enum): + """ + Bulk APIで指定可能なエンドポイント + """ + + # 上場銘柄一覧API + EQ_MASTER = "/equities/master" + # 株価四本値API + EQ_BARS_DAILY = "/equities/bars/daily" + # 分足API + EQ_BARS_MINUTE = "/equities/bars/minute" + # 投資部門別売買状況API + EQ_INVESTOR_TYPES = "/equities/investor-types" + # TickデータAPI + EQ_TRADES = "/equities/trades" + + # 財務情報API + FIN_SUMMARY = "/fins/summary" + # 財務諸表API + FIN_DETAILS = "/fins/details" + # 配当金情報API + FIN_DIVIDEND = "/fins/dividend" + + # 業種別空売り比率API + MKT_SHORT_RATIO = "/markets/short-ratio" + # 空売り残高報告API + MKT_SHORT_SALE_REPORT = "/markets/short-sale-report" + # 信用取引週末残高API + MKT_MARGIN_INTEREST = "/markets/margin-interest" + # 日々公表信用取引残高API + MKT_MARGIN_ALERT = "/markets/margin-alert" + # 売買内訳データAPI + MKT_BREAKDOWN = "/markets/breakdown" + + # 指数四本値API + IDX_BARS_DAILY = "/indices/bars/daily" + # TOPIX指数四本値API + IDX_BARS_DAILY_TOPIX = "/indices/bars/daily/topix" + + # 先物四本値API + DRV_BARS_DAILY_FUT = "/derivatives/bars/daily/futures" + # オプション四本値API + DRV_BARS_DAILY_OPT = "/derivatives/bars/daily/options" + # 日経225オプション四本値API + DRV_BARS_DAILY_OPT_225 = "/derivatives/bars/daily/options/225" diff --git a/pyproject.toml b/pyproject.toml index a1cd032..49956e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,7 @@ numpy = [ { version = "^1.26.0", python = ">=3.12" } ] tomli = { version = "^2.0.1", python = ">=3.8,<3.11" } +typing_extensions = { version = "^4.5.0", python = "<3.13" } tenacity = "^8.0.1" [tool.poetry.group.dev.dependencies] From c3eeb601da1cb2ce460ce4922b5938da5df7ca56 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Tue, 6 Jan 2026 09:09:31 +0900 Subject: [PATCH 04/21] Resolve dependencies --- poetry.lock | 57 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/poetry.lock b/poetry.lock index 3960eeb..4ab2d92 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand. [[package]] name = "black" @@ -6,6 +6,7 @@ version = "24.3.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, @@ -42,7 +43,7 @@ typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] +d = ["aiohttp (>=3.7.4) ; sys_platform != \"win32\" or implementation_name != \"pypy\"", "aiohttp (>=3.7.4,!=3.9.0) ; sys_platform == \"win32\" and implementation_name == \"pypy\""] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] uvloop = ["uvloop (>=0.15.2)"] @@ -52,6 +53,7 @@ version = "2024.2.2" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, @@ -63,6 +65,7 @@ version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" +groups = ["main"] files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, @@ -162,6 +165,7 @@ version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, @@ -176,6 +180,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -187,6 +193,7 @@ version = "7.4.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "coverage-7.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0be5efd5127542ef31f165de269f77560d6cdef525fffa446de6f7e9186cfb2"}, {file = "coverage-7.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ccd341521be3d1b3daeb41960ae94a5e87abe2f46f17224ba5d6f2b8398016cf"}, @@ -246,7 +253,7 @@ files = [ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} [package.extras] -toml = ["tomli"] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] [[package]] name = "exceptiongroup" @@ -254,6 +261,8 @@ version = "1.2.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version <= \"3.10\"" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, @@ -268,6 +277,7 @@ version = "5.0.4" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.6.1" +groups = ["dev"] files = [ {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, @@ -284,6 +294,7 @@ version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.5" +groups = ["main"] files = [ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, @@ -295,6 +306,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -306,6 +318,7 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -320,6 +333,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -331,6 +345,7 @@ version = "1.9.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, @@ -378,6 +393,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, @@ -389,6 +405,8 @@ version = "1.24.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.8" +groups = ["main"] +markers = "python_version < \"3.10\"" files = [ {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, @@ -426,6 +444,8 @@ version = "1.26.4" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" +groups = ["main"] +markers = "python_version >= \"3.10\"" files = [ {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, @@ -471,6 +491,7 @@ version = "24.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, @@ -482,6 +503,7 @@ version = "2.0.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, @@ -513,8 +535,8 @@ files = [ [package.dependencies] numpy = [ {version = ">=1.20.3", markers = "python_version < \"3.10\""}, - {version = ">=1.21.0", markers = "python_version >= \"3.10\" and python_version < \"3.11\""}, {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, + {version = ">=1.21.0", markers = "python_version == \"3.10\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -549,6 +571,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -560,6 +583,7 @@ version = "4.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, @@ -575,6 +599,7 @@ version = "1.4.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, @@ -590,6 +615,7 @@ version = "2.9.1" description = "Python style guide checker" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, @@ -601,6 +627,7 @@ version = "2.5.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, @@ -612,6 +639,7 @@ version = "5.0.4.post1" description = "pyproject-flake8 (`pflake8`), a monkey patching wrapper to connect flake8 with pyproject.toml configuration" optional = false python-versions = "*" +groups = ["dev"] files = [ {file = "pyproject-flake8-5.0.4.post1.tar.gz", hash = "sha256:c2dfdf1064f47efbb2e4faf1a32b0b6a6ea67dc4d1debb98d862b0cdee377941"}, {file = "pyproject_flake8-5.0.4.post1-py2.py3-none-any.whl", hash = "sha256:457e52dde1b7a1f84b5230c70d61afa58ced64a44b81a609f19e972319fa68ed"}, @@ -627,6 +655,7 @@ version = "8.1.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, @@ -649,6 +678,7 @@ version = "5.0.0" description = "Pytest plugin for measuring coverage." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, @@ -667,6 +697,7 @@ version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -681,6 +712,7 @@ version = "2024.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, @@ -692,6 +724,7 @@ version = "2.31.0" description = "Python HTTP for Humans." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, @@ -713,6 +746,7 @@ version = "1.16.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["main"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -724,6 +758,7 @@ version = "8.2.3" description = "Retry code until it succeeds" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, @@ -738,6 +773,8 @@ version = "2.0.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.7" +groups = ["main", "dev"] +markers = "python_version <= \"3.10\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -749,6 +786,7 @@ version = "2.9.0.20240316" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, @@ -760,6 +798,7 @@ version = "2.31.0.20240406" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, @@ -774,10 +813,12 @@ version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] +markers = {main = "python_version <= \"3.12\""} [[package]] name = "tzdata" @@ -785,6 +826,7 @@ version = "2024.1" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" +groups = ["main"] files = [ {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, @@ -796,18 +838,19 @@ version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.8.0" -content-hash = "99905c2ff019460a1f3198d9d05288b09a6c1a63f10344e7a1061cc551f94386" +content-hash = "a351df77926baa8336857a1948a41df2c175ef8b025e00103e8eb9d3f120b6c7" From fd541013d12f72eb436785124c86a25461d7b711 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Tue, 6 Jan 2026 13:10:11 +0900 Subject: [PATCH 05/21] Update libraries --- poetry.lock | 218 +++++++++++++++++++++++++++++++------------------ pyproject.toml | 19 ++--- 2 files changed, 148 insertions(+), 89 deletions(-) diff --git a/poetry.lock b/poetry.lock index 4ab2d92..989af1f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -262,7 +262,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version <= \"3.10\"" +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, @@ -401,88 +401,151 @@ files = [ [[package]] name = "numpy" -version = "1.24.4" +version = "2.2.6" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["main"] -markers = "python_version < \"3.10\"" -files = [ - {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, - {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, - {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, - {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, - {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, - {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, - {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, - {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, - {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, - {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, - {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, - {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, - {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, - {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, - {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, - {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, - {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, - {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, - {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, - {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, - {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, - {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, - {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, - {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, - {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, - {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, - {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, - {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +markers = "python_version == \"3.10\"" +files = [ + {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"}, + {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"}, + {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"}, + {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"}, + {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"}, + {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"}, + {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"}, + {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"}, + {file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"}, + {file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"}, + {file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"}, + {file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"}, + {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"}, + {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"}, + {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"}, + {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"}, + {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"}, + {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"}, + {file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"}, + {file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"}, + {file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"}, + {file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"}, + {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"}, + {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"}, + {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"}, + {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"}, + {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"}, + {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"}, + {file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"}, + {file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"}, + {file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"}, + {file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"}, + {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"}, + {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"}, + {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"}, + {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"}, + {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"}, + {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"}, + {file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"}, + {file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"}, + {file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"}, + {file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"}, + {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"}, + {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"}, + {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"}, + {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"}, + {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"}, + {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"}, + {file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"}, + {file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"}, + {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"}, + {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"}, + {file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"}, + {file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"}, + {file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"}, ] [[package]] name = "numpy" -version = "1.26.4" +version = "2.4.0" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.11" groups = ["main"] -markers = "python_version >= \"3.10\"" -files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, +markers = "python_version >= \"3.11\"" +files = [ + {file = "numpy-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:316b2f2584682318539f0bcaca5a496ce9ca78c88066579ebd11fd06f8e4741e"}, + {file = "numpy-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2718c1de8504121714234b6f8241d0019450353276c88b9453c9c3d92e101db"}, + {file = "numpy-2.4.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:21555da4ec4a0c942520ead42c3b0dc9477441e085c42b0fbdd6a084869a6f6b"}, + {file = "numpy-2.4.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:413aa561266a4be2d06cd2b9665e89d9f54c543f418773076a76adcf2af08bc7"}, + {file = "numpy-2.4.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0feafc9e03128074689183031181fac0897ff169692d8492066e949041096548"}, + {file = "numpy-2.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8fdfed3deaf1928fb7667d96e0567cdf58c2b370ea2ee7e586aa383ec2cb346"}, + {file = "numpy-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e06a922a469cae9a57100864caf4f8a97a1026513793969f8ba5b63137a35d25"}, + {file = "numpy-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:927ccf5cd17c48f801f4ed43a7e5673a2724bd2171460be3e3894e6e332ef83a"}, + {file = "numpy-2.4.0-cp311-cp311-win32.whl", hash = "sha256:882567b7ae57c1b1a0250208cc21a7976d8cbcc49d5a322e607e6f09c9e0bd53"}, + {file = "numpy-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:8b986403023c8f3bf8f487c2e6186afda156174d31c175f747d8934dfddf3479"}, + {file = "numpy-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:3f3096405acc48887458bbf9f6814d43785ac7ba2a57ea6442b581dedbc60ce6"}, + {file = "numpy-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2a8b6bb8369abefb8bd1801b054ad50e02b3275c8614dc6e5b0373c305291037"}, + {file = "numpy-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2e284ca13d5a8367e43734148622caf0b261b275673823593e3e3634a6490f83"}, + {file = "numpy-2.4.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:49ff32b09f5aa0cd30a20c2b39db3e669c845589f2b7fc910365210887e39344"}, + {file = "numpy-2.4.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:36cbfb13c152b1c7c184ddac43765db8ad672567e7bafff2cc755a09917ed2e6"}, + {file = "numpy-2.4.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:35ddc8f4914466e6fc954c76527aa91aa763682a4f6d73249ef20b418fe6effb"}, + {file = "numpy-2.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc578891de1db95b2a35001b695451767b580bb45753717498213c5ff3c41d63"}, + {file = "numpy-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98e81648e0b36e325ab67e46b5400a7a6d4a22b8a7c8e8bbfe20e7db7906bf95"}, + {file = "numpy-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d57b5046c120561ba8fa8e4030fbb8b822f3063910fa901ffadf16e2b7128ad6"}, + {file = "numpy-2.4.0-cp312-cp312-win32.whl", hash = "sha256:92190db305a6f48734d3982f2c60fa30d6b5ee9bff10f2887b930d7b40119f4c"}, + {file = "numpy-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:680060061adb2d74ce352628cb798cfdec399068aa7f07ba9fb818b2b3305f98"}, + {file = "numpy-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:39699233bc72dd482da1415dcb06076e32f60eddc796a796c5fb6c5efce94667"}, + {file = "numpy-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a152d86a3ae00ba5f47b3acf3b827509fd0b6cb7d3259665e63dafbad22a75ea"}, + {file = "numpy-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:39b19251dec4de8ff8496cd0806cbe27bf0684f765abb1f4809554de93785f2d"}, + {file = "numpy-2.4.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:009bd0ea12d3c784b6639a8457537016ce5172109e585338e11334f6a7bb88ee"}, + {file = "numpy-2.4.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5fe44e277225fd3dff6882d86d3d447205d43532c3627313d17e754fb3905a0e"}, + {file = "numpy-2.4.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f935c4493eda9069851058fa0d9e39dbf6286be690066509305e52912714dbb2"}, + {file = "numpy-2.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8cfa5f29a695cb7438965e6c3e8d06e0416060cf0d709c1b1c1653a939bf5c2a"}, + {file = "numpy-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ba0cb30acd3ef11c94dc27fbfba68940652492bc107075e7ffe23057f9425681"}, + {file = "numpy-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:60e8c196cd82cbbd4f130b5290007e13e6de3eca79f0d4d38014769d96a7c475"}, + {file = "numpy-2.4.0-cp313-cp313-win32.whl", hash = "sha256:5f48cb3e88fbc294dc90e215d86fbaf1c852c63dbdb6c3a3e63f45c4b57f7344"}, + {file = "numpy-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:a899699294f28f7be8992853c0c60741f16ff199205e2e6cdca155762cbaa59d"}, + {file = "numpy-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:9198f447e1dc5647d07c9a6bbe2063cc0132728cc7175b39dbc796da5b54920d"}, + {file = "numpy-2.4.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74623f2ab5cc3f7c886add4f735d1031a1d2be4a4ae63c0546cfd74e7a31ddf6"}, + {file = "numpy-2.4.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:0804a8e4ab070d1d35496e65ffd3cf8114c136a2b81f61dfab0de4b218aacfd5"}, + {file = "numpy-2.4.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:02a2038eb27f9443a8b266a66911e926566b5a6ffd1a689b588f7f35b81e7dc3"}, + {file = "numpy-2.4.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1889b3a3f47a7b5bee16bc25a2145bd7cb91897f815ce3499db64c7458b6d91d"}, + {file = "numpy-2.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85eef4cb5625c47ee6425c58a3502555e10f45ee973da878ac8248ad58c136f3"}, + {file = "numpy-2.4.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6dc8b7e2f4eb184b37655195f421836cfae6f58197b67e3ffc501f1333d993fa"}, + {file = "numpy-2.4.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:44aba2f0cafd287871a495fb3163408b0bd25bbce135c6f621534a07f4f7875c"}, + {file = "numpy-2.4.0-cp313-cp313t-win32.whl", hash = "sha256:20c115517513831860c573996e395707aa9fb691eb179200125c250e895fcd93"}, + {file = "numpy-2.4.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b48e35f4ab6f6a7597c46e301126ceba4c44cd3280e3750f85db48b082624fa4"}, + {file = "numpy-2.4.0-cp313-cp313t-win_arm64.whl", hash = "sha256:4d1cfce39e511069b11e67cd0bd78ceff31443b7c9e5c04db73c7a19f572967c"}, + {file = "numpy-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c95eb6db2884917d86cde0b4d4cf31adf485c8ec36bf8696dd66fa70de96f36b"}, + {file = "numpy-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:65167da969cd1ec3a1df31cb221ca3a19a8aaa25370ecb17d428415e93c1935e"}, + {file = "numpy-2.4.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3de19cfecd1465d0dcf8a5b5ea8b3155b42ed0b639dba4b71e323d74f2a3be5e"}, + {file = "numpy-2.4.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6c05483c3136ac4c91b4e81903cb53a8707d316f488124d0398499a4f8e8ef51"}, + {file = "numpy-2.4.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36667db4d6c1cea79c8930ab72fadfb4060feb4bfe724141cd4bd064d2e5f8ce"}, + {file = "numpy-2.4.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a818668b674047fd88c4cddada7ab8f1c298812783e8328e956b78dc4807f9f"}, + {file = "numpy-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1ee32359fb7543b7b7bd0b2f46294db27e29e7bbdf70541e81b190836cd83ded"}, + {file = "numpy-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e493962256a38f58283de033d8af176c5c91c084ea30f15834f7545451c42059"}, + {file = "numpy-2.4.0-cp314-cp314-win32.whl", hash = "sha256:6bbaebf0d11567fa8926215ae731e1d58e6ec28a8a25235b8a47405d301332db"}, + {file = "numpy-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d857f55e7fdf7c38ab96c4558c95b97d1c685be6b05c249f5fdafcbd6f9899e"}, + {file = "numpy-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:bb50ce5fb202a26fd5404620e7ef820ad1ab3558b444cb0b55beb7ef66cd2d63"}, + {file = "numpy-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:355354388cba60f2132df297e2d53053d4063f79077b67b481d21276d61fc4df"}, + {file = "numpy-2.4.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:1d8f9fde5f6dc1b6fc34df8162f3b3079365468703fee7f31d4e0cc8c63baed9"}, + {file = "numpy-2.4.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e0434aa22c821f44eeb4c650b81c7fbdd8c0122c6c4b5a576a76d5a35625ecd9"}, + {file = "numpy-2.4.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40483b2f2d3ba7aad426443767ff5632ec3156ef09742b96913787d13c336471"}, + {file = "numpy-2.4.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9e6a7664ddd9746e20b7325351fe1a8408d0a2bf9c63b5e898290ddc8f09544"}, + {file = "numpy-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ecb0019d44f4cdb50b676c5d0cb4b1eae8e15d1ed3d3e6639f986fc92b2ec52c"}, + {file = "numpy-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d0ffd9e2e4441c96a9c91ec1783285d80bf835b677853fc2770a89d50c1e48ac"}, + {file = "numpy-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:77f0d13fa87036d7553bf81f0e1fe3ce68d14c9976c9851744e4d3e91127e95f"}, + {file = "numpy-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b1f5b45829ac1848893f0ddf5cb326110604d6df96cdc255b0bf9edd154104d4"}, + {file = "numpy-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:23a3e9d1a6f360267e8fbb38ba5db355a6a7e9be71d7fce7ab3125e88bb646c8"}, + {file = "numpy-2.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b54c83f1c0c0f1d748dca0af516062b8829d53d1f0c402be24b4257a9c48ada6"}, + {file = "numpy-2.4.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:aabb081ca0ec5d39591fc33018cd4b3f96e1a2dd6756282029986d00a785fba4"}, + {file = "numpy-2.4.0-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:8eafe7c36c8430b7794edeab3087dec7bf31d634d92f2af9949434b9d1964cba"}, + {file = "numpy-2.4.0-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2f585f52b2baf07ff3356158d9268ea095e221371f1074fadea2f42544d58b4d"}, + {file = "numpy-2.4.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:32ed06d0fe9cae27d8fb5f400c63ccee72370599c75e683a6358dd3a4fb50aaf"}, + {file = "numpy-2.4.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:57c540ed8fb1f05cb997c6761cd56db72395b0d6985e90571ff660452ade4f98"}, + {file = "numpy-2.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a39fb973a726e63223287adc6dafe444ce75af952d711e400f3bf2b36ef55a7b"}, + {file = "numpy-2.4.0.tar.gz", hash = "sha256:6e504f7b16118198f138ef31ba24d985b124c2c469fe8467007cf30fd992f934"}, ] [[package]] @@ -534,7 +597,6 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.20.3", markers = "python_version < \"3.10\""}, {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, {version = ">=1.21.0", markers = "python_version == \"3.10\""}, ] @@ -774,7 +836,7 @@ description = "A lil' TOML parser" optional = false python-versions = ">=3.7" groups = ["main", "dev"] -markers = "python_version <= \"3.10\"" +markers = "python_version == \"3.10\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -818,7 +880,7 @@ files = [ {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, ] -markers = {main = "python_version <= \"3.12\""} +markers = {main = "python_version < \"3.13\""} [[package]] name = "tzdata" @@ -852,5 +914,5 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" -python-versions = "^3.8.0" -content-hash = "a351df77926baa8336857a1948a41df2c175ef8b025e00103e8eb9d3f120b6c7" +python-versions = "^3.10" +content-hash = "786969e28ce63b1b2ecae58f88903b8a408adfd886166f27c18a17c0b869c2a4" diff --git a/pyproject.toml b/pyproject.toml index 49956e8..3961a0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,18 +26,15 @@ requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"] build-backend = "poetry_dynamic_versioning.backend" [tool.poetry.dependencies] -python = "^3.8.0" -requests = "^2.23.0" -types-requests = "^2.28.5" -types-python-dateutil = "^2.8.19" -pandas = ">=1.4.3" -numpy = [ - { version = "^1.22.4", python = ">=3.8,<3.12" }, - { version = "^1.26.0", python = ">=3.12" } -] -tomli = { version = "^2.0.1", python = ">=3.8,<3.11" } +python = "^3.10" +requests = "^2.28.0" +types-requests = "^2.31.0" +types-python-dateutil = "^2.9.0" +pandas = ">=2.0.0" +numpy = ">=2.0.0" +tomli = { version = "^2.0.1", python = "<3.11" } typing_extensions = { version = "^4.5.0", python = "<3.13" } -tenacity = "^8.0.1" +tenacity = "^8.2.0" [tool.poetry.group.dev.dependencies] black = "^24.3.0" From fc2e45cb9e8af6e96c43cf1f3861613fd3829e6d Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Tue, 6 Jan 2026 16:08:11 +0900 Subject: [PATCH 06/21] Update README.md --- README.md | 166 +++++++++++++++++++++++++++++++++------- jquantsapi/client_v2.py | 91 ++++++++++++++++++++-- 2 files changed, 225 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index a01c5fb..feafff1 100644 --- a/README.md +++ b/README.md @@ -24,49 +24,138 @@ jquants-api-client-python を使用するためには「J-Quants API ログイ [J-Quants API ログインページ](https://jpx-jquants.com/login) -### サンプルコード +### サンプルコード (V2) + +V2 API では API キーによる認証を使用します。API キーは [J-Quants API ダッシュボード](https://jpx-jquants.com/dashboard/api-keys) から取得できます。 ```python from datetime import datetime from dateutil import tz import jquantsapi -my_mail_address:str = "*****" -my_password: str = "*****" -cli = jquantsapi.Client(mail_address=my_mail_address, password=my_password) -df = cli.get_price_range( +my_api_key: str = "*****" +cli = jquantsapi.ClientV2(api_key=my_api_key) +df = cli.get_eq_bars_daily_range( start_dt=datetime(2022, 7, 25, tzinfo=tz.gettz("Asia/Tokyo")), end_dt=datetime(2022, 7, 26, tzinfo=tz.gettz("Asia/Tokyo")), ) print(df) ``` +環境変数 `JQUANTS_API_KEY` を設定している場合は、引数を省略できます。 + +```python +import jquantsapi + +cli = jquantsapi.ClientV2() # 環境変数 JQUANTS_API_KEY を使用 +``` + API レスポンスが Dataframe の形式で取得できます。 ```shell - Code Date ... AdjustmentClose AdjustmentVolume -0 13010 2022-07-25 ... 3630.0 8100.0 -1 13050 2022-07-25 ... 2023.0 54410.0 -2 13060 2022-07-25 ... 2001.0 943830.0 -3 13080 2022-07-25 ... 1977.5 121300.0 -4 13090 2022-07-25 ... 43300.0 391.0 -... ... ... ... ... ... -4189 99930 2022-07-26 ... 1426.0 5600.0 -4190 99940 2022-07-26 ... 2605.0 7300.0 -4191 99950 2022-07-26 ... 404.0 13000.0 -4192 99960 2022-07-26 ... 1255.0 4000.0 -4193 99970 2022-07-26 ... 825.0 133600.0 + Code Date ... AdjC AdjVo +0 13010 2022-07-25 ... 3630.0 8100.0 +1 13050 2022-07-25 ... 2023.0 54410.0 +2 13060 2022-07-25 ... 2001.0 943830.0 +3 13080 2022-07-25 ... 1977.5 121300.0 +4 13090 2022-07-25 ... 43300.0 391.0 +... ... ... ... ... ... +4189 99930 2022-07-26 ... 1426.0 5600.0 +4190 99940 2022-07-26 ... 2605.0 7300.0 +4191 99950 2022-07-26 ... 404.0 13000.0 +4192 99960 2022-07-26 ... 1255.0 4000.0 +4193 99970 2022-07-26 ... 825.0 133600.0 [8388 rows x 14 columns] ``` より具体的な使用例は [サンプルノートブック(/examples)](examples) をご参照ください。 -## 対応 API +## 対応 API (V2) + +`ClientV2` クラスで利用可能な V2 API エンドポイントです。 + +### ラッパー群 + +------------------ Free plan or higher is required ------------------ + +- get_eq_master - 上場銘柄一覧 +- get_eq_bars_daily - 株価日足 +- get_fin_summary - 決算サマリー +- get_eq_earnings_cal - 決算発表日 + +------------------ Light plan or higher is required ------------------ + +- get_idx_bars_daily - 指数日足 +- get_idx_bars_daily_topix - TOPIX日足 +- get_mkt_calendar - 営業日カレンダー +- get_bulk_list - バルクデータ一覧 +- get_bulk - バルクデータ取得 + +------------------ Standard plan or higher is required ------------------ + +- get_mkt_short_ratio - 空売り比率 +- get_mkt_short_sale_report - 空売り報告 +- get_mkt_margin_interest - 週次信用取引残高 +- get_mkt_margin_alert - 信用規制情報 +- get_drv_bars_daily_fut - 先物日足 +- get_drv_bars_daily_opt - オプション日足 +- get_drv_bars_daily_opt_225 - 日経225オプション日足 + +------------------ Premium plan or higher is required ------------------ + +- get_mkt_breakdown - 売買内訳 +- get_eq_bars_daily_am - 株価午前終値 +- get_eq_investor_types - 投資部門別売買状況 +- get_fin_details - 財務詳細 +- get_fin_dividend - 配当情報 + +------------------ Minute Bar Addon is required ------------------ + +- get_eq_bars_minute - 分足 +- get_eq_bars_5minute - 5分足(分足から算出) +- get_eq_bars_15minute - 15分足(分足から算出) + +### ユーティリティ群 + +業種や市場区分一覧などを返します。 + +- get_market_segments - 市場区分一覧 +- get_17_sectors - 17業種一覧 +- get_33_sectors - 33業種一覧 + +日付範囲を指定して一括でデータ取得して、取得したデータを結合して返すユーティリティです。 + +------------------ Free plan or higher is required ------------------ + +- get_list - 銘柄一覧(セクター情報付き) +- get_eq_bars_daily_range - 株価日足(範囲指定) +- get_fin_summary_range - 決算サマリー(範囲指定) + +------------------ Standard plan or higher is required ------------------ + +- get_mkt_short_ratio_range - 空売り比率(範囲指定) +- get_mkt_short_sale_report_range - 空売り報告(範囲指定) +- get_mkt_margin_interest_range - 信用取引残高(範囲指定) +- get_mkt_margin_alert_range - 信用規制情報(範囲指定) +- get_drv_bars_daily_fut_range - 先物日足(範囲指定) +- get_drv_bars_daily_opt_range - オプション日足(範囲指定) +- get_drv_bars_daily_opt_225_range - 日経225オプション日足(範囲指定) + +------------------ Premium plan or higher is required ------------------ + +- get_mkt_breakdown_range - 売買内訳(範囲指定) +- get_fin_details_range - 財務詳細(範囲指定) + +## 対応 API (V1) - Deprecated -### ラッパー群  +> **⚠️ 非推奨**: `Client` クラス (V1) は非推奨となりました。今後は `ClientV2` をご利用ください。 +> V1 API は将来のバージョンで削除される予定です。 -J-Quants API の各 API エンドポイントに対応しています。 +
+V1 API 一覧(クリックで展開) + +### ラッパー群 ------------------ Free plan or higher is required ------------------ @@ -102,14 +191,10 @@ J-Quants API の各 API エンドポイントに対応しています。 ### ユーティリティ群 -業種や市場区分一覧などを返します。 - - get_market_segments - get_17_sectors - get_33_sectors -日付範囲を指定して一括でデータ取得して、取得したデータを結合して返すようなユーティリティが用意されています。 - ------------------ Free plan or higher is required ------------------ - get_list @@ -132,8 +217,35 @@ J-Quants API の各 API エンドポイントに対応しています。 - get_derivatives_futures_range - get_derivatives_options_range +
+ ## 設定 +### V2 (ClientV2) + +API キーは設定ファイルおよび環境変数を使用して指定することも可能です。 +設定は下記の順に読み込まれ、設定項目が重複している場合は後に読み込まれた値で上書きされます。 + +1. `/content/drive/MyDrive/drive_ws/secret/jquants-api.toml` (Google Colab のみ) +2. `${HOME}/.jquants-api/jquants-api.toml` +3. `jquants-api.toml` +4. `os.environ["JQUANTS_API_CLIENT_CONFIG_FILE"]` +5. `${JQUANTS_API_KEY}` + +#### 設定ファイル例 + +`jquants-api.toml` は下記のように設定します。 + +```toml +[jquants-api-client] +api_key = "*****" +``` + +### V1 (Client) - Deprecated + +
+V1 設定方法(クリックで展開) + 認証用のメールアドレス/パスワードおよびリフレッシュトークンは設定ファイルおよび環境変数を使用して指定することも可能です。 設定は下記の順に読み込まれ、設定項目が重複している場合は後に読み込まれた値で上書きされます。 @@ -143,7 +255,7 @@ J-Quants API の各 API エンドポイントに対応しています。 4. `os.environ["JQUANTS_API_CLIENT_CONFIG_FILE"]` 5. `${JQUANTS_API_MAIL_ADDRESS}`, `${JQUANTS_API_PASSWORD}`, `${JQUANTS_API_REFRESH_TOKEN}` -### 設定ファイル例 +#### 設定ファイル例 `jquants-api.toml` は下記のように設定します。 @@ -154,9 +266,11 @@ password = "*****" refresh_token = "*****" ``` +
+ ## 動作確認 -Google Colab および Python 3.11 で動作確認を行っています。 +Google Colab および Python 3.13 で動作確認を行っています。 J-Quants API は有償版で継続開発されているため、本ライブラリも今後仕様が変更となる可能性があります。 Python の EOL を迎えたバージョンはサポート対象外となります。 Please note we only support Python supported versions. Unsupported versions (after EOL) are not supported. diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py index d29743f..37b6a42 100644 --- a/jquantsapi/client_v2.py +++ b/jquantsapi/client_v2.py @@ -1,7 +1,9 @@ import os import platform +import sys from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime +from pathlib import Path from typing import Any, Dict, List, Optional, Union import pandas as pd # type: ignore @@ -9,6 +11,11 @@ from requests.adapters import HTTPAdapter from urllib3.util import Retry +if sys.version_info >= (3, 11): + import tomllib +else: + import tomli as tomllib + from jquantsapi import __version__, constants from jquantsapi.apis.v2.equities import ( EqBarsDailyAmApiV2, @@ -60,17 +67,27 @@ def __init__(self, api_key: Optional[str] = None) -> None: """ Args: api_key: J-Quants API v2 の API キー - 未指定の場合は環境変数 `JQUANTS_API_KEY` を参照します。 + 未指定の場合は設定ファイルまたは環境変数から取得します。 + + 設定の読み込み順序(後のものが優先): + 1. /content/drive/MyDrive/drive_ws/secret/jquants-api.toml (Google Colab のみ) + 2. ${HOME}/.jquants-api/jquants-api.toml + 3. jquants-api.toml (カレントディレクトリ) + 4. os.environ["JQUANTS_API_CLIENT_CONFIG_FILE"] で指定されたファイル + 5. 環境変数 JQUANTS_API_KEY """ - if api_key is None: - api_key = os.environ.get("JQUANTS_API_KEY", "") + config = self._load_config() - if not api_key: + if api_key is not None: + self._api_key = api_key + else: + self._api_key = config.get("api_key", "") + + if not self._api_key: raise ValueError( - "api_key is required. Set it via argument or JQUANTS_API_KEY env var." + "api_key is required. Set it via argument, config file, or JQUANTS_API_KEY env var." ) - self._api_key = api_key self._session: Optional[requests.Session] = None # API 実装 (v2) @@ -100,6 +117,68 @@ def __init__(self, api_key: Optional[str] = None) -> None: # ------------------------------------------------------------------ # 内部ユーティリティ # ------------------------------------------------------------------ + def _is_colab(self) -> bool: + """ + Return True if running in colab + """ + return "google.colab" in sys.modules + + def _load_config(self) -> dict: + """ + load config from files and environment variables + + Args: + N/A + Returns: + dict: configurations + """ + config: dict = {} + + # colab config + if self._is_colab(): + colab_config_path = ( + "/content/drive/MyDrive/drive_ws/secret/jquants-api.toml" + ) + config = {**config, **self._read_config(colab_config_path)} + + # user default config + user_config_path = f"{Path.home()}/.jquants-api/jquants-api.toml" + config = {**config, **self._read_config(user_config_path)} + + # current dir config + current_config_path = "jquants-api.toml" + config = {**config, **self._read_config(current_config_path)} + + # env specified config + if "JQUANTS_API_CLIENT_CONFIG_FILE" in os.environ: + env_config_path = os.environ["JQUANTS_API_CLIENT_CONFIG_FILE"] + config = {**config, **self._read_config(env_config_path)} + + # env var (highest priority) + config["api_key"] = os.environ.get( + "JQUANTS_API_KEY", config.get("api_key", "") + ) + + return config + + def _read_config(self, config_path: str) -> dict: + """ + read config from a toml file + + Params: + config_path: a path to a toml file + """ + if not os.path.isfile(config_path): + return {} + + with open(config_path, mode="rb") as f: + ret = tomllib.load(f) + + if "jquants-api-client" not in ret: + return {} + + return ret["jquants-api-client"] + def _request_session( self, status_forcelist: Optional[List[int]] = None, From 14908a3c5d87a3f5a5ba2b91a08959ab0e3b076a Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Tue, 6 Jan 2026 16:18:36 +0900 Subject: [PATCH 07/21] Fix python version in workflow compatibly --- .github/workflows/build.yml | 4 ++-- .github/workflows/publish.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f3586c1..06d646f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: - name: Setup python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.13" cache: "poetry" - name: Install dependencies run: poetry install @@ -29,7 +29,7 @@ jobs: fail-fast: false matrix: os: [ "ubuntu-latest" ] - python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ] + python-version: [ "3.10", "3.11", "3.12", "3.13" ] runs-on: ${{ matrix.os }} steps: #---------------------------------------------- diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a47cf37..dbe600f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,7 +16,7 @@ jobs: - name: Setup python uses: actions/setup-python@v5 with: - python-version: "3.11" + python-version: "3.13" cache: 'poetry' - name: Install dependencies run: poetry install From 2fe772241045b82b3876d5eb9c5c5ddf45ccfef4 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Wed, 7 Jan 2026 09:10:57 +0900 Subject: [PATCH 08/21] Add client v2 test file --- tests/test_client_v2.py | 390 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 tests/test_client_v2.py diff --git a/tests/test_client_v2.py b/tests/test_client_v2.py new file mode 100644 index 0000000..8464426 --- /dev/null +++ b/tests/test_client_v2.py @@ -0,0 +1,390 @@ +from contextlib import nullcontext as does_not_raise +from datetime import datetime +from unittest.mock import MagicMock, call, patch + +import pandas as pd +import pytest +from dateutil import tz + +import jquantsapi +from jquantsapi import client_v2 + + +@pytest.mark.parametrize( + "api_key," + "env, isfile, load," + "exp_api_key," + "exp_raise", + ( + # Case 1: api_key未指定、設定ファイルなし、環境変数なし → エラー + ( + None, + {}, + [False, False, False, False], + [], + "", + pytest.raises(ValueError), + ), + # Case 2: 設定ファイルからapi_key取得 + ( + None, + {}, + [True, False, False, False], + [ + { + "jquants-api-client": { + "api_key": "api_key_from_colab", + } + }, + ], + "api_key_from_colab", + does_not_raise(), + ), + # Case 3: 複数の設定ファイルがある場合、後のものが優先 + ( + None, + {"JQUANTS_API_CLIENT_CONFIG_FILE": "custom.toml"}, + [True, True, True, True], + [ + { + "jquants-api-client": { + "api_key": "api_key_colab", + } + }, + { + "jquants-api-client": { + "api_key": "api_key_user", + } + }, + { + "jquants-api-client": { + "api_key": "api_key_current", + } + }, + { + "jquants-api-client": { + "api_key": "api_key_env_file", + } + }, + ], + "api_key_env_file", + does_not_raise(), + ), + # Case 4: 環境変数JQUANTS_API_KEYが最優先 + ( + None, + {"JQUANTS_API_KEY": "api_key_from_env"}, + [True, False, False, False], + [ + { + "jquants-api-client": { + "api_key": "api_key_from_file", + } + }, + ], + "api_key_from_env", + does_not_raise(), + ), + # Case 5: 引数で直接指定(最優先) + ( + "api_key_from_arg", + {"JQUANTS_API_KEY": "api_key_from_env"}, + [True, False, False, False], + [ + { + "jquants-api-client": { + "api_key": "api_key_from_file", + } + }, + ], + "api_key_from_arg", + does_not_raise(), + ), + # Case 6: 引数のみで指定(設定ファイルなし、環境変数なし) + ( + "api_key_only_arg", + {}, + [False, False, False, False], + [], + "api_key_only_arg", + does_not_raise(), + ), + # Case 7: 設定ファイルにjquants-api-clientセクションがない場合 + ( + None, + {}, + [True, False, False, False], + [ + { + "other-section": { + "api_key": "api_key_wrong_section", + } + }, + ], + "", + pytest.raises(ValueError), + ), + ), +) +def test_client_v2_config( + api_key, + env, + isfile, + load, + exp_api_key, + exp_raise, +): + """ClientV2の設定ファイルと環境変数からのapi_key読み込みテスト""" + with exp_raise, patch.object( + jquantsapi.ClientV2, "_is_colab", return_value=True + ), patch.object(client_v2.os.path, "isfile", side_effect=isfile), patch( + "builtins.open" + ), patch.dict( + client_v2.os.environ, env, clear=True + ), patch.object( + client_v2.tomllib, "load", side_effect=load + ): + cli = jquantsapi.ClientV2(api_key=api_key) + assert cli._api_key == exp_api_key + + +@pytest.mark.parametrize( + "code, date_yyyymmdd, exp_params", + ( + ("", "", {}), + ("86970", "", {"code": "86970"}), + ("", "20220101", {"date": "20220101"}), + ("86970", "20220101", {"code": "86970", "date": "20220101"}), + ), +) +def test_get_eq_master(code, date_yyyymmdd, exp_params): + """get_eq_masterのパラメータテスト""" + ret_value = '{"eq_master": []}' + exp_ret_len = 0 + exp_raise = does_not_raise() + + with exp_raise, patch.object( + jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} + ), patch.object(jquantsapi.ClientV2, "_get") as mock_get: + mock_get.return_value.text = ret_value + + cli = jquantsapi.ClientV2() + ret = cli.get_eq_master(code=code, date_yyyymmdd=date_yyyymmdd) + args, _ = mock_get.call_args + assert args[1] == exp_params + assert len(ret) == exp_ret_len + + +@pytest.mark.parametrize( + "code, from_yyyymmdd, to_yyyymmdd, date_yyyymmdd, exp_params", + ( + ("", "", "", "", {}), + ("86970", "", "", "", {"code": "86970"}), + ("86970", "20220101", "", "", {"code": "86970", "from": "20220101"}), + ("86970", "", "20220131", "", {"code": "86970", "to": "20220131"}), + ( + "86970", + "20220101", + "20220131", + "", + {"code": "86970", "from": "20220101", "to": "20220131"}, + ), + ("", "", "", "20220115", {"date": "20220115"}), + ("86970", "", "", "20220115", {"code": "86970", "date": "20220115"}), + ), +) +def test_get_eq_bars_daily(code, from_yyyymmdd, to_yyyymmdd, date_yyyymmdd, exp_params): + """get_eq_bars_dailyのパラメータテスト""" + ret_value = '{"eq_bars_daily": []}' + exp_ret_len = 0 + exp_raise = does_not_raise() + + with exp_raise, patch.object( + jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} + ), patch.object(jquantsapi.ClientV2, "_get") as mock_get: + mock_get.return_value.text = ret_value + + cli = jquantsapi.ClientV2() + ret = cli.get_eq_bars_daily( + code=code, + from_yyyymmdd=from_yyyymmdd, + to_yyyymmdd=to_yyyymmdd, + date_yyyymmdd=date_yyyymmdd, + ) + args, _ = mock_get.call_args + assert args[1] == exp_params + assert len(ret) == exp_ret_len + + +def test_get_eq_bars_daily_range(): + """ + get_eq_bars_daily_range()を呼ぶ際、引数に様々な型が入っていても問題なく + get_eq_bars_daily()に単一の年月日が渡される事を確認する。 + """ + mock = MagicMock(return_value=pd.DataFrame(columns=["Code", "Date"])) + + with patch.object( + jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} + ): + cli = jquantsapi.ClientV2() + cli.get_eq_bars_daily = mock + + formats = { + "str_8digits": ("20200227", "20200302"), + "str_split_by_hyphen": ("2020-02-27", "2020-03-02"), + "datetime_without_tz": (datetime(2020, 2, 27), datetime(2020, 3, 2)), + "datetime_with_tz": ( + datetime(2020, 2, 27, tzinfo=tz.gettz("Asia/Tokyo")), + datetime(2020, 3, 2, tzinfo=tz.gettz("Asia/Tokyo")), + ), + "pd.Timestamp": (pd.Timestamp("2020-02-27"), pd.Timestamp("2020-03-02")), + } + + for _fmt, (start, end) in formats.items(): + cli.get_eq_bars_daily_range(start, end) + + assert mock.mock_calls == [ + call(date_yyyymmdd="2020-02-27"), + call(date_yyyymmdd="2020-02-28"), + call(date_yyyymmdd="2020-02-29"), + call(date_yyyymmdd="2020-03-01"), + call(date_yyyymmdd="2020-03-02"), + ] + mock.reset_mock() + + +def test_aggregate_bars_n_minute(): + """_aggregate_bars_n_minute()のテスト: 1分足から5分足への集計""" + # テスト用の1分足データ + data = { + "Date": ["2024-01-01"] * 10, + "Time": [ + "09:00:00", + "09:01:00", + "09:02:00", + "09:03:00", + "09:04:00", + "09:05:00", + "09:06:00", + "09:07:00", + "09:08:00", + "09:09:00", + ], + "Code": ["86970"] * 10, + "O": [100, 101, 102, 103, 104, 105, 106, 107, 108, 109], + "H": [110, 111, 112, 113, 114, 115, 116, 117, 118, 119], + "L": [90, 91, 92, 93, 94, 95, 96, 97, 98, 99], + "C": [105, 106, 107, 108, 109, 110, 111, 112, 113, 114], + "Vo": [1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900], + "Va": [100000, 110000, 120000, 130000, 140000, 150000, 160000, 170000, 180000, 190000], + } + df_1min = pd.DataFrame(data) + + with patch.object( + jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} + ): + cli = jquantsapi.ClientV2() + result = cli._aggregate_bars_n_minute(df_1min, 5) + + # 5分足に集計されることを確認 + assert len(result) == 2 + + # 最初の5分足(09:00-09:04) + row1 = result.iloc[0] + assert row1["Time"] == "09:00:00" + assert row1["O"] == 100 # 最初の始値 + assert row1["H"] == 114 # 最高値 + assert row1["L"] == 90 # 最安値 + assert row1["C"] == 109 # 最後の終値 + assert row1["Vo"] == 6000 # 出来高合計 + assert row1["Va"] == 600000 # 売買代金合計 + + # 2番目の5分足(09:05-09:09) + row2 = result.iloc[1] + assert row2["Time"] == "09:05:00" + assert row2["O"] == 105 + assert row2["H"] == 119 + assert row2["L"] == 95 + assert row2["C"] == 114 + assert row2["Vo"] == 8500 + assert row2["Va"] == 850000 + + +def test_aggregate_bars_n_minute_15min(): + """_aggregate_bars_n_minute()のテスト: 1分足から15分足への集計""" + # テスト用の1分足データ(15分分) + times = [f"09:{str(i).zfill(2)}:00" for i in range(15)] + data = { + "Date": ["2024-01-01"] * 15, + "Time": times, + "Code": ["86970"] * 15, + "O": list(range(100, 115)), + "H": list(range(120, 135)), + "L": list(range(80, 95)), + "C": list(range(110, 125)), + "Vo": [1000] * 15, + "Va": [100000] * 15, + } + df_1min = pd.DataFrame(data) + + with patch.object( + jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} + ): + cli = jquantsapi.ClientV2() + result = cli._aggregate_bars_n_minute(df_1min, 15) + + # 15分足に集計されることを確認 + assert len(result) == 1 + + row = result.iloc[0] + assert row["Time"] == "09:00:00" + assert row["O"] == 100 # 最初の始値 + assert row["H"] == 134 # 最高値 + assert row["L"] == 80 # 最安値 + assert row["C"] == 124 # 最後の終値 + assert row["Vo"] == 15000 # 出来高合計 + assert row["Va"] == 1500000 # 売買代金合計 + + +@pytest.mark.parametrize( + "endpoint, exp_params", + ( + ("/equities/master", {"endpoint": "/equities/master"}), + ("/equities/bars/daily", {"endpoint": "/equities/bars/daily"}), + ("/fins/summary", {"endpoint": "/fins/summary"}), + ), +) +def test_get_bulk_list(endpoint, exp_params): + """get_bulk_listのパラメータテスト""" + ret_value = '{"bulk_list": []}' + exp_ret_len = 0 + exp_raise = does_not_raise() + + with exp_raise, patch.object( + jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} + ), patch.object(jquantsapi.ClientV2, "_get") as mock_get: + mock_get.return_value.text = ret_value + + cli = jquantsapi.ClientV2() + ret = cli.get_bulk_list(endpoint=endpoint) + args, _ = mock_get.call_args + assert args[1] == exp_params + assert len(ret) == exp_ret_len + + +def test_get_bulk(): + """get_bulkのテスト""" + ret_value = '{"url": "https://example.com/data.csv"}' + exp_raise = does_not_raise() + + with exp_raise, patch.object( + jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} + ), patch.object(jquantsapi.ClientV2, "_get") as mock_get: + mock_get.return_value.text = ret_value + + cli = jquantsapi.ClientV2() + ret = cli.get_bulk(key="2024/01/01/eq_master.csv") + args, _ = mock_get.call_args + assert args[1] == {"key": "2024/01/01/eq_master.csv"} + assert ret == "https://example.com/data.csv" + From c114022b3666f348ff12622dc6c2b2734bf3646d Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Wed, 7 Jan 2026 09:56:43 +0900 Subject: [PATCH 09/21] Fix dependencies version --- poetry.lock | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 989af1f..e66a83a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -915,4 +915,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "786969e28ce63b1b2ecae58f88903b8a408adfd886166f27c18a17c0b869c2a4" +content-hash = "f04f670001740e9e1f7a3e552d047480def39a36a5a36a3da7ed450b3ebe8844" diff --git a/pyproject.toml b/pyproject.toml index 3961a0b..05c36dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,8 +30,8 @@ python = "^3.10" requests = "^2.28.0" types-requests = "^2.31.0" types-python-dateutil = "^2.9.0" -pandas = ">=2.0.0" -numpy = ">=2.0.0" +pandas = ">=1.4.3" +numpy = ">=1.22.4" tomli = { version = "^2.0.1", python = "<3.11" } typing_extensions = { version = "^4.5.0", python = "<3.13" } tenacity = "^8.2.0" From a11ce497da1cd30f2f359ff855440d07f3173068 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Wed, 7 Jan 2026 10:08:04 +0900 Subject: [PATCH 10/21] Add notebook and tests --- .../20260119-007-jquants-api-v2-starter.ipynb | 446 ++++++++++++++++++ examples/README.md | 10 + jquantsapi/client_v2.py | 82 ++-- jquantsapi/constants.py | 45 +- 4 files changed, 528 insertions(+), 55 deletions(-) create mode 100644 examples/20260119-007-jquants-api-v2-starter.ipynb diff --git a/examples/20260119-007-jquants-api-v2-starter.ipynb b/examples/20260119-007-jquants-api-v2-starter.ipynb new file mode 100644 index 0000000..8073bf0 --- /dev/null +++ b/examples/20260119-007-jquants-api-v2-starter.ipynb @@ -0,0 +1,446 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# J-Quants API V2 スタートガイド\n", + "\n", + "本ノートブックでは、J-Quants API V2 の Python クライアントライブラリ `jquants-api-client` の使用方法について解説します。\n", + "\n", + "## 本ノートブックの内容\n", + "\n", + "- J-Quants API V2 の認証方法(API キーの発行・設定)\n", + "- `ClientV2` クラスを使用したデータ取得の実行例\n", + "- 株価データの可視化\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1. 環境構築\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# jquants-api-client のインストール\n", + "%pip install jquants-api-client\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 2. J-Quants API V2 の認証方法\n", + "\n", + "J-Quants API V2 では、API キー(x-api-key)による認証方式を採用しています。\n", + "\n", + "## 2.1 API キーの発行手順\n", + "\n", + "1. [J-Quants API サービス登録ページ](https://jpx-jquants.com/auth/signup) よりサービスへ登録します。\n", + "2. [ログインページ](https://jpx-jquants.com/auth/signin) から登録したメールアドレス及びパスワードでログインします。\n", + "3. [API キー管理ページ](https://jpx-jquants.com/dashboard/api-keys) にアクセスし、API キーを発行します。\n", + "4. 発行された API キーをコピーして安全な場所に保管してください。\n", + "\n", + "※ API キーは第三者に漏洩しないよう厳重に管理してください。\n", + "\n", + "## 2.2 API キーの設定方法\n", + "\n", + "API キーは以下のいずれかの方法で設定できます。\n", + "\n", + "### 方法 1: 引数で直接指定\n", + "\n", + "```python\n", + "cli = jquantsapi.ClientV2(api_key=\"your-api-key\")\n", + "```\n", + "\n", + "### 方法 2: 環境変数で指定\n", + "\n", + "```bash\n", + "export JQUANTS_API_KEY=\"your-api-key\"\n", + "```\n", + "\n", + "```python\n", + "cli = jquantsapi.ClientV2() # 環境変数から自動取得\n", + "```\n", + "\n", + "### 方法 3: 設定ファイル(jquants-api.toml)で指定\n", + "\n", + "`~/.jquants-api/jquants-api.toml` または `./jquants-api.toml` に以下の内容を記載します。\n", + "\n", + "```toml\n", + "[jquants-api-client]\n", + "api_key = \"your-api-key\"\n", + "```\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Google Colab を使用する場合は、Google Drive をマウントして設定ファイルを読み込むことも可能です\n", + "from google.colab import drive\n", + "drive.mount('/content/drive')\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import plotly.express as px\n", + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "from datetime import datetime, timedelta\n", + "import warnings\n", + "\n", + "import jquantsapi\n", + "\n", + "# pandas データフレームの表示設定\n", + "pd.set_option(\"display.max_columns\", None)\n", + "pd.set_option(\"display.max_rows\", 40)\n", + "pd.set_option(\"display.max_colwidth\", 80)\n", + "\n", + "warnings.simplefilter('ignore')\n", + "\n", + "# ClientV2 の初期化\n", + "# 設定ファイルまたは環境変数に API キーが設定されている場合は引数不要\n", + "cli = jquantsapi.ClientV2()\n", + "\n", + "# 引数で直接指定する場合は以下のように記述します\n", + "# cli = jquantsapi.ClientV2(api_key=\"your_api_key\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 3. 上場銘柄一覧 API\n", + "\n", + "東京証券取引所に上場している銘柄の情報を取得します。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 上場銘柄一覧を取得\n", + "df_master = cli.get_eq_master()\n", + "\n", + "# データフレームの情報を確認\n", + "df_master.info()\n", + "\n", + "print(\"\\n\")\n", + "\n", + "# データフレームを表示\n", + "df_master\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# セクター情報付きの銘柄一覧を取得\n", + "df_list = cli.get_list()\n", + "df_list\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3.1 市場区分別・業種別の銘柄数\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 市場区分別の銘柄数\n", + "df_market = df_list.groupby(\"MktEn\").size().to_frame(\n", + " \"Count\").reset_index().sort_values(by=\"Count\", ascending=False)\n", + "px.bar(df_market, x=\"MktEn\", y=\"Count\", title=\"市場区分別の銘柄数\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ETF/REIT 等を除外した 17 業種別の銘柄数\n", + "df = df_list[df_list[\"S17\"] != \"99\"]\n", + "df_sector = df.groupby(\"S17En\").size().to_frame(\n", + " \"Count\").reset_index().sort_values(by=\"Count\", ascending=False)\n", + "px.bar(df_sector, x=\"S17En\", y=\"Count\", title=\"17業種別の銘柄数\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 4. 株価日足 API\n", + "\n", + "株価データを取得します。調整済み株価(株式分割・併合を考慮)と調整前の株価の両方を取得できます。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 特定銘柄の株価を取得(例: JPX 8697)\n", + "df_price = cli.get_eq_bars_daily(code=\"86970\")\n", + "\n", + "# データフレームの情報を確認\n", + "df_price.info()\n", + "\n", + "print(\"\\n\")\n", + "\n", + "# データフレームを表示\n", + "df_price\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 日付範囲を指定して全銘柄の株価を取得\n", + "d_from = datetime.now() - timedelta(days=30)\n", + "d_to = datetime.now() - timedelta(days=1)\n", + "\n", + "df_prices = cli.get_eq_bars_daily_range(\n", + " start_dt=d_from.strftime(format=\"%Y%m%d\"),\n", + " end_dt=d_to.strftime(format=\"%Y%m%d\")\n", + ")\n", + "df_prices\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4.1 株価チャートの描画\n", + "\n", + "取得した株価データをローソク足チャートとして可視化します。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# JPX(8697)の株価を取得\n", + "df_jpx = cli.get_eq_bars_daily(code=\"86970\")\n", + "\n", + "# グラフに第2軸を設定\n", + "fig = make_subplots(specs=[[{\"secondary_y\": True}]])\n", + "\n", + "# ローソク足を描画\n", + "fig.add_trace(\n", + " go.Candlestick(\n", + " x=df_jpx.Date,\n", + " open=df_jpx.AdjO,\n", + " high=df_jpx.AdjH,\n", + " low=df_jpx.AdjL,\n", + " close=df_jpx.AdjC,\n", + " name=\"OHLC\"\n", + " )\n", + ")\n", + "\n", + "# 25日移動平均線\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=df_jpx.Date,\n", + " y=df_jpx.AdjC.rolling(25).mean(),\n", + " name=\"25日移動平均線\",\n", + " line=dict(color=\"#FF6B6B\")\n", + " )\n", + ")\n", + "\n", + "# 75日移動平均線\n", + "fig.add_trace(\n", + " go.Scatter(\n", + " x=df_jpx.Date,\n", + " y=df_jpx.AdjC.rolling(75).mean(),\n", + " name=\"75日移動平均線\",\n", + " line=dict(color=\"#4ECDC4\")\n", + " )\n", + ")\n", + "\n", + "# 出来高を第2軸に設定\n", + "fig.add_trace(\n", + " go.Bar(\n", + " x=df_jpx.Date,\n", + " y=df_jpx.AdjVo,\n", + " name=\"出来高\",\n", + " marker_color=\"rgba(128, 128, 128, 0.3)\"\n", + " ),\n", + " secondary_y=True\n", + ")\n", + "\n", + "fig.update_layout(\n", + " title_text=\"JPX(8697)株価チャート\",\n", + " xaxis_title=\"日付\",\n", + " yaxis_title=\"株価(円)\",\n", + " yaxis2_title=\"出来高\"\n", + ")\n", + "fig.show()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 5. 決算情報 API\n", + "\n", + "上場会社の四半期及び通期の決算短信に係る情報を取得します。\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# 特定銘柄の決算情報を取得\n", + "df_fins = cli.get_fin_summary(code=\"86970\")\n", + "\n", + "# データフレームの情報を確認\n", + "df_fins.info()\n", + "\n", + "print(\"\\n\")\n", + "\n", + "# データフレームを表示\n", + "df_fins\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5.1 キャッシュを使用した範囲取得\n", + "\n", + "`get_fin_summary_range()` メソッドでは、`cache_dir` 引数を指定することで、取得したデータをローカルにキャッシュできます。\n", + "2回目以降の実行ではキャッシュが利用されるため、API 呼び出し回数を削減できます。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from datetime import datetime, timedelta\n", + "\n", + "# キャッシュディレクトリを指定(例: ホームディレクトリ配下)\n", + "cache_dir = os.path.expanduser(\"~/.jquants/cache\")\n", + "\n", + "# 日付範囲を指定して決算情報を取得(キャッシュ有効)\n", + "d_from = datetime.now() - timedelta(days=30)\n", + "d_to = datetime.now() - timedelta(days=1)\n", + "\n", + "df_fins_range = cli.get_fin_summary_range(\n", + " start_dt=d_from.strftime(\"%Y%m%d\"),\n", + " end_dt=d_to.strftime(\"%Y%m%d\"),\n", + " cache_dir=cache_dir # キャッシュを有効化\n", + ")\n", + "\n", + "print(f\"取得件数: {len(df_fins_range)}\")\n", + "print(f\"キャッシュ保存先: {cache_dir}\")\n", + "df_fins_range.head()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 6. 指数 API(Light プラン以上)\n", + "\n", + "TOPIX 等の指数データを取得します。\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TOPIX 指数を取得\n", + "df_topix = cli.get_idx_bars_daily_topix()\n", + "\n", + "df_topix.info()\n", + "\n", + "print(\"\\n\")\n", + "\n", + "df_topix\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TOPIX 指数の推移をプロット\n", + "fig = px.line(\n", + " df_topix,\n", + " x=\"Date\",\n", + " y=\"C\",\n", + " title=\"TOPIX 指数の推移\"\n", + ")\n", + "fig.update_layout(\n", + " xaxis_title=\"日付\",\n", + " yaxis_title=\"TOPIX\"\n", + ")\n", + "fig.show()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.14" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/README.md b/examples/README.md index c5aaeb4..319b61a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,6 +6,16 @@ jquants-api-client-python の使用例集です。データの取得方法や分 Google Colab での使用例です。実際に実行することも可能となっていますのでご活用ください。 +### V2 API (ClientV2) + +| ファイル | 内容 | Colab で開く | +| :--------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| [20260119-007-jquants-api-v2-starter.ipynb](20260119-007-jquants-api-v2-starter.ipynb) | J-Quants API V2 の使い方や ClientV2 を使った基本的なデータ取得方法を解説します。 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/J-Quants/jquants-api-client-python/blob/master/examples/20260119-007-jquants-api-v2-starter.ipynb) | + +### V1 API (Client) - Deprecated + +> **⚠️ 非推奨**: 以下のノートブックは `Client` クラス (V1) を使用しています。V1 API は非推奨となりました。新規の開発には上記の V2 API (ClientV2) をご利用ください。 + | ファイル | 内容 | Colab で開く | | :--------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------- | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | [20220825-000-write-refresh_token.ipynb](20220825-000-write-refresh_token.ipynb) | リフレッシュトークンを Google Drive のファイルに書き込みます。 | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/J-Quants/jquants-api-client-python/blob/master/examples/20220825-000-write-refresh_token.ipynb) | diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py index 37b6a42..e9efdd2 100644 --- a/jquantsapi/client_v2.py +++ b/jquantsapi/client_v2.py @@ -284,30 +284,30 @@ def get_eq_master( # ------------------------------------------------------------------ # ユーティリティ: 業種・市場区分マスタ (v1 と同様のローカル定義) # ------------------------------------------------------------------ - def get_market_segments() -> pd.DataFrame: + def get_market_segments(self) -> pd.DataFrame: """ - 市場区分コードと名称 (v2 でも v1 と同様にローカル定義を利用) + 市場区分コードと名称 (V2 カラム名) """ df = pd.DataFrame( - constants.MARKET_SEGMENT_DATA, columns=constants.MARKET_SEGMENT_COLUMNS + constants.MARKET_SEGMENT_DATA, columns=constants.MARKET_SEGMENT_COLUMNS_V2 ) - df.sort_values(constants.MARKET_SEGMENT_COLUMNS[0], inplace=True) + df.sort_values(constants.MARKET_SEGMENT_COLUMNS_V2[0], inplace=True) return df def get_17_sectors(self) -> pd.DataFrame: """ - 17 業種コードと名称 + 17 業種コードと名称 (V2 カラム名) """ - df = pd.DataFrame(constants.SECTOR_17_DATA, columns=constants.SECTOR_17_COLUMNS) - df.sort_values(constants.SECTOR_17_COLUMNS[0], inplace=True) + df = pd.DataFrame(constants.SECTOR_17_DATA, columns=constants.SECTOR_17_COLUMNS_V2) + df.sort_values(constants.SECTOR_17_COLUMNS_V2[0], inplace=True) return df def get_33_sectors(self) -> pd.DataFrame: """ - 33 業種コードと名称 + 33 業種コードと名称 (V2 カラム名) """ - df = pd.DataFrame(constants.SECTOR_33_DATA, columns=constants.SECTOR_33_COLUMNS) - df.sort_values(constants.SECTOR_33_COLUMNS[0], inplace=True) + df = pd.DataFrame(constants.SECTOR_33_DATA, columns=constants.SECTOR_33_COLUMNS_V2) + df.sort_values(constants.SECTOR_33_COLUMNS_V2[0], inplace=True) return df # ------------------------------------------------------------------ @@ -330,15 +330,9 @@ def get_list(self, code: str = "", date_yyyymmdd: str = "") -> pd.DataFrame: return pd.DataFrame([], columns=constants.EQ_MASTER_COLUMNS_V2) # 17/33 業種 & 市場区分の英語名を付与 - df_17_sectors = self.get_17_sectors()[ - ["S17", "S17En"] - ] - df_33_sectors = self.get_33_sectors()[ - ["S33", "S33En"] - ] - df_segments = self.get_market_segments()[ - ["Mkt", "MktEn"] - ] + df_17_sectors = self.get_17_sectors()[["S17", "S17En"]] + df_33_sectors = self.get_33_sectors()[["S33", "S33En"]] + df_segments = self.get_market_segments()[["Mkt", "MktEn"]] df_list = pd.merge(df_list, df_17_sectors, how="left", on=["S17"]) df_list = pd.merge(df_list, df_33_sectors, how="left", on=["S33"]) @@ -614,11 +608,8 @@ def get_fin_summary_range( Args: start_dt: 取得開始日 end_dt: 取得終了日 - cache_dir: キャッシュディレクトリ (未指定時は ~/.jquants/cache/v2 を使用) + cache_dir: CSV形式のキャッシュファイルが存在するディレクトリ (未指定時はキャッシュしない) """ - if not cache_dir: - cache_dir = os.path.expanduser("~/.jquants/cache/v2") - buff: list[pd.DataFrame] = [] futures: dict[Any, str] = {} dates = pd.date_range(start_dt, end_dt, freq="D") @@ -627,11 +618,12 @@ def get_fin_summary_range( for s in dates: yyyymmdd = s.strftime("%Y%m%d") yyyy = yyyymmdd[:4] - cache_file = f"fin_summary_{yyyymmdd}.csv.gz" - cache_path = os.path.join(cache_dir, yyyy, cache_file) + cache_file = f"v2_fin_summary_{yyyymmdd}.csv.gz" - if os.path.isfile(cache_path): - df = pd.read_csv(cache_path, dtype=str) + if (cache_dir != "") and os.path.isfile( + f"{cache_dir}/{yyyy}/{cache_file}" + ): + df = pd.read_csv(f"{cache_dir}/{yyyy}/{cache_file}", dtype=str) # 日付カラムをdatetimeに変換 date_cols = [ "DiscDate", @@ -650,16 +642,19 @@ def get_fin_summary_range( future = executor.submit( self.get_fin_summary, date_yyyymmdd=yyyymmdd ) - futures[future] = cache_path + futures[future] = yyyymmdd for future in as_completed(futures): df = future.result() if df.empty: continue buff.append(df) - cache_path = futures[future] - os.makedirs(os.path.dirname(cache_path), exist_ok=True) - df.to_csv(cache_path, index=False) + yyyymmdd = futures[future] + yyyy = yyyymmdd[:4] + cache_file = f"v2_fin_summary_{yyyymmdd}.csv.gz" + if cache_dir != "": + os.makedirs(f"{cache_dir}/{yyyy}", exist_ok=True) + df.to_csv(f"{cache_dir}/{yyyy}/{cache_file}", index=False) if not buff: return pd.DataFrame() @@ -703,11 +698,8 @@ def get_fin_details_range( Args: start_dt: 取得開始日 end_dt: 取得終了日 - cache_dir: キャッシュディレクトリ (未指定時は ~/.jquants/cache/v2 を使用) + cache_dir: CSV形式のキャッシュファイルが存在するディレクトリ (未指定時はキャッシュしない) """ - if not cache_dir: - cache_dir = os.path.expanduser("~/.jquants/cache/v2") - buff: list[pd.DataFrame] = [] futures: dict[Any, str] = {} dates = pd.date_range(start_dt, end_dt, freq="D") @@ -716,11 +708,12 @@ def get_fin_details_range( for s in dates: yyyymmdd = s.strftime("%Y%m%d") yyyy = yyyymmdd[:4] - cache_file = f"fin_details_{yyyymmdd}.csv.gz" - cache_path = os.path.join(cache_dir, yyyy, cache_file) + cache_file = f"v2_fin_details_{yyyymmdd}.csv.gz" - if os.path.isfile(cache_path): - df = pd.read_csv(cache_path, dtype=str) + if (cache_dir != "") and os.path.isfile( + f"{cache_dir}/{yyyy}/{cache_file}" + ): + df = pd.read_csv(f"{cache_dir}/{yyyy}/{cache_file}", dtype=str) if "DiscDate" in df.columns: df["DiscDate"] = pd.to_datetime(df["DiscDate"], errors="coerce") buff.append(df) @@ -728,16 +721,19 @@ def get_fin_details_range( future = executor.submit( self.get_fin_details, date_yyyymmdd=yyyymmdd ) - futures[future] = cache_path + futures[future] = yyyymmdd for future in as_completed(futures): df = future.result() if df.empty: continue buff.append(df) - cache_path = futures[future] - os.makedirs(os.path.dirname(cache_path), exist_ok=True) - df.to_csv(cache_path, index=False) + yyyymmdd = futures[future] + yyyy = yyyymmdd[:4] + cache_file = f"v2_fin_details_{yyyymmdd}.csv.gz" + if cache_dir != "": + os.makedirs(f"{cache_dir}/{yyyy}", exist_ok=True) + df.to_csv(f"{cache_dir}/{yyyy}/{cache_file}", index=False) if not buff: return pd.DataFrame() diff --git a/jquantsapi/constants.py b/jquantsapi/constants.py index eec7071..ca21e63 100644 --- a/jquantsapi/constants.py +++ b/jquantsapi/constants.py @@ -4,7 +4,6 @@ # ref. ja https://jpx-jquants.com/ja/spec/eq-master/sector17code # ref. en https://jpx-jquants.com/en/spec/eq-master/sector17code -SECTOR_17_COLUMNS = ["Sector17Code", "Sector17CodeName", "Sector17CodeNameEnglish"] SECTOR_17_DATA = [ ("1", "食品", "FOODS"), ("2", "エネルギー資源", "ENERGY RESOURCES"), @@ -29,12 +28,6 @@ # ref. ja https://jpx-jquants.com/ja/spec/eq-master/sector33code # ref. en https://jpx-jquants.com/en/spec/eq-master/sector33code # ref. 33-17 mapping https://www.jpx.co.jp/markets/indices/line-up/files/fac_13_sector.pdf -SECTOR_33_COLUMNS = [ - "Sector33Code", - "Sector33CodeName", - "Sector33CodeNameEnglish", - "Sector17Code", -] SECTOR_33_DATA = [ ("0050", "水産・農林業", "Fishery, Agriculture & Forestry", "1"), ("1050", "鉱業", "Mining", "2"), @@ -74,11 +67,6 @@ # ref. ja https://jpx-jquants.com/ja/spec/eq-master/marketcode # ref. en https://jpx-jquants.com/en/spec/eq-master/marketcode -MARKET_SEGMENT_COLUMNS = [ - "MarketCode", - "MarketCodeName", - "MarketCodeNameEnglish", -] MARKET_SEGMENT_DATA = [ ("0101", "東証一部", "1st Section"), ("0102", "東証二部", "2nd Section"), @@ -97,6 +85,27 @@ # V1 API用カラム定義 # ============================================================================ +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/sector17code +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/sector17code +SECTOR_17_COLUMNS = ["Sector17Code", "Sector17CodeName", "Sector17CodeNameEnglish"] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/sector33code +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/sector33code +SECTOR_33_COLUMNS = [ + "Sector33Code", + "Sector33CodeName", + "Sector33CodeNameEnglish", + "Sector17Code", +] + +# ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info/marketcode +# ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info/marketcode +MARKET_SEGMENT_COLUMNS = [ + "MarketCode", + "MarketCodeName", + "MarketCodeNameEnglish", +] + # ref. ja https://jpx.gitbook.io/j-quants-ja/api-reference/listed_info # ref. en https://jpx.gitbook.io/j-quants-en/api-reference/listed_info LISTED_INFO_COLUMNS = [ @@ -670,6 +679,18 @@ # V2 API用カラム定義 # ============================================================================ +# ref. ja https://jpx-jquants.com/ja/spec/eq-master/sector17code +# ref. en https://jpx-jquants.com/en/spec/eq-master/sector17code +SECTOR_17_COLUMNS_V2 = ["S17", "S17Nm", "S17En"] + +# ref. ja https://jpx-jquants.com/ja/spec/eq-master/sector33code +# ref. en https://jpx-jquants.com/en/spec/eq-master/sector33code +SECTOR_33_COLUMNS_V2 = ["S33", "S33Nm", "S33En", "S17"] + +# ref. ja https://jpx-jquants.com/ja/spec/eq-master/marketcode +# ref. en https://jpx-jquants.com/en/spec/eq-master/marketcode +MARKET_SEGMENT_COLUMNS_V2 = ["Mkt", "MktNm", "MktEn"] + # ref. ja https://jpx-jquants.com/ja/spec/eq-master # ref. en https://jpx-jquants.com/en/spec/eq-master EQ_MASTER_COLUMNS_V2 = [ From ef09ebd0d73783afe12d9c7d9b8347fbb0b0b090 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Wed, 7 Jan 2026 10:17:54 +0900 Subject: [PATCH 11/21] Fix dependencies scope appropriately --- poetry.lock | 8 ++++---- pyproject.toml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index e66a83a..f8388bb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -848,7 +848,7 @@ version = "2.9.0.20240316" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, @@ -860,7 +860,7 @@ version = "2.31.0.20240406" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["dev"] files = [ {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, @@ -900,7 +900,7 @@ version = "2.2.1" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, @@ -915,4 +915,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "f04f670001740e9e1f7a3e552d047480def39a36a5a36a3da7ed450b3ebe8844" +content-hash = "5dd3e22e6b2b1d7d371b78e76d43bfadea12775369f79a5b0b8c7763c9db867e" diff --git a/pyproject.toml b/pyproject.toml index 05c36dc..35d6b57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,8 +28,6 @@ build-backend = "poetry_dynamic_versioning.backend" [tool.poetry.dependencies] python = "^3.10" requests = "^2.28.0" -types-requests = "^2.31.0" -types-python-dateutil = "^2.9.0" pandas = ">=1.4.3" numpy = ">=1.22.4" tomli = { version = "^2.0.1", python = "<3.11" } @@ -44,6 +42,8 @@ mypy = "^1.9.0" pyproject-flake8 = "^5.0.0" pytest = "8.1.1" pytest-cov = "^5.0.0" +types-requests = "^2.31.0" +types-python-dateutil = "^2.9.0" [tool.poetry-dynamic-versioning] enable = true From 7fa28bf82ba4c202e3be4b8831badf857d756b61 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Wed, 7 Jan 2026 18:22:32 +0900 Subject: [PATCH 12/21] Fix as review --- .../20260119-007-jquants-api-v2-starter.ipynb | 8 ++--- jquantsapi/client_v2.py | 30 +++++++++++++++++-- jquantsapi/constants.py | 6 ++-- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/examples/20260119-007-jquants-api-v2-starter.ipynb b/examples/20260119-007-jquants-api-v2-starter.ipynb index 8073bf0..27a7758 100644 --- a/examples/20260119-007-jquants-api-v2-starter.ipynb +++ b/examples/20260119-007-jquants-api-v2-starter.ipynb @@ -172,9 +172,9 @@ "outputs": [], "source": [ "# 市場区分別の銘柄数\n", - "df_market = df_list.groupby(\"MktEn\").size().to_frame(\n", + "df_market = df_list.groupby(\"MktNmEn\").size().to_frame(\n", " \"Count\").reset_index().sort_values(by=\"Count\", ascending=False)\n", - "px.bar(df_market, x=\"MktEn\", y=\"Count\", title=\"市場区分別の銘柄数\")\n" + "px.bar(df_market, x=\"MktNmEn\", y=\"Count\", title=\"市場区分別の銘柄数\")\n" ] }, { @@ -185,9 +185,9 @@ "source": [ "# ETF/REIT 等を除外した 17 業種別の銘柄数\n", "df = df_list[df_list[\"S17\"] != \"99\"]\n", - "df_sector = df.groupby(\"S17En\").size().to_frame(\n", + "df_sector = df.groupby(\"S17NmEn\").size().to_frame(\n", " \"Count\").reset_index().sort_values(by=\"Count\", ascending=False)\n", - "px.bar(df_sector, x=\"S17En\", y=\"Count\", title=\"17業種別の銘柄数\")\n" + "px.bar(df_sector, x=\"S17NmEn\", y=\"Count\", title=\"17業種別の銘柄数\")\n" ] }, { diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py index e9efdd2..9682ee8 100644 --- a/jquantsapi/client_v2.py +++ b/jquantsapi/client_v2.py @@ -330,9 +330,9 @@ def get_list(self, code: str = "", date_yyyymmdd: str = "") -> pd.DataFrame: return pd.DataFrame([], columns=constants.EQ_MASTER_COLUMNS_V2) # 17/33 業種 & 市場区分の英語名を付与 - df_17_sectors = self.get_17_sectors()[["S17", "S17En"]] - df_33_sectors = self.get_33_sectors()[["S33", "S33En"]] - df_segments = self.get_market_segments()[["Mkt", "MktEn"]] + df_17_sectors = self.get_17_sectors()[["S17", "S17NmEn"]] + df_33_sectors = self.get_33_sectors()[["S33", "S33NmEn"]] + df_segments = self.get_market_segments()[["Mkt", "MktNmEn"]] df_list = pd.merge(df_list, df_17_sectors, how="left", on=["S17"]) df_list = pd.merge(df_list, df_33_sectors, how="left", on=["S33"]) @@ -1306,3 +1306,27 @@ def get_bulk(self, key: str) -> str: """ return self._bulk_get_api.execute(self, key=key) + def download_bulk(self, key: str, output_path: str) -> None: + """ + bulk-get で取得した URL からファイルをダウンロードして保存 + + Args: + key: get_bulk_listで取得したKey + output_path: ダウンロードファイルの保存先パス + """ + # ダウンロード URL を取得 + url = self._bulk_get_api.execute(self, key=key) + + # ディレクトリが存在しない場合は作成 + os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) + + # ファイルをダウンロード + session = self._request_session() + response = session.get(url, stream=True, timeout=300) + response.raise_for_status() + + # ファイルに書き込み + with open(output_path, "wb") as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + diff --git a/jquantsapi/constants.py b/jquantsapi/constants.py index ca21e63..bbf6795 100644 --- a/jquantsapi/constants.py +++ b/jquantsapi/constants.py @@ -681,15 +681,15 @@ # ref. ja https://jpx-jquants.com/ja/spec/eq-master/sector17code # ref. en https://jpx-jquants.com/en/spec/eq-master/sector17code -SECTOR_17_COLUMNS_V2 = ["S17", "S17Nm", "S17En"] +SECTOR_17_COLUMNS_V2 = ["S17", "S17Nm", "S17NmEn"] # ref. ja https://jpx-jquants.com/ja/spec/eq-master/sector33code # ref. en https://jpx-jquants.com/en/spec/eq-master/sector33code -SECTOR_33_COLUMNS_V2 = ["S33", "S33Nm", "S33En", "S17"] +SECTOR_33_COLUMNS_V2 = ["S33", "S33Nm", "S33NmEn", "S17"] # ref. ja https://jpx-jquants.com/ja/spec/eq-master/marketcode # ref. en https://jpx-jquants.com/en/spec/eq-master/marketcode -MARKET_SEGMENT_COLUMNS_V2 = ["Mkt", "MktNm", "MktEn"] +MARKET_SEGMENT_COLUMNS_V2 = ["Mkt", "MktNm", "MktNmEn"] # ref. ja https://jpx-jquants.com/ja/spec/eq-master # ref. en https://jpx-jquants.com/en/spec/eq-master From 63b0e6780b3e12bd2e1121ceaf924d399458a4cd Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Thu, 8 Jan 2026 09:29:52 +0900 Subject: [PATCH 13/21] Fix tests to pass --- jquantsapi/apis/v1/derivatives.py | 73 ++++++----- jquantsapi/apis/v1/indices.py | 55 ++++---- jquantsapi/apis/v1/markets.py | 209 ++++++++++++++++++------------ tests/test_client_v2.py | 24 ++-- 4 files changed, 212 insertions(+), 149 deletions(-) diff --git a/jquantsapi/apis/v1/derivatives.py b/jquantsapi/apis/v1/derivatives.py index 4ffdc12..3067e47 100644 --- a/jquantsapi/apis/v1/derivatives.py +++ b/jquantsapi/apis/v1/derivatives.py @@ -27,20 +27,24 @@ def execute( category: str = "", contract_flag: str = "", ) -> pd.DataFrame: - j = client._get_derivatives_futures_raw( # type: ignore[attr-defined] - category=category, - date_yyyymmdd=date_yyyymmdd, - contract_flag=contract_flag, - ) + # 元の _get_derivatives_futures_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/derivatives/futures" # type: ignore[attr-defined] + params = { + "category": category, + "date": date_yyyymmdd, + "contract_flag": contract_flag, + } + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data = d["futures"] while "pagination_key" in d: - j = client._get_derivatives_futures_raw( # type: ignore[attr-defined] - category=category, - date_yyyymmdd=date_yyyymmdd, - contract_flag=contract_flag, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["futures"] @@ -72,22 +76,25 @@ def execute( contract_flag: str = "", code: str = "", ) -> pd.DataFrame: - j = client._get_derivatives_options_raw( # type: ignore[attr-defined] - category=category, - date_yyyymmdd=date_yyyymmdd, - contract_flag=contract_flag, - code=code, - ) + # 元の _get_derivatives_options_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/derivatives/options" # type: ignore[attr-defined] + params = { + "category": category, + "date": date_yyyymmdd, + "contract_flag": contract_flag, + "code": code, + } + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data = d["options"] while "pagination_key" in d: - j = client._get_derivatives_options_raw( # type: ignore[attr-defined] - category=category, - date_yyyymmdd=date_yyyymmdd, - contract_flag=contract_flag, - code=code, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["options"] @@ -116,16 +123,20 @@ def execute( *, date_yyyymmdd: str, ) -> pd.DataFrame: - j = client._get_option_index_option_raw( # type: ignore[attr-defined] - date_yyyymmdd=date_yyyymmdd, - ) + # 元の _get_option_index_option_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/option/index_option" # type: ignore[attr-defined] + params = {"date": date_yyyymmdd} + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data = d["index_option"] while "pagination_key" in d: - j = client._get_option_index_option_raw( # type: ignore[attr-defined] - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["index_option"] diff --git a/jquantsapi/apis/v1/indices.py b/jquantsapi/apis/v1/indices.py index 9a4ab3b..187a044 100644 --- a/jquantsapi/apis/v1/indices.py +++ b/jquantsapi/apis/v1/indices.py @@ -28,22 +28,27 @@ def execute( to_yyyymmdd: str = "", date_yyyymmdd: str = "", ) -> pd.DataFrame: - j = client._get_indices_raw( # type: ignore[attr-defined] - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - ) + # 元の _get_indices_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/indices" # type: ignore[attr-defined] + params = {"code": code} + if date_yyyymmdd != "": + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data = d["indices"] while "pagination_key" in d: - j = client._get_indices_raw( # type: ignore[attr-defined] - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["indices"] @@ -73,18 +78,24 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", ) -> pd.DataFrame: - j = client._get_indices_topix_raw( # type: ignore[attr-defined] - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - ) + # 元の _get_indices_topix_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/indices/topix" # type: ignore[attr-defined] + params = {} + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data = d["topix"] while "pagination_key" in d: - j = client._get_indices_topix_raw( # type: ignore[attr-defined] - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["topix"] diff --git a/jquantsapi/apis/v1/markets.py b/jquantsapi/apis/v1/markets.py index ce676e5..61d823b 100644 --- a/jquantsapi/apis/v1/markets.py +++ b/jquantsapi/apis/v1/markets.py @@ -37,21 +37,26 @@ def execute( from_yyyymmdd: starting point of data period (e.g. 20210901 or 2021-09-01) to_yyyymmdd: end point of data period (e.g. 20210907 or 2021-09-07) """ - # 元の get_markets_trades_spec と同じ処理をそのまま適用 - j = client._get_markets_trades_spec_raw( # type: ignore[attr-defined] - section=section, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - ) + # 元の _get_markets_trades_spec_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/markets/trades_spec" # type: ignore[attr-defined] + params = {} + if section != "": + params["section"] = section + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data: List[Dict[str, Any]] = d["trades_spec"] while "pagination_key" in d: - j = client._get_markets_trades_spec_raw( # type: ignore[attr-defined] - section=section, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["trades_spec"] df = pd.DataFrame.from_dict(data) @@ -87,22 +92,27 @@ def execute( """ `/markets/weekly_margin_interest` を実行し、信用取引週末残高を DataFrame で返す。 """ - j = client._get_markets_weekly_margin_interest_raw( # type: ignore[attr-defined] - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - ) + # 元の _get_markets_weekly_margin_interest_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/markets/weekly_margin_interest" # type: ignore[attr-defined] + params = {"code": code} + if date_yyyymmdd != "": + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data: List[Dict[str, Any]] = d["weekly_margin_interest"] while "pagination_key" in d: - j = client._get_markets_weekly_margin_interest_raw( # type: ignore[attr-defined] - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["weekly_margin_interest"] df = pd.DataFrame.from_dict(data) @@ -135,11 +145,19 @@ def execute( """ `/markets/trading_calendar` を実行し、取引カレンダーデータを DataFrame で返す。 """ - j = client._get_markets_trading_calendar_raw( # type: ignore[attr-defined] - holiday_division=holiday_division, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - ) + # 元の _get_markets_trading_calendar_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/markets/trading_calendar" # type: ignore[attr-defined] + params = {} + if holiday_division != "": + params["holidaydivision"] = holiday_division + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) df = pd.DataFrame.from_dict(d["trading_calendar"]) cols = constants.MARKETS_TRADING_CALENDAR @@ -172,22 +190,27 @@ def execute( """ `/markets/short_selling` を実行し、業種別空売り比率データを DataFrame で返す。 """ - j = client._get_markets_short_selling_raw( # type: ignore[attr-defined] - sector_33_code=sector_33_code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - ) + # 元の _get_markets_short_selling_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/markets/short_selling" # type: ignore[attr-defined] + params = {"sector33code": sector_33_code} + if date_yyyymmdd != "": + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data: List[Dict[str, Any]] = d["short_selling"] while "pagination_key" in d: - j = client._get_markets_short_selling_raw( # type: ignore[attr-defined] - sector_33_code=sector_33_code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["short_selling"] df = pd.DataFrame.from_dict(data) @@ -221,22 +244,27 @@ def execute( """ `/markets/breakdown` を実行し、売買内訳データを DataFrame で返す。 """ - j = client._get_markets_breakdown_raw( # type: ignore[attr-defined] - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - ) + # 元の _get_markets_breakdown_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/markets/breakdown" # type: ignore[attr-defined] + params = {"code": code} + if date_yyyymmdd != "": + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data: List[Dict[str, Any]] = d["breakdown"] while "pagination_key" in d: - j = client._get_markets_breakdown_raw( # type: ignore[attr-defined] - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["breakdown"] df = pd.DataFrame.from_dict(data) @@ -271,24 +299,30 @@ def execute( """ `/markets/short_selling_positions` を実行し、空売り残高報告データを DataFrame で返す。 """ - j = client._get_markets_short_selling_positions_raw( # type: ignore[attr-defined] - code=code, - disclosed_date=disclosed_date, - disclosed_date_from=disclosed_date_from, - disclosed_date_to=disclosed_date_to, - calculated_date=calculated_date, - ) + # 元の _get_markets_short_selling_positions_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/markets/short_selling_positions" # type: ignore[attr-defined] + params = {} + if code != "": + params["code"] = code + if disclosed_date != "": + params["disclosed_date"] = disclosed_date + if disclosed_date_from != "": + params["disclosed_date_from"] = disclosed_date_from + if disclosed_date_to != "": + params["disclosed_date_to"] = disclosed_date_to + if calculated_date != "": + params["calculated_date"] = calculated_date + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data: List[Dict[str, Any]] = d["short_selling_positions"] while "pagination_key" in d: - j = client._get_markets_short_selling_positions_raw( # type: ignore[attr-defined] - code=code, - disclosed_date=disclosed_date, - disclosed_date_from=disclosed_date_from, - disclosed_date_to=disclosed_date_to, - calculated_date=calculated_date, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["short_selling_positions"] df = pd.DataFrame.from_dict(data) @@ -330,22 +364,29 @@ def execute( """ `/markets/daily_margin_interest` を実行し、日々公表信用取引残高を DataFrame で返す。 """ - j = client._get_markets_daily_margin_interest_raw( # type: ignore[attr-defined] - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - ) + # 元の _get_markets_daily_margin_interest_raw の実装を統合 + url = f"{client.JQUANTS_API_BASE}/markets/daily_margin_interest" # type: ignore[attr-defined] + params = {} + if code != "": + params["code"] = code + if date_yyyymmdd != "": + params["date"] = date_yyyymmdd + else: + if from_yyyymmdd != "": + params["from"] = from_yyyymmdd + if to_yyyymmdd != "": + params["to"] = to_yyyymmdd + + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d: Dict[str, Any] = json.loads(j) data: List[Dict[str, Any]] = d["daily_margin_interest"] while "pagination_key" in d: - j = client._get_markets_daily_margin_interest_raw( # type: ignore[attr-defined] - code=code, - from_yyyymmdd=from_yyyymmdd, - to_yyyymmdd=to_yyyymmdd, - date_yyyymmdd=date_yyyymmdd, - pagination_key=d["pagination_key"], - ) + params["pagination_key"] = d["pagination_key"] + ret = client._get(url, params) # type: ignore[attr-defined] + ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] + j = ret.text d = json.loads(j) data += d["daily_margin_interest"] df = pd.json_normalize(data=data) diff --git a/tests/test_client_v2.py b/tests/test_client_v2.py index 8464426..33fddb4 100644 --- a/tests/test_client_v2.py +++ b/tests/test_client_v2.py @@ -159,19 +159,19 @@ def test_client_v2_config( ) def test_get_eq_master(code, date_yyyymmdd, exp_params): """get_eq_masterのパラメータテスト""" - ret_value = '{"eq_master": []}' + ret_value = [] # _get_paginatedは配列を返す exp_ret_len = 0 exp_raise = does_not_raise() with exp_raise, patch.object( jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} - ), patch.object(jquantsapi.ClientV2, "_get") as mock_get: - mock_get.return_value.text = ret_value + ), patch.object(jquantsapi.ClientV2, "_get_paginated") as mock_get_paginated: + mock_get_paginated.return_value = ret_value cli = jquantsapi.ClientV2() - ret = cli.get_eq_master(code=code, date_yyyymmdd=date_yyyymmdd) - args, _ = mock_get.call_args - assert args[1] == exp_params + ret = cli.get_eq_master(code=code, date=date_yyyymmdd) + args, kwargs = mock_get_paginated.call_args + assert kwargs.get("params", {}) == exp_params assert len(ret) == exp_ret_len @@ -195,14 +195,14 @@ def test_get_eq_master(code, date_yyyymmdd, exp_params): ) def test_get_eq_bars_daily(code, from_yyyymmdd, to_yyyymmdd, date_yyyymmdd, exp_params): """get_eq_bars_dailyのパラメータテスト""" - ret_value = '{"eq_bars_daily": []}' + ret_value = {"data": []} # resp.json()で返される辞書 exp_ret_len = 0 exp_raise = does_not_raise() with exp_raise, patch.object( jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} ), patch.object(jquantsapi.ClientV2, "_get") as mock_get: - mock_get.return_value.text = ret_value + mock_get.return_value.json.return_value = ret_value cli = jquantsapi.ClientV2() ret = cli.get_eq_bars_daily( @@ -356,14 +356,14 @@ def test_aggregate_bars_n_minute_15min(): ) def test_get_bulk_list(endpoint, exp_params): """get_bulk_listのパラメータテスト""" - ret_value = '{"bulk_list": []}' + ret_value = {"data": []} # resp.json()で返される辞書 exp_ret_len = 0 exp_raise = does_not_raise() with exp_raise, patch.object( jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} ), patch.object(jquantsapi.ClientV2, "_get") as mock_get: - mock_get.return_value.text = ret_value + mock_get.return_value.json.return_value = ret_value cli = jquantsapi.ClientV2() ret = cli.get_bulk_list(endpoint=endpoint) @@ -374,13 +374,13 @@ def test_get_bulk_list(endpoint, exp_params): def test_get_bulk(): """get_bulkのテスト""" - ret_value = '{"url": "https://example.com/data.csv"}' + ret_value = {"url": "https://example.com/data.csv"} # resp.json()で返される辞書 exp_raise = does_not_raise() with exp_raise, patch.object( jquantsapi.ClientV2, "_load_config", return_value={"api_key": "dummy_key"} ), patch.object(jquantsapi.ClientV2, "_get") as mock_get: - mock_get.return_value.text = ret_value + mock_get.return_value.json.return_value = ret_value cli = jquantsapi.ClientV2() ret = cli.get_bulk(key="2024/01/01/eq_master.csv") From 2d79df5e5385d1350cb07368afa8464eb98c88f0 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Thu, 8 Jan 2026 10:17:20 +0900 Subject: [PATCH 14/21] Add rate limit description on README --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index feafff1..115e15c 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,19 @@ API レスポンスが Dataframe の形式で取得できます。 +## レートリミット + +J-Quants API には、サービスの安定稼働を目的としてレートリミット(利用頻度の制限)が設けられています。 +プランごとのレートリミットの詳細については、[公式ドキュメント](https://jpx-jquants.com/spec/rate-limits) をご参照ください。 + +### 注意事項 + +サフィックスが `_range` で終わるメソッド(例: `get_eq_bars_daily_range`、`get_fin_summary_range` など)は、指定された日付範囲に対して並列処理で繰り返し API リクエストを行います。 +そのため、広い日付範囲を指定した場合や、短時間に複数回実行した場合、レートリミットに達する可能性があります。 + +レートリミットを超過すると、API は HTTP ステータスコード `429 Too Many Requests` を返します。 +エラーが発生した場合は、一定時間待機してから再試行するか、より狭い日付範囲で分割してリクエストすることをご検討ください。 + ## 設定 ### V2 (ClientV2) From 62663cb394e2a1716e847cd56ebd61c074f4d2e7 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Thu, 8 Jan 2026 15:11:10 +0900 Subject: [PATCH 15/21] Fix concat key --- jquantsapi/client_v2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py index 9682ee8..a2391cf 100644 --- a/jquantsapi/client_v2.py +++ b/jquantsapi/client_v2.py @@ -1010,7 +1010,7 @@ def get_mkt_margin_alert_range( if not buff: return pd.DataFrame() return pd.concat(buff).sort_values( - ["Date", "Code"] + ["PubDate", "Code"] ).reset_index(drop=True) # ------------------------------------------------------------------ From b43fc4a64dee6173fe129dddb27e0af6ed17dada Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Thu, 8 Jan 2026 15:32:29 +0900 Subject: [PATCH 16/21] Fix comments --- jquantsapi/apis/v1/derivatives.py | 12 +++--------- jquantsapi/apis/v1/fins.py | 16 ++++------------ jquantsapi/apis/v1/indices.py | 8 ++------ jquantsapi/apis/v1/listed.py | 2 +- jquantsapi/apis/v1/markets.py | 29 +++++++---------------------- jquantsapi/apis/v1/prices.py | 10 ++-------- jquantsapi/apis/v2/bulk.py | 4 ++-- jquantsapi/apis/v2/derivatives.py | 6 +++--- jquantsapi/apis/v2/equities.py | 14 ++++++-------- jquantsapi/apis/v2/fins.py | 12 +++--------- jquantsapi/apis/v2/indices.py | 4 ++-- jquantsapi/apis/v2/markets.py | 18 ++++++------------ 12 files changed, 41 insertions(+), 94 deletions(-) diff --git a/jquantsapi/apis/v1/derivatives.py b/jquantsapi/apis/v1/derivatives.py index 3067e47..8d55241 100644 --- a/jquantsapi/apis/v1/derivatives.py +++ b/jquantsapi/apis/v1/derivatives.py @@ -11,9 +11,7 @@ class DerivativesFuturesApiV1(BaseApi): """ - v1 `/derivatives/futures` のラッパ。 - - 既存の `Client.get_derivatives_futures` のロジックをそのまま移植する。 + v1 の先物四本値 API (`/derivatives/futures`) のラッパークラス。 """ name = "derivatives_futures" @@ -59,9 +57,7 @@ def execute( class DerivativesOptionsApiV1(BaseApi): """ - v1 `/derivatives/options` のラッパ。 - - 既存の `Client.get_derivatives_options` のロジックをそのまま移植する。 + v1 のオプション四本値 API (`/derivatives/options`) のラッパークラス。 """ name = "derivatives_options" @@ -109,9 +105,7 @@ def execute( class OptionIndexOptionApiV1(BaseApi): """ - v1 `/option/index_option` のラッパ。 - - 既存の `Client.get_option_index_option` のロジックをそのまま移植する。 + v1 のオプション指数四本値 API (`/option/index_option`) のラッパークラス。 """ name = "option_index_option" diff --git a/jquantsapi/apis/v1/fins.py b/jquantsapi/apis/v1/fins.py index 30f9890..9527002 100644 --- a/jquantsapi/apis/v1/fins.py +++ b/jquantsapi/apis/v1/fins.py @@ -11,9 +11,7 @@ class FinsStatementsApiV1(BaseApi): """ - v1 の財務情報 API (`/fins/statements`) を担当するクラス。 - - 既存の `Client.get_fins_statements` のロジックをそのまま移植します。 + v1 の財務情報 API (`/fins/statements`) のラッパークラス。 """ name = "fins_statements" @@ -78,9 +76,7 @@ def execute( class FinsFsDetailsApiV1(BaseApi): """ - v1 の財務諸表(BS/PL) API (`/fins/fs_details`) を担当するクラス。 - - 既存の `Client.get_fins_fs_details` のロジックをそのまま移植します。 + v1 の財務諸表(BS/PL) API (`/fins/fs_details`) のラッパークラス。 """ name = "fins_fs_details" @@ -127,9 +123,7 @@ def execute( class FinsDividendApiV1(BaseApi): """ - v1 の配当金情報 API (`/fins/dividend`) を担当するクラス。 - - 既存の `Client.get_fins_dividend` のロジックをそのまま移植します。 + v1 の配当金情報 API (`/fins/dividend`) のラッパークラス。 """ name = "fins_dividend" @@ -187,9 +181,7 @@ def execute( class FinsAnnouncementApiV1(BaseApi): """ - v1 の決算発表予定 API (`/fins/announcement`) を担当するクラス。 - - 既存の `Client.get_fins_announcement` のロジックをそのまま移植します。 + v1 の決算発表予定 API (`/fins/announcement`) のラッパークラス。 """ name = "fins_announcement" diff --git a/jquantsapi/apis/v1/indices.py b/jquantsapi/apis/v1/indices.py index 187a044..0c6b48c 100644 --- a/jquantsapi/apis/v1/indices.py +++ b/jquantsapi/apis/v1/indices.py @@ -11,9 +11,7 @@ class IndicesApiV1(BaseApi): """ - v1 `/indices` のラッパ。 - - 既存の `Client.get_indices` のロジックをそのまま移植する。 + v1 の指数四本値 API (`/indices`) のラッパークラス。 """ name = "indices" @@ -63,9 +61,7 @@ def execute( class IndicesTopixApiV1(BaseApi): """ - v1 `/indices/topix` のラッパ。 - - 既存の `Client.get_indices_topix` のロジックをそのまま移植する。 + v1 の TOPIX 指数四本値 API (`/indices/topix`) のラッパークラス。 """ name = "indices_topix" diff --git a/jquantsapi/apis/v1/listed.py b/jquantsapi/apis/v1/listed.py index aa6ec3d..c704943 100644 --- a/jquantsapi/apis/v1/listed.py +++ b/jquantsapi/apis/v1/listed.py @@ -11,7 +11,7 @@ class ListedInfoApiV1(BaseApi): """ - v1 の上場銘柄一覧 API (`/listed/info`) を担当するクラス。 + v1 の上場銘柄一覧 API (`/listed/info`) のラッパークラス。 """ name = "listed_info" diff --git a/jquantsapi/apis/v1/markets.py b/jquantsapi/apis/v1/markets.py index 61d823b..8144e72 100644 --- a/jquantsapi/apis/v1/markets.py +++ b/jquantsapi/apis/v1/markets.py @@ -11,10 +11,7 @@ class MarketsTradesSpecApiV1(BaseApi): """ - v1 の投資部門別売買状況 API (`/markets/trades_spec`) を担当するクラス。 - - 既存の `Client._get_markets_trades_spec_raw` と - `Client.get_markets_trades_spec` のロジックをそのまま移植します。 + v1 の投資部門別売買状況 API (`/markets/trades_spec`) のラッパークラス。 """ name = "markets_trades_spec" @@ -72,9 +69,7 @@ def execute( class MarketsWeeklyMarginInterestApiV1(BaseApi): """ - v1 の信用取引週末残高 API (`/markets/weekly_margin_interest`) を担当するクラス。 - - 既存の `Client.get_markets_weekly_margin_interest` のロジックをそのまま移植します。 + v1 の信用取引週末残高 API (`/markets/weekly_margin_interest`) のラッパークラス。 """ name = "markets_weekly_margin_interest" @@ -126,9 +121,7 @@ def execute( class MarketsTradingCalendarApiV1(BaseApi): """ - v1 の取引カレンダー API (`/markets/trading_calendar`) を担当するクラス。 - - 既存の `Client.get_markets_trading_calendar` のロジックをそのまま移植します。 + v1 の取引カレンダー API (`/markets/trading_calendar`) のラッパークラス。 """ name = "markets_trading_calendar" @@ -170,9 +163,7 @@ def execute( class MarketsShortSellingApiV1(BaseApi): """ - v1 の業種別空売り比率 API (`/markets/short_selling`) を担当するクラス。 - - 既存の `Client.get_markets_short_selling` のロジックをそのまま移植します。 + v1 の業種別空売り比率 API (`/markets/short_selling`) のラッパークラス。 """ name = "markets_short_selling" @@ -224,9 +215,7 @@ def execute( class MarketsBreakdownApiV1(BaseApi): """ - v1 の売買内訳 API (`/markets/breakdown`) を担当するクラス。 - - 既存の `Client.get_markets_breakdown` のロジックをそのまま移植します。 + v1 の売買内訳 API (`/markets/breakdown`) のラッパークラス。 """ name = "markets_breakdown" @@ -278,9 +267,7 @@ def execute( class MarketsShortSellingPositionsApiV1(BaseApi): """ - v1 の空売り残高報告 API (`/markets/short_selling_positions`) を担当するクラス。 - - 既存の `Client.get_markets_short_selling_positions` のロジックをそのまま移植します。 + v1 の空売り残高報告 API (`/markets/short_selling_positions`) のラッパークラス。 """ name = "markets_short_selling_positions" @@ -344,9 +331,7 @@ def execute( class MarketsDailyMarginInterestApiV1(BaseApi): """ - v1 の日々公表信用取引残高 API (`/markets/daily_margin_interest`) を担当するクラス。 - - 既存の `Client.get_markets_daily_margin_interest` のロジックをそのまま移植します。 + v1 の日々公表信用取引残高 API (`/markets/daily_margin_interest`) のラッパークラス。 """ name = "markets_daily_margin_interest" diff --git a/jquantsapi/apis/v1/prices.py b/jquantsapi/apis/v1/prices.py index 0736535..e25d0f3 100644 --- a/jquantsapi/apis/v1/prices.py +++ b/jquantsapi/apis/v1/prices.py @@ -11,10 +11,7 @@ class PricesDailyQuotesApiV1(BaseApi): """ - v1 の株価四本値 API (`/prices/daily_quotes`) を担当するクラス。 - - 既存の `Client._get_prices_daily_quotes_raw` と - `Client.get_prices_daily_quotes` のロジックをそのまま移植します。 + v1 の株価四本値 API (`/prices/daily_quotes`) のラッパークラス。 """ name = "prices_daily_quotes" @@ -84,10 +81,7 @@ def execute( class PricesPricesAmApiV1(BaseApi): """ - v1 の前場四本値 API (`/prices/prices_am`) を担当するクラス。 - - 既存の `Client._get_prices_prices_am_raw` と - `Client.get_prices_prices_am` のロジックをそのまま移植します。 + v1 の前場四本値 API (`/prices/prices_am`) のラッパークラス。 """ name = "prices_prices_am" diff --git a/jquantsapi/apis/v2/bulk.py b/jquantsapi/apis/v2/bulk.py index 11dc209..5f67cd4 100644 --- a/jquantsapi/apis/v2/bulk.py +++ b/jquantsapi/apis/v2/bulk.py @@ -11,7 +11,7 @@ class BulkListApiV2(BaseApi): """ - v2 の Bulk List API (`/bulk/list`) を担当するクラス。 + v2 の Bulk List API (`/bulk/list`) のラッパークラス。 指定したエンドポイントで取得可能なデータ一覧を取得します。 """ @@ -57,7 +57,7 @@ def execute( class BulkGetApiV2(BaseApi): """ - v2 の Bulk Get API (`/bulk/get`) を担当するクラス。 + v2 の Bulk Get API (`/bulk/get`) のラッパークラス。 指定したキーのデータをダウンロードするためのURLを取得します。 """ diff --git a/jquantsapi/apis/v2/derivatives.py b/jquantsapi/apis/v2/derivatives.py index f3d8a5e..ad7e020 100644 --- a/jquantsapi/apis/v2/derivatives.py +++ b/jquantsapi/apis/v2/derivatives.py @@ -9,7 +9,7 @@ class DrvBarsDailyFutApiV2(BaseApi): """ - v2 `/derivatives/bars/daily/futures` のラッパ。 + v2 の先物四本値 API (`/derivatives/bars/daily/futures`) のラッパークラス。 v1 `/derivatives/futures` に対応する先物四本値エンドポイント。 """ @@ -52,7 +52,7 @@ def execute( class DrvBarsDailyOptApiV2(BaseApi): """ - v2 `/derivatives/bars/daily/options` のラッパ。 + v2 のオプション四本値 API (`/derivatives/bars/daily/options`) のラッパークラス。 v1 `/derivatives/options` に対応するオプション四本値エンドポイント。 """ @@ -99,7 +99,7 @@ def execute( class DrvBarsDailyOpt225ApiV2(BaseApi): """ - v2 `/derivatives/bars/daily/options/225` のラッパ。 + v2 の日経225オプション四本値 API (`/derivatives/bars/daily/options/225`) のラッパークラス。 v1 `/option/index_option` に対応する日経225オプション四本値エンドポイント。 """ diff --git a/jquantsapi/apis/v2/equities.py b/jquantsapi/apis/v2/equities.py index fe3df7c..8f5fa89 100644 --- a/jquantsapi/apis/v2/equities.py +++ b/jquantsapi/apis/v2/equities.py @@ -10,7 +10,7 @@ class EqMasterApiV2(BaseApi): """ - v2: eq-master (/equities/master) + v2 の銘柄マスタ API (`/equities/master`) のラッパークラス。 上場銘柄一覧 (v2) を取得します。 """ @@ -44,7 +44,7 @@ def execute(self, client: Any, code: str = "", date: str = "") -> pd.DataFrame: class EqBarsDailyApiV2(BaseApi): """ - v2 の株価四本値 API (`/equities/bars/daily`) を担当するクラス。 + v2 の株価四本値 API (`/equities/bars/daily`) のラッパークラス。 """ name = "eq_bars_daily" @@ -117,7 +117,7 @@ def execute( class EqBarsDailyAmApiV2(BaseApi): """ - v2 の前場四本値 API (`/equities/bars/daily/am`) を担当するクラス。 + v2 の前場四本値 API (`/equities/bars/daily/am`) のラッパークラス。 """ name = "eq_bars_daily_am" @@ -177,7 +177,7 @@ def execute( class EqBarsMinuteApiV2(BaseApi): """ - v2 の分足 API (`/equities/bars/minute`) を担当するクラス。 + v2 の分足 API (`/equities/bars/minute`) のラッパークラス。 """ name = "eq_bars_minute" @@ -251,7 +251,7 @@ def execute( class EqInvestorTypesApiV2(BaseApi): """ - v2 の投資部門別売買状況 API (`/equities/investor-types`) を担当するクラス。 + v2 の投資部門別売買状況 API (`/equities/investor-types`) のラッパークラス。 """ name = "eq_investor_types" @@ -320,9 +320,7 @@ def execute( class EqEarningsCalApiV2(BaseApi): """ - v2 の決算発表予定日 API (`/equities/earnings-calendar`) を担当するクラス。 - - 既存の `ClientV2.get_fins_announcement` のロジックをそのまま移植します。 + v2 の決算発表予定日 API (`/equities/earnings-calendar`) のラッパークラス。 """ name = "eq_earnings_cal" diff --git a/jquantsapi/apis/v2/fins.py b/jquantsapi/apis/v2/fins.py index bda51c6..7360152 100644 --- a/jquantsapi/apis/v2/fins.py +++ b/jquantsapi/apis/v2/fins.py @@ -10,9 +10,7 @@ class FinSummaryApiV2(BaseApi): """ - v2 の財務情報サマリ API (`/fins/summary`) を担当するクラス。 - - 既存の `ClientV2.get_fins_statements` のロジックをそのまま移植します。 + v2 の財務情報サマリ API (`/fins/summary`) のラッパークラス。 """ name = "fin_summary" @@ -80,9 +78,7 @@ def execute( class FinDetailsApiV2(BaseApi): """ - v2 の財務諸表詳細 API (`/fins/details`) を担当するクラス。 - - 既存の `ClientV2.get_fins_fs_details` のロジックをそのまま移植します。 + v2 の財務諸表詳細 API (`/fins/details`) のラッパークラス。 """ name = "fin_details" @@ -138,9 +134,7 @@ def execute( class FinDividendApiV2(BaseApi): """ - v2 の配当金情報 API (`/fins/dividend`) を担当するクラス。 - - 既存の `ClientV2.get_fins_dividend` のロジックをそのまま移植します。 + v2 の配当金情報 API (`/fins/dividend`) のラッパークラス。 """ name = "fin_dividend" diff --git a/jquantsapi/apis/v2/indices.py b/jquantsapi/apis/v2/indices.py index 376b68f..fd59801 100644 --- a/jquantsapi/apis/v2/indices.py +++ b/jquantsapi/apis/v2/indices.py @@ -9,7 +9,7 @@ class IdxBarsDailyApiV2(BaseApi): """ - v2 `/indices/bars/daily` のラッパ。 + v2 の指数四本値 API (`/indices/bars/daily`) のラッパークラス。 v1 `/indices` に対応する指数四本値エンドポイント。 """ @@ -55,7 +55,7 @@ def execute( class IdxBarsDailyTopixApiV2(BaseApi): """ - v2 `/indices/bars/daily/topix` のラッパ。 + v2 の TOPIX 指数四本値 API (`/indices/bars/daily/topix`) のラッパークラス。 v1 `/indices/topix` に対応する TOPIX 指数四本値エンドポイント。 """ diff --git a/jquantsapi/apis/v2/markets.py b/jquantsapi/apis/v2/markets.py index 2f8b606..e871874 100644 --- a/jquantsapi/apis/v2/markets.py +++ b/jquantsapi/apis/v2/markets.py @@ -10,9 +10,7 @@ class MktShortRatioApiV2(BaseApi): """ - v2 の業種別空売り比率 API (`/markets/short-ratio`) を担当するクラス。 - - 既存の `ClientV2.get_markets_short_selling` のロジックをそのまま移植します。 + v2 の業種別空売り比率 API (`/markets/short-ratio`) のラッパークラス。 """ name = "mkt_short_ratio" @@ -78,9 +76,7 @@ def execute( class MktShortSaleReportApiV2(BaseApi): """ - v2 の空売り残高報告 API (`/markets/short-sale-report`) を担当するクラス。 - - 既存の `ClientV2.get_markets_short_selling_positions` のロジックをそのまま移植します。 + v2 の空売り残高報告 API (`/markets/short-sale-report`) のラッパークラス。 """ name = "mkt_short_sale_report" @@ -146,7 +142,7 @@ def execute( class MktMarginInterestApiV2(BaseApi): """ - v2 の信用取引週末残高 API (`/markets/margin-interest`) を担当するクラス。 + v2 の信用取引週末残高 API (`/markets/margin-interest`) のラッパークラス。 """ name = "mkt_margin_interest" @@ -193,7 +189,7 @@ def execute( class MktBreakdownApiV2(BaseApi): """ - v2 の売買内訳 API (`/markets/breakdown`) を担当するクラス。 + v2 の売買内訳 API (`/markets/breakdown`) のラッパークラス。 """ name = "mkt_breakdown" @@ -243,7 +239,7 @@ def execute( class MktMarginAlertApiV2(BaseApi): """ - v2 の日々公表信用取引残高 API (`/markets/margin-alert`) を担当するクラス。 + v2 の日々公表信用取引残高 API (`/markets/margin-alert`) のラッパークラス。 """ name = "mkt_margin_alert" @@ -290,9 +286,7 @@ def execute( class MktCalendarApiV2(BaseApi): """ - v2 の取引カレンダー API (`/markets/calendar`) を担当するクラス。 - - 既存の `ClientV2.get_markets_trading_calendar` のロジックをそのまま移植します。 + v2 の取引カレンダー API (`/markets/calendar`) のラッパークラス。 """ name = "markets_trading_calendar" From 9a4f628dda74aa8c8e636408665300f7e510ee4e Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Tue, 13 Jan 2026 16:31:59 +0900 Subject: [PATCH 17/21] Fix as review --- jquantsapi/apis/v1/derivatives.py | 8 +- jquantsapi/apis/v1/fins.py | 26 +++--- jquantsapi/apis/v1/indices.py | 6 +- jquantsapi/apis/v1/listed.py | 6 +- jquantsapi/apis/v1/markets.py | 28 +++---- jquantsapi/apis/v1/prices.py | 14 ++-- jquantsapi/apis/v2/bulk.py | 6 +- jquantsapi/apis/v2/derivatives.py | 14 ++-- jquantsapi/apis/v2/equities.py | 135 +++++++----------------------- jquantsapi/apis/v2/fins.py | 77 ++++------------- jquantsapi/apis/v2/indices.py | 8 +- jquantsapi/apis/v2/markets.py | 60 ++++--------- jquantsapi/client.py | 6 +- jquantsapi/client_v2.py | 29 ++++--- 14 files changed, 139 insertions(+), 284 deletions(-) diff --git a/jquantsapi/apis/v1/derivatives.py b/jquantsapi/apis/v1/derivatives.py index 8d55241..ff946cc 100644 --- a/jquantsapi/apis/v1/derivatives.py +++ b/jquantsapi/apis/v1/derivatives.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import Any, Dict +from typing import Any import pandas as pd # type: ignore @@ -36,7 +36,7 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) + d: dict[str, Any] = json.loads(j) data = d["futures"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] @@ -84,7 +84,7 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) + d: dict[str, Any] = json.loads(j) data = d["options"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] @@ -124,7 +124,7 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) + d: dict[str, Any] = json.loads(j) data = d["index_option"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] diff --git a/jquantsapi/apis/v1/fins.py b/jquantsapi/apis/v1/fins.py index 9527002..a4acd61 100644 --- a/jquantsapi/apis/v1/fins.py +++ b/jquantsapi/apis/v1/fins.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import Any, Dict, List +from typing import Any import pandas as pd # type: ignore @@ -28,9 +28,9 @@ def execute( `/fins/statements` を実行し、財務情報を DataFrame で返す。 """ url = f"{client.JQUANTS_API_BASE}/fins/statements" # type: ignore[attr-defined] - params: Dict[str, Any] = {"code": code, "date": date_yyyymmdd} + params: dict[str, Any] = {"code": code, "date": date_yyyymmdd} - data: List[Dict[str, Any]] = [] + data: list[dict[str, Any]] = [] pagination_key: str = "" while True: req_params = dict(params) @@ -39,7 +39,7 @@ def execute( resp = client._get(url, req_params) # type: ignore[arg-type] resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] - d: Dict[str, Any] = json.loads(resp.text) + d: dict[str, Any] = json.loads(resp.text) page = d.get("statements", []) if isinstance(page, list): data.extend(page) @@ -93,9 +93,9 @@ def execute( `/fins/fs_details` を実行し、財務諸表(BS/PL)を DataFrame で返す。 """ url = f"{client.JQUANTS_API_BASE}/fins/fs_details" # type: ignore[attr-defined] - params: Dict[str, Any] = {"code": code, "date": date_yyyymmdd} + params: dict[str, Any] = {"code": code, "date": date_yyyymmdd} - data: List[Dict[str, Any]] = [] + data: list[dict[str, Any]] = [] pagination_key: str = "" while True: req_params = dict(params) @@ -104,7 +104,7 @@ def execute( resp = client._get(url, req_params) # type: ignore[arg-type] resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] - d: Dict[str, Any] = json.loads(resp.text) + d: dict[str, Any] = json.loads(resp.text) page = d.get("fs_details", []) if isinstance(page, list): data.extend(page) @@ -142,7 +142,7 @@ def execute( `/fins/dividend` を実行し、配当金情報を DataFrame で返す。 """ url = f"{client.JQUANTS_API_BASE}/fins/dividend" # type: ignore[attr-defined] - params: Dict[str, Any] = {"code": code} + params: dict[str, Any] = {"code": code} if date_yyyymmdd != "": params["date"] = date_yyyymmdd else: @@ -151,7 +151,7 @@ def execute( if to_yyyymmdd != "": params["to"] = to_yyyymmdd - data: List[Dict[str, Any]] = [] + data: list[dict[str, Any]] = [] pagination_key: str = "" while True: req_params = dict(params) @@ -160,7 +160,7 @@ def execute( resp = client._get(url, req_params) # type: ignore[arg-type] resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] - d: Dict[str, Any] = json.loads(resp.text) + d: dict[str, Any] = json.loads(resp.text) page = d.get("dividend", []) if isinstance(page, list): data.extend(page) @@ -195,9 +195,9 @@ def execute( `/fins/announcement` を実行し、決算発表予定データを DataFrame で返す。 """ url = f"{client.JQUANTS_API_BASE}/fins/announcement" # type: ignore[attr-defined] - params: Dict[str, Any] = {} + params: dict[str, Any] = {} - data: List[Dict[str, Any]] = [] + data: list[dict[str, Any]] = [] pagination_key: str = "" while True: req_params = dict(params) @@ -206,7 +206,7 @@ def execute( resp = client._get(url, req_params) # type: ignore[arg-type] resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] - d: Dict[str, Any] = json.loads(resp.text) + d: dict[str, Any] = json.loads(resp.text) page = d.get("announcement", []) if isinstance(page, list): data.extend(page) diff --git a/jquantsapi/apis/v1/indices.py b/jquantsapi/apis/v1/indices.py index 0c6b48c..ba8e51d 100644 --- a/jquantsapi/apis/v1/indices.py +++ b/jquantsapi/apis/v1/indices.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import Any, Dict +from typing import Any import pandas as pd # type: ignore @@ -40,7 +40,7 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) + d: dict[str, Any] = json.loads(j) data = d["indices"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] @@ -85,7 +85,7 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) + d: dict[str, Any] = json.loads(j) data = d["topix"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] diff --git a/jquantsapi/apis/v1/listed.py b/jquantsapi/apis/v1/listed.py index c704943..0090ef0 100644 --- a/jquantsapi/apis/v1/listed.py +++ b/jquantsapi/apis/v1/listed.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import Any, Dict, List +from typing import Any import pandas as pd # type: ignore @@ -35,8 +35,8 @@ def execute( url = f"{client.JQUANTS_API_BASE}/listed/info" # ページングしながら全件取得 - all_info: List[Dict[str, Any]] = [] - base_params: Dict[str, Any] = {} + all_info: list[dict[str, Any]] = [] + base_params: dict[str, Any] = {} if code != "": base_params["code"] = code if date_yyyymmdd != "": diff --git a/jquantsapi/apis/v1/markets.py b/jquantsapi/apis/v1/markets.py index 8144e72..f7b8250 100644 --- a/jquantsapi/apis/v1/markets.py +++ b/jquantsapi/apis/v1/markets.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import Any, Dict, List, Union +from typing import Any, Union import pandas as pd # type: ignore @@ -47,8 +47,8 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) - data: List[Dict[str, Any]] = d["trades_spec"] + d: dict[str, Any] = json.loads(j) + data: list[dict[str, Any]] = d["trades_spec"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] ret = client._get(url, params) # type: ignore[attr-defined] @@ -101,8 +101,8 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) - data: List[Dict[str, Any]] = d["weekly_margin_interest"] + d: dict[str, Any] = json.loads(j) + data: list[dict[str, Any]] = d["weekly_margin_interest"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] ret = client._get(url, params) # type: ignore[attr-defined] @@ -151,7 +151,7 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) + d: dict[str, Any] = json.loads(j) df = pd.DataFrame.from_dict(d["trading_calendar"]) cols = constants.MARKETS_TRADING_CALENDAR if len(df) == 0: @@ -195,8 +195,8 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) - data: List[Dict[str, Any]] = d["short_selling"] + d: dict[str, Any] = json.loads(j) + data: list[dict[str, Any]] = d["short_selling"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] ret = client._get(url, params) # type: ignore[attr-defined] @@ -247,8 +247,8 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) - data: List[Dict[str, Any]] = d["breakdown"] + d: dict[str, Any] = json.loads(j) + data: list[dict[str, Any]] = d["breakdown"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] ret = client._get(url, params) # type: ignore[attr-defined] @@ -303,8 +303,8 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) - data: List[Dict[str, Any]] = d["short_selling_positions"] + d: dict[str, Any] = json.loads(j) + data: list[dict[str, Any]] = d["short_selling_positions"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] ret = client._get(url, params) # type: ignore[attr-defined] @@ -365,8 +365,8 @@ def execute( ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text - d: Dict[str, Any] = json.loads(j) - data: List[Dict[str, Any]] = d["daily_margin_interest"] + d: dict[str, Any] = json.loads(j) + data: list[dict[str, Any]] = d["daily_margin_interest"] while "pagination_key" in d: params["pagination_key"] = d["pagination_key"] ret = client._get(url, params) # type: ignore[attr-defined] diff --git a/jquantsapi/apis/v1/prices.py b/jquantsapi/apis/v1/prices.py index e25d0f3..754d684 100644 --- a/jquantsapi/apis/v1/prices.py +++ b/jquantsapi/apis/v1/prices.py @@ -1,7 +1,7 @@ from __future__ import annotations import json -from typing import Any, Dict, List +from typing import Any import pandas as pd # type: ignore @@ -37,7 +37,7 @@ def execute( date_yyyymmdd: 取得日 """ url = f"{client.JQUANTS_API_BASE}/prices/daily_quotes" # type: ignore[attr-defined] - params: Dict[str, Any] = {"code": code} + params: dict[str, Any] = {"code": code} if date_yyyymmdd != "": params["date"] = date_yyyymmdd else: @@ -46,7 +46,7 @@ def execute( if to_yyyymmdd != "": params["to"] = to_yyyymmdd - data: List[Dict[str, Any]] = [] + data: list[dict[str, Any]] = [] pagination_key: str = "" while True: req_params = dict(params) @@ -55,7 +55,7 @@ def execute( resp = client._get(url, req_params) # type: ignore[arg-type] resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] - d: Dict[str, Any] = json.loads(resp.text) + d: dict[str, Any] = json.loads(resp.text) page = d.get("daily_quotes", []) if isinstance(page, list): data.extend(page) @@ -101,14 +101,14 @@ def execute( code: issue code (e.g. 27800 or 2780) """ url = f"{client.JQUANTS_API_BASE}/prices/prices_am" # type: ignore[attr-defined] - params: Dict[str, Any] = {"code": code} + params: dict[str, Any] = {"code": code} resp = client._get(url, params) # type: ignore[arg-type] resp.encoding = client.RAW_ENCODING # type: ignore[attr-defined] - d: Dict[str, Any] = json.loads(resp.text) + d: dict[str, Any] = json.loads(resp.text) if d.get("message"): return d["message"] # type: ignore[return-value] - data: List[Dict[str, Any]] = d.get("prices_am", []) + data: list[dict[str, Any]] = d.get("prices_am", []) while "pagination_key" in d: req_params = dict(params) req_params["pagination_key"] = d["pagination_key"] diff --git a/jquantsapi/apis/v2/bulk.py b/jquantsapi/apis/v2/bulk.py index 5f67cd4..0654bfd 100644 --- a/jquantsapi/apis/v2/bulk.py +++ b/jquantsapi/apis/v2/bulk.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, Union +from typing import Any, Union import pandas as pd # type: ignore @@ -37,7 +37,7 @@ def execute( # BulkEndpointの場合はvalue(str)を取得 endpoint_str = endpoint.value if isinstance(endpoint, BulkEndpoint) else endpoint - params: Dict[str, Any] = {"endpoint": endpoint_str} + params: dict[str, Any] = {"endpoint": endpoint_str} resp = client._get(url, params) # type: ignore[arg-type] payload = resp.json() @@ -83,7 +83,7 @@ def execute( """ url = f"{client.JQUANTS_API_BASE}/bulk/get" - params: Dict[str, Any] = {"key": key} + params: dict[str, Any] = {"key": key} resp = client._get(url, params) # type: ignore[arg-type] payload = resp.json() diff --git a/jquantsapi/apis/v2/derivatives.py b/jquantsapi/apis/v2/derivatives.py index ad7e020..9508f8d 100644 --- a/jquantsapi/apis/v2/derivatives.py +++ b/jquantsapi/apis/v2/derivatives.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List +from typing import Any import pandas as pd # type: ignore @@ -25,7 +25,7 @@ def execute( category: str = "", contract_flag: str = "", ) -> pd.DataFrame: - params: Dict[str, Any] = {"date": date_yyyymmdd} + params: dict[str, Any] = {"date": date_yyyymmdd} if category: params["category"] = category if contract_flag: @@ -40,7 +40,7 @@ def execute( df = pd.DataFrame.from_records(data) if "Date" in df.columns: df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - sort_cols: List[str] = [] + sort_cols: list[str] = [] if "Code" in df.columns: sort_cols.append("Code") if "Date" in df.columns: @@ -69,7 +69,7 @@ def execute( contract_flag: str = "", code: str = "", ) -> pd.DataFrame: - params: Dict[str, Any] = {"date": date_yyyymmdd} + params: dict[str, Any] = {"date": date_yyyymmdd} if category: params["category"] = category if contract_flag: @@ -87,7 +87,7 @@ def execute( df = pd.DataFrame.from_records(data) if "Date" in df.columns: df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - sort_cols: List[str] = [] + sort_cols: list[str] = [] if "Code" in df.columns: sort_cols.append("Code") if "Date" in df.columns: @@ -113,7 +113,7 @@ def execute( *, date_yyyymmdd: str, ) -> pd.DataFrame: - params: Dict[str, Any] = {"date": date_yyyymmdd} + params: dict[str, Any] = {"date": date_yyyymmdd} data = client._get_paginated( # type: ignore[attr-defined] "/derivatives/bars/daily/options/225", @@ -125,7 +125,7 @@ def execute( df = pd.DataFrame.from_records(data) if "Date" in df.columns: df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - sort_cols: List[str] = [] + sort_cols: list[str] = [] if "Code" in df.columns: sort_cols.append("Code") if "Date" in df.columns: diff --git a/jquantsapi/apis/v2/equities.py b/jquantsapi/apis/v2/equities.py index 8f5fa89..790177d 100644 --- a/jquantsapi/apis/v2/equities.py +++ b/jquantsapi/apis/v2/equities.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List +from typing import Any import pandas as pd # type: ignore @@ -19,14 +19,14 @@ class EqMasterApiV2(BaseApi): version = "v2" def execute(self, client: Any, code: str = "", date: str = "") -> pd.DataFrame: - params: Dict[str, str] = {} + params: dict[str, str] = {} if code: params["code"] = code if date: params["date"] = date # ClientV2 の _get_paginated を利用する - data: List[Dict[str, Any]] = client._get_paginated( # type: ignore[attr-defined] + data: list[dict[str, Any]] = client._get_paginated( # type: ignore[attr-defined] "/equities/master", params=params, data_key="data" ) cols = constants.EQ_MASTER_COLUMNS_V2 @@ -69,9 +69,7 @@ def execute( to_yyyymmdd: 取得終了日 date_yyyymmdd: 取得日 """ - url = f"{client.JQUANTS_API_BASE}/equities/bars/daily" - - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if date_yyyymmdd: @@ -82,24 +80,10 @@ def execute( if to_yyyymmdd: params["to"] = to_yyyymmdd - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/equities/bars/daily", + params=params, + ) if not all_data: return pd.DataFrame() @@ -136,30 +120,14 @@ def execute( client: v2 `ClientV2` インスタンスを想定 code: 銘柄コード (5桁 or 4桁)。空文字の場合は全銘柄。 """ - url = f"{client.JQUANTS_API_BASE}/equities/bars/daily/am" - - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/equities/bars/daily/am", + params=params, + ) if not all_data: return pd.DataFrame() @@ -202,9 +170,7 @@ def execute( to_yyyymmdd: 取得終了日 date_yyyymmdd: 取得日 """ - url = f"{client.JQUANTS_API_BASE}/equities/bars/minute" - - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if date_yyyymmdd: @@ -215,24 +181,10 @@ def execute( if to_yyyymmdd: params["to"] = to_yyyymmdd - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/equities/bars/minute", + params=params, + ) cols = constants.EQ_BARS_MINUTE_COLUMNS_V2 if not all_data: @@ -274,9 +226,7 @@ def execute( from_yyyymmdd: 期間開始日 to_yyyymmdd: 期間終了日 """ - url = f"{client.JQUANTS_API_BASE}/equities/investor-types" - - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if section: params["section"] = section if from_yyyymmdd: @@ -284,24 +234,10 @@ def execute( if to_yyyymmdd: params["to"] = to_yyyymmdd - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/equities/investor-types", + params=params, + ) if not all_data: return pd.DataFrame() @@ -333,27 +269,12 @@ def execute( """ `/equities/earnings-calendar` を実行し、決算発表予定データを DataFrame で返す。 """ - url = f"{client.JQUANTS_API_BASE}/equities/earnings-calendar" - - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - params: Dict[str, Any] = {} - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key + params: dict[str, Any] = {} - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/equities/earnings-calendar", + params=params, + ) if not all_data: return pd.DataFrame() diff --git a/jquantsapi/apis/v2/fins.py b/jquantsapi/apis/v2/fins.py index 7360152..27f26bb 100644 --- a/jquantsapi/apis/v2/fins.py +++ b/jquantsapi/apis/v2/fins.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List +from typing import Any import pandas as pd # type: ignore @@ -26,31 +26,16 @@ def execute( """ `/fins/summary` を実行し、財務情報サマリを DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if date_yyyymmdd: params["date"] = date_yyyymmdd - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - url = f"{client.JQUANTS_API_BASE}/fins/summary" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/fins/summary", + params=params, + ) if not all_data: return pd.DataFrame() @@ -94,31 +79,16 @@ def execute( """ `/fins/details` を実行し、財務諸表詳細を DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if date_yyyymmdd: params["date"] = date_yyyymmdd - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - url = f"{client.JQUANTS_API_BASE}/fins/details" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/fins/details", + params=params, + ) if not all_data: return pd.DataFrame() @@ -152,7 +122,7 @@ def execute( """ `/fins/dividend` を実行し、配当金情報を DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if date_yyyymmdd: @@ -163,25 +133,10 @@ def execute( if to_yyyymmdd: params["to"] = to_yyyymmdd - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - url = f"{client.JQUANTS_API_BASE}/fins/dividend" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/fins/dividend", + params=params, + ) if not all_data: return pd.DataFrame() diff --git a/jquantsapi/apis/v2/indices.py b/jquantsapi/apis/v2/indices.py index fd59801..ffffeb8 100644 --- a/jquantsapi/apis/v2/indices.py +++ b/jquantsapi/apis/v2/indices.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List +from typing import Any import pandas as pd # type: ignore @@ -26,7 +26,7 @@ def execute( to_yyyymmdd: str = "", date_yyyymmdd: str = "", ) -> pd.DataFrame: - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code # v1 と同様: date があれば date 優先、なければ from/to @@ -45,7 +45,7 @@ def execute( df = pd.DataFrame.from_records(data) if "Date" in df.columns: df["Date"] = pd.to_datetime(df["Date"], errors="coerce") - sort_cols: List[str] = [] + sort_cols: list[str] = [] if "Code" in df.columns: sort_cols.append("Code") sort_cols.append("Date") @@ -70,7 +70,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", ) -> pd.DataFrame: - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if from_yyyymmdd: params["from"] = from_yyyymmdd if to_yyyymmdd: diff --git a/jquantsapi/apis/v2/markets.py b/jquantsapi/apis/v2/markets.py index e871874..c5ae2eb 100644 --- a/jquantsapi/apis/v2/markets.py +++ b/jquantsapi/apis/v2/markets.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List +from typing import Any import pandas as pd # type: ignore @@ -28,7 +28,7 @@ def execute( """ `/markets/short-ratio` を実行し、業種別空売り比率データを DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if sector_33_code: params["s33"] = sector_33_code if date_yyyymmdd: @@ -39,25 +39,10 @@ def execute( if to_yyyymmdd: params["to"] = to_yyyymmdd - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - url = f"{client.JQUANTS_API_BASE}/markets/short-ratio" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/markets/short-ratio", + params=params, + ) if not all_data: return pd.DataFrame() @@ -95,7 +80,7 @@ def execute( """ `/markets/short-sale-report` を実行し、空売り残高報告データを DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if disclosed_date: @@ -107,25 +92,10 @@ def execute( if calculated_date: params["calc_date"] = calculated_date - all_data: List[Dict[str, Any]] = [] - pagination_key = "" - url = f"{client.JQUANTS_API_BASE}/markets/short-sale-report" - - while True: - req_params = dict(params) - if pagination_key: - req_params["pagination_key"] = pagination_key - - resp = client._get(url, req_params) # type: ignore[arg-type] - payload = resp.json() - - batch = payload.get("data", []) - if isinstance(batch, list): - all_data.extend(batch) - - pagination_key = payload.get("pagination_key", "") - if not pagination_key: - break + all_data = client._get_paginated( # type: ignore[attr-defined] + "/markets/short-sale-report", + params=params, + ) if not all_data: return pd.DataFrame() @@ -160,7 +130,7 @@ def execute( """ `/markets/margin-interest` を実行し、信用取引週末残高を DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if date_yyyymmdd: @@ -207,7 +177,7 @@ def execute( """ `/markets/breakdown` を実行し、売買内訳データを DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if date_yyyymmdd: @@ -257,7 +227,7 @@ def execute( """ `/markets/margin-alert` を実行し、日々公表信用取引残高を DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if code: params["code"] = code if date_yyyymmdd: @@ -303,7 +273,7 @@ def execute( """ `/markets/calendar` を実行し、取引カレンダーデータを DataFrame で返す。 """ - params: Dict[str, Any] = {} + params: dict[str, Any] = {} if holiday_division: params["hol_div"] = holiday_division if from_yyyymmdd: diff --git a/jquantsapi/client.py b/jquantsapi/client.py index 6bb4776..fee0dac 100644 --- a/jquantsapi/client.py +++ b/jquantsapi/client.py @@ -5,7 +5,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime from pathlib import Path -from typing import Any, List, Mapping, Optional, Union +from typing import Any, Mapping, Optional, Union import pandas as pd # type: ignore import requests @@ -229,8 +229,8 @@ def _base_headers(self) -> dict: def _request_session( self, - status_forcelist: Optional[List[int]] = None, - allowed_methods: Optional[List[str]] = None, + status_forcelist: Optional[list[int]] = None, + allowed_methods: Optional[list[str]] = None, ) -> requests.Session: """ requests の session 取得 diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py index a2391cf..57c624e 100644 --- a/jquantsapi/client_v2.py +++ b/jquantsapi/client_v2.py @@ -4,7 +4,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import datetime from pathlib import Path -from typing import Any, Dict, List, Optional, Union +from typing import Any, Optional, Union import pandas as pd # type: ignore import requests @@ -181,8 +181,8 @@ def _read_config(self, config_path: str) -> dict: def _request_session( self, - status_forcelist: Optional[List[int]] = None, - allowed_methods: Optional[List[str]] = None, + status_forcelist: Optional[list[int]] = None, + allowed_methods: Optional[list[str]] = None, ) -> requests.Session: """ requests の session を取得し、リトライ設定を行う @@ -208,7 +208,7 @@ def _request_session( return self._session - def _base_headers(self) -> Dict[str, str]: + def _base_headers(self) -> dict[str, str]: """ J-Quants API v2 にアクセスする際の共通ヘッダを生成 """ @@ -218,7 +218,7 @@ def _base_headers(self) -> Dict[str, str]: f"p/{platform.python_version()}", } - def _get(self, url: str, params: Optional[Dict[str, Any]] = None) -> requests.Response: + def _get(self, url: str, params: Optional[dict[str, Any]] = None) -> requests.Response: """ GET リクエスト用ラッパー """ @@ -231,9 +231,9 @@ def _get(self, url: str, params: Optional[Dict[str, Any]] = None) -> requests.Re def _get_paginated( self, path: str, - params: Optional[Dict[str, Any]] = None, + params: Optional[dict[str, Any]] = None, data_key: str = "data", - ) -> List[Dict[str, Any]]: + ) -> list[dict[str, Any]]: """ pagination_key に対応した共通 GET ヘルパー @@ -245,8 +245,8 @@ def _get_paginated( List[Dict[str, Any]]: 連結済みのデータ配列 """ url = f"{self.JQUANTS_API_BASE}{path}" - all_data: List[Dict[str, Any]] = [] - query: Dict[str, Any] = dict(params or {}) + all_data: list[dict[str, Any]] = [] + query: dict[str, Any] = dict(params or {}) while True: resp = self._get(url, params=query) @@ -1313,12 +1313,21 @@ def download_bulk(self, key: str, output_path: str) -> None: Args: key: get_bulk_listで取得したKey output_path: ダウンロードファイルの保存先パス + + Raises: + ValueError: output_path が空文字列の場合 """ + # バリデーション + if not output_path or not output_path.strip(): + raise ValueError("output_path must not be empty") + # ダウンロード URL を取得 url = self._bulk_get_api.execute(self, key=key) # ディレクトリが存在しない場合は作成 - os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True) + output_dir = os.path.dirname(os.path.abspath(output_path)) + if output_dir: + os.makedirs(output_dir, exist_ok=True) # ファイルをダウンロード session = self._request_session() From bdf856ecc9afa2e741605a9e01f0d88b4f8ca212 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Mon, 19 Jan 2026 14:01:58 +0900 Subject: [PATCH 18/21] Update pandas dependencies --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 35d6b57..f9e3f5c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ build-backend = "poetry_dynamic_versioning.backend" [tool.poetry.dependencies] python = "^3.10" requests = "^2.28.0" -pandas = ">=1.4.3" +pandas = "^2.2.0" numpy = ">=1.22.4" tomli = { version = "^2.0.1", python = "<3.11" } typing_extensions = { version = "^4.5.0", python = "<3.13" } From 43b946cbb6748dfc744957b80d98b619b29e9142 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Mon, 19 Jan 2026 14:06:19 +0900 Subject: [PATCH 19/21] Update poetry.lock --- poetry.lock | 137 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 85 insertions(+), 52 deletions(-) diff --git a/poetry.lock b/poetry.lock index f8388bb..7eb24ff 100644 --- a/poetry.lock +++ b/poetry.lock @@ -562,70 +562,103 @@ files = [ [[package]] name = "pandas" -version = "2.0.3" +version = "2.3.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8"}, - {file = "pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f"}, - {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183"}, - {file = "pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0"}, - {file = "pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210"}, - {file = "pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e"}, - {file = "pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8"}, - {file = "pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26"}, - {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d"}, - {file = "pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df"}, - {file = "pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd"}, - {file = "pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b"}, - {file = "pandas-2.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9e4da0d45e7f34c069fe4d522359df7d23badf83abc1d1cef398895822d11061"}, - {file = "pandas-2.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:32fca2ee1b0d93dd71d979726b12b61faa06aeb93cf77468776287f41ff8fdc5"}, - {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:258d3624b3ae734490e4d63c430256e716f488c4fcb7c8e9bde2d3aa46c29089"}, - {file = "pandas-2.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eae3dc34fa1aa7772dd3fc60270d13ced7346fcbcfee017d3132ec625e23bb0"}, - {file = "pandas-2.0.3-cp38-cp38-win32.whl", hash = "sha256:f3421a7afb1a43f7e38e82e844e2bca9a6d793d66c1a7f9f0ff39a795bbc5e02"}, - {file = "pandas-2.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:69d7f3884c95da3a31ef82b7618af5710dba95bb885ffab339aad925c3e8ce78"}, - {file = "pandas-2.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5247fb1ba347c1261cbbf0fcfba4a3121fbb4029d95d9ef4dc45406620b25c8b"}, - {file = "pandas-2.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81af086f4543c9d8bb128328b5d32e9986e0c84d3ee673a2ac6fb57fd14f755e"}, - {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1994c789bf12a7c5098277fb43836ce090f1073858c10f9220998ac74f37c69b"}, - {file = "pandas-2.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ec591c48e29226bcbb316e0c1e9423622bc7a4eaf1ef7c3c9fa1a3981f89641"}, - {file = "pandas-2.0.3-cp39-cp39-win32.whl", hash = "sha256:04dbdbaf2e4d46ca8da896e1805bc04eb85caa9a82e259e8eed00254d5e0c682"}, - {file = "pandas-2.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:1168574b036cd8b93abc746171c9b4f1b83467438a5e45909fed645cf8692dbc"}, - {file = "pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c"}, + {file = "pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c"}, + {file = "pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a"}, + {file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1"}, + {file = "pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838"}, + {file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250"}, + {file = "pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4"}, + {file = "pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826"}, + {file = "pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523"}, + {file = "pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45"}, + {file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66"}, + {file = "pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b"}, + {file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791"}, + {file = "pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151"}, + {file = "pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c"}, + {file = "pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53"}, + {file = "pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35"}, + {file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908"}, + {file = "pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89"}, + {file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98"}, + {file = "pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084"}, + {file = "pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b"}, + {file = "pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713"}, + {file = "pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8"}, + {file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d"}, + {file = "pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac"}, + {file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c"}, + {file = "pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493"}, + {file = "pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee"}, + {file = "pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5"}, + {file = "pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21"}, + {file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78"}, + {file = "pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110"}, + {file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86"}, + {file = "pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc"}, + {file = "pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0"}, + {file = "pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593"}, + {file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c"}, + {file = "pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b"}, + {file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6"}, + {file = "pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3"}, + {file = "pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5"}, + {file = "pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec"}, + {file = "pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7"}, + {file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450"}, + {file = "pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5"}, + {file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788"}, + {file = "pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87"}, + {file = "pandas-2.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c503ba5216814e295f40711470446bc3fd00f0faea8a086cbc688808e26f92a2"}, + {file = "pandas-2.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a637c5cdfa04b6d6e2ecedcb81fc52ffb0fd78ce2ebccc9ea964df9f658de8c8"}, + {file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:854d00d556406bffe66a4c0802f334c9ad5a96b4f1f868adf036a21b11ef13ff"}, + {file = "pandas-2.3.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf1f8a81d04ca90e32a0aceb819d34dbd378a98bf923b6398b9a3ec0bf44de29"}, + {file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:23ebd657a4d38268c7dfbdf089fbc31ea709d82e4923c5ffd4fbd5747133ce73"}, + {file = "pandas-2.3.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5554c929ccc317d41a5e3d1234f3be588248e61f08a74dd17c9eabb535777dc9"}, + {file = "pandas-2.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:d3e28b3e83862ccf4d85ff19cf8c20b2ae7e503881711ff2d534dc8f761131aa"}, + {file = "pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b"}, ] [package.dependencies] numpy = [ - {version = ">=1.23.2", markers = "python_version >= \"3.11\""}, - {version = ">=1.21.0", markers = "python_version == \"3.10\""}, + {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" -tzdata = ">=2022.1" +tzdata = ">=2022.7" [package.extras] -all = ["PyQt5 (>=5.15.1)", "SQLAlchemy (>=1.4.16)", "beautifulsoup4 (>=4.9.3)", "bottleneck (>=1.3.2)", "brotlipy (>=0.7.0)", "fastparquet (>=0.6.3)", "fsspec (>=2021.07.0)", "gcsfs (>=2021.07.0)", "html5lib (>=1.1)", "hypothesis (>=6.34.2)", "jinja2 (>=3.0.0)", "lxml (>=4.6.3)", "matplotlib (>=3.6.1)", "numba (>=0.53.1)", "numexpr (>=2.7.3)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pandas-gbq (>=0.15.0)", "psycopg2 (>=2.8.6)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "python-snappy (>=0.6.0)", "pyxlsb (>=1.0.8)", "qtpy (>=2.2.0)", "s3fs (>=2021.08.0)", "scipy (>=1.7.1)", "tables (>=3.6.1)", "tabulate (>=0.8.9)", "xarray (>=0.21.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)", "zstandard (>=0.15.2)"] -aws = ["s3fs (>=2021.08.0)"] -clipboard = ["PyQt5 (>=5.15.1)", "qtpy (>=2.2.0)"] -compression = ["brotlipy (>=0.7.0)", "python-snappy (>=0.6.0)", "zstandard (>=0.15.2)"] -computation = ["scipy (>=1.7.1)", "xarray (>=0.21.0)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.7)", "pyxlsb (>=1.0.8)", "xlrd (>=2.0.1)", "xlsxwriter (>=1.4.3)"] -feather = ["pyarrow (>=7.0.0)"] -fss = ["fsspec (>=2021.07.0)"] -gcp = ["gcsfs (>=2021.07.0)", "pandas-gbq (>=0.15.0)"] -hdf5 = ["tables (>=3.6.1)"] -html = ["beautifulsoup4 (>=4.9.3)", "html5lib (>=1.1)", "lxml (>=4.6.3)"] -mysql = ["SQLAlchemy (>=1.4.16)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.0.0)", "tabulate (>=0.8.9)"] -parquet = ["pyarrow (>=7.0.0)"] -performance = ["bottleneck (>=1.3.2)", "numba (>=0.53.1)", "numexpr (>=2.7.1)"] -plot = ["matplotlib (>=3.6.1)"] -postgresql = ["SQLAlchemy (>=1.4.16)", "psycopg2 (>=2.8.6)"] -spss = ["pyreadstat (>=1.1.2)"] -sql-other = ["SQLAlchemy (>=1.4.16)"] -test = ["hypothesis (>=6.34.2)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.6.3)"] +all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] +aws = ["s3fs (>=2022.11.0)"] +clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] +compression = ["zstandard (>=0.19.0)"] +computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] +consortium-standard = ["dataframe-api-compat (>=0.1.7)"] +excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] +feather = ["pyarrow (>=10.0.1)"] +fss = ["fsspec (>=2022.11.0)"] +gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] +hdf5 = ["tables (>=3.8.0)"] +html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] +mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] +output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] +parquet = ["pyarrow (>=10.0.1)"] +performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] +plot = ["matplotlib (>=3.6.3)"] +postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] +spss = ["pyreadstat (>=1.2.0)"] +sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] +test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] +xml = ["lxml (>=4.9.2)"] [[package]] name = "pathspec" @@ -915,4 +948,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "5dd3e22e6b2b1d7d371b78e76d43bfadea12775369f79a5b0b8c7763c9db867e" +content-hash = "a74deb08794928259592a46c40adc0f0c9549deb3b52e46ec4d2d978e67b3560" From 11cde466211fd3f937f0f603e7966fdbdb6618b4 Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Mon, 19 Jan 2026 14:11:17 +0900 Subject: [PATCH 20/21] Execute lint --- jquantsapi/apis/__init__.py | 2 - jquantsapi/apis/base.py | 6 +-- jquantsapi/apis/v1/__init__.py | 2 - jquantsapi/apis/v1/derivatives.py | 7 ++- jquantsapi/apis/v1/fins.py | 1 - jquantsapi/apis/v1/indices.py | 5 +- jquantsapi/apis/v1/listed.py | 1 - jquantsapi/apis/v1/markets.py | 15 +++--- jquantsapi/apis/v1/prices.py | 1 - jquantsapi/apis/v2/__init__.py | 2 - jquantsapi/apis/v2/bulk.py | 5 +- jquantsapi/apis/v2/derivatives.py | 1 - jquantsapi/apis/v2/equities.py | 1 - jquantsapi/apis/v2/fins.py | 1 - jquantsapi/apis/v2/indices.py | 1 - jquantsapi/apis/v2/markets.py | 1 - jquantsapi/client.py | 31 ++++++----- jquantsapi/client_v2.py | 85 ++++++++++++++++--------------- tests/test_client_v2.py | 19 ++++--- 19 files changed, 91 insertions(+), 96 deletions(-) diff --git a/jquantsapi/apis/__init__.py b/jquantsapi/apis/__init__.py index 5f83808..5b9124f 100644 --- a/jquantsapi/apis/__init__.py +++ b/jquantsapi/apis/__init__.py @@ -7,5 +7,3 @@ """ from .base import BaseApi # noqa: F401 - - diff --git a/jquantsapi/apis/base.py b/jquantsapi/apis/base.py index e6d2d1f..ca53463 100644 --- a/jquantsapi/apis/base.py +++ b/jquantsapi/apis/base.py @@ -18,7 +18,9 @@ class SupportsRequest(Protocol): JQUANTS_API_BASE: str RAW_ENCODING: str - def _get(self, url: str, params: dict[str, Any] | None = None): # pragma: no cover - Protocol 定義のみ + def _get( + self, url: str, params: dict[str, Any] | None = None + ): # pragma: no cover - Protocol 定義のみ ... @@ -43,5 +45,3 @@ def execute(self, client: SupportsRequest, **params: Any) -> pd.DataFrame: client: HTTP リクエストなどを実行するクライアント **params: API ごとのパラメータ """ - - diff --git a/jquantsapi/apis/v1/__init__.py b/jquantsapi/apis/v1/__init__.py index 0c46cd0..447e585 100644 --- a/jquantsapi/apis/v1/__init__.py +++ b/jquantsapi/apis/v1/__init__.py @@ -1,5 +1,3 @@ """ J-Quants API v1 向けの各種 Api 実装。 """ - - diff --git a/jquantsapi/apis/v1/derivatives.py b/jquantsapi/apis/v1/derivatives.py index ff946cc..f94b1e8 100644 --- a/jquantsapi/apis/v1/derivatives.py +++ b/jquantsapi/apis/v1/derivatives.py @@ -32,7 +32,7 @@ def execute( "date": date_yyyymmdd, "contract_flag": contract_flag, } - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -80,7 +80,7 @@ def execute( "contract_flag": contract_flag, "code": code, } - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -120,7 +120,7 @@ def execute( # 元の _get_option_index_option_raw の実装を統合 url = f"{client.JQUANTS_API_BASE}/option/index_option" # type: ignore[attr-defined] params = {"date": date_yyyymmdd} - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -141,4 +141,3 @@ def execute( df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") df.sort_values(["Code"], inplace=True) return df[cols] - diff --git a/jquantsapi/apis/v1/fins.py b/jquantsapi/apis/v1/fins.py index a4acd61..2dda639 100644 --- a/jquantsapi/apis/v1/fins.py +++ b/jquantsapi/apis/v1/fins.py @@ -221,4 +221,3 @@ def execute( df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") df.sort_values(["Date", "Code"], inplace=True) return df[cols] - diff --git a/jquantsapi/apis/v1/indices.py b/jquantsapi/apis/v1/indices.py index ba8e51d..357e99b 100644 --- a/jquantsapi/apis/v1/indices.py +++ b/jquantsapi/apis/v1/indices.py @@ -36,7 +36,7 @@ def execute( params["from"] = from_yyyymmdd if to_yyyymmdd != "": params["to"] = to_yyyymmdd - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -81,7 +81,7 @@ def execute( params["from"] = from_yyyymmdd if to_yyyymmdd != "": params["to"] = to_yyyymmdd - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -102,4 +102,3 @@ def execute( df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") df.sort_values(["Date"], inplace=True) return df[cols] - diff --git a/jquantsapi/apis/v1/listed.py b/jquantsapi/apis/v1/listed.py index 0090ef0..24c1092 100644 --- a/jquantsapi/apis/v1/listed.py +++ b/jquantsapi/apis/v1/listed.py @@ -72,4 +72,3 @@ def execute( df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") df.sort_values("Code", inplace=True) return df[cols].reset_index(drop=True) - diff --git a/jquantsapi/apis/v1/markets.py b/jquantsapi/apis/v1/markets.py index f7b8250..0081eb6 100644 --- a/jquantsapi/apis/v1/markets.py +++ b/jquantsapi/apis/v1/markets.py @@ -43,7 +43,7 @@ def execute( params["from"] = from_yyyymmdd if to_yyyymmdd != "": params["to"] = to_yyyymmdd - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -97,7 +97,7 @@ def execute( params["from"] = from_yyyymmdd if to_yyyymmdd != "": params["to"] = to_yyyymmdd - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -147,7 +147,7 @@ def execute( params["from"] = from_yyyymmdd if to_yyyymmdd != "": params["to"] = to_yyyymmdd - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -191,7 +191,7 @@ def execute( params["from"] = from_yyyymmdd if to_yyyymmdd != "": params["to"] = to_yyyymmdd - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -243,7 +243,7 @@ def execute( params["from"] = from_yyyymmdd if to_yyyymmdd != "": params["to"] = to_yyyymmdd - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -299,7 +299,7 @@ def execute( params["disclosed_date_to"] = disclosed_date_to if calculated_date != "": params["calculated_date"] = calculated_date - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -361,7 +361,7 @@ def execute( params["from"] = from_yyyymmdd if to_yyyymmdd != "": params["to"] = to_yyyymmdd - + ret = client._get(url, params) # type: ignore[attr-defined] ret.encoding = client.RAW_ENCODING # type: ignore[attr-defined] j = ret.text @@ -382,4 +382,3 @@ def execute( df["ApplicationDate"] = pd.to_datetime(df["ApplicationDate"], format="%Y-%m-%d") df.sort_values(["Code", "PublishedDate"], inplace=True) return df[cols] - diff --git a/jquantsapi/apis/v1/prices.py b/jquantsapi/apis/v1/prices.py index 754d684..7138bde 100644 --- a/jquantsapi/apis/v1/prices.py +++ b/jquantsapi/apis/v1/prices.py @@ -123,4 +123,3 @@ def execute( df["Date"] = pd.to_datetime(df["Date"], format="%Y-%m-%d") df.sort_values(["Code"], inplace=True) return df[cols] - diff --git a/jquantsapi/apis/v2/__init__.py b/jquantsapi/apis/v2/__init__.py index 1f8d3a5..99500d1 100644 --- a/jquantsapi/apis/v2/__init__.py +++ b/jquantsapi/apis/v2/__init__.py @@ -1,5 +1,3 @@ """ v2 用 API クラス """ - - diff --git a/jquantsapi/apis/v2/bulk.py b/jquantsapi/apis/v2/bulk.py index 0654bfd..aa9814a 100644 --- a/jquantsapi/apis/v2/bulk.py +++ b/jquantsapi/apis/v2/bulk.py @@ -35,7 +35,9 @@ def execute( url = f"{client.JQUANTS_API_BASE}/bulk/list" # BulkEndpointの場合はvalue(str)を取得 - endpoint_str = endpoint.value if isinstance(endpoint, BulkEndpoint) else endpoint + endpoint_str = ( + endpoint.value if isinstance(endpoint, BulkEndpoint) else endpoint + ) params: dict[str, Any] = {"endpoint": endpoint_str} @@ -89,4 +91,3 @@ def execute( payload = resp.json() return payload.get("url", "") - diff --git a/jquantsapi/apis/v2/derivatives.py b/jquantsapi/apis/v2/derivatives.py index 9508f8d..b8f9d36 100644 --- a/jquantsapi/apis/v2/derivatives.py +++ b/jquantsapi/apis/v2/derivatives.py @@ -133,4 +133,3 @@ def execute( if sort_cols: df.sort_values(sort_cols, inplace=True) return df.reset_index(drop=True) - diff --git a/jquantsapi/apis/v2/equities.py b/jquantsapi/apis/v2/equities.py index 790177d..fcd4a24 100644 --- a/jquantsapi/apis/v2/equities.py +++ b/jquantsapi/apis/v2/equities.py @@ -286,4 +286,3 @@ def execute( if sort_cols: df.sort_values(sort_cols, inplace=True) return df.reset_index(drop=True) - diff --git a/jquantsapi/apis/v2/fins.py b/jquantsapi/apis/v2/fins.py index 27f26bb..77c55f1 100644 --- a/jquantsapi/apis/v2/fins.py +++ b/jquantsapi/apis/v2/fins.py @@ -148,4 +148,3 @@ def execute( if sort_cols: df.sort_values(sort_cols, inplace=True) return df.reset_index(drop=True) - diff --git a/jquantsapi/apis/v2/indices.py b/jquantsapi/apis/v2/indices.py index ffffeb8..3a3a966 100644 --- a/jquantsapi/apis/v2/indices.py +++ b/jquantsapi/apis/v2/indices.py @@ -85,4 +85,3 @@ def execute( df["Date"] = pd.to_datetime(df["Date"], errors="coerce") df.sort_values("Date", inplace=True) return df.reset_index(drop=True) - diff --git a/jquantsapi/apis/v2/markets.py b/jquantsapi/apis/v2/markets.py index c5ae2eb..6e3c47b 100644 --- a/jquantsapi/apis/v2/markets.py +++ b/jquantsapi/apis/v2/markets.py @@ -293,4 +293,3 @@ def execute( df["Date"] = pd.to_datetime(df["Date"], errors="coerce") df.sort_values("Date", inplace=True) return df.reset_index(drop=True) - diff --git a/jquantsapi/client.py b/jquantsapi/client.py index fee0dac..5c29be1 100644 --- a/jquantsapi/client.py +++ b/jquantsapi/client.py @@ -20,18 +20,6 @@ from urllib3.util import Retry from jquantsapi import __version__, constants, enums -from jquantsapi.apis.v1.listed import ListedInfoApiV1 -from jquantsapi.apis.v1.prices import PricesDailyQuotesApiV1, PricesPricesAmApiV1 -from jquantsapi.apis.v1.markets import ( - MarketsTradesSpecApiV1, - MarketsWeeklyMarginInterestApiV1, - MarketsTradingCalendarApiV1, - MarketsShortSellingApiV1, - MarketsBreakdownApiV1, - MarketsShortSellingPositionsApiV1, - MarketsDailyMarginInterestApiV1, -) -from jquantsapi.apis.v1.indices import IndicesApiV1, IndicesTopixApiV1 from jquantsapi.apis.v1.derivatives import ( DerivativesFuturesApiV1, DerivativesOptionsApiV1, @@ -43,6 +31,18 @@ FinsFsDetailsApiV1, FinsStatementsApiV1, ) +from jquantsapi.apis.v1.indices import IndicesApiV1, IndicesTopixApiV1 +from jquantsapi.apis.v1.listed import ListedInfoApiV1 +from jquantsapi.apis.v1.markets import ( + MarketsBreakdownApiV1, + MarketsDailyMarginInterestApiV1, + MarketsShortSellingApiV1, + MarketsShortSellingPositionsApiV1, + MarketsTradesSpecApiV1, + MarketsTradingCalendarApiV1, + MarketsWeeklyMarginInterestApiV1, +) +from jquantsapi.apis.v1.prices import PricesDailyQuotesApiV1, PricesPricesAmApiV1 if sys.version_info >= (3, 11): import tomllib @@ -63,7 +63,9 @@ class TokenAuthRefreshBadRequestException(Exception): pass -@deprecated("Client (V1) is deprecated and will be removed in a future version. Please use ClientV2 instead.") +@deprecated( + "Client (V1) is deprecated and will be removed in a future version. Please use ClientV2 instead." +) class Client: """ J-Quants API からデータを取得する @@ -803,6 +805,7 @@ def get_indices( to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) + def get_indices_topix( self, from_yyyymmdd: str = "", @@ -1222,6 +1225,7 @@ def get_derivatives_options( contract_flag=contract_flag, code=code, ) + def get_derivatives_options_range( self, start_dt: DatetimeLike = "20170101", @@ -1386,4 +1390,3 @@ def get_daily_margin_interest_range( df = future.result() buff.append(df) return pd.concat(buff).sort_values(["Code", "PublishedDate"]) - diff --git a/jquantsapi/client_v2.py b/jquantsapi/client_v2.py index 57c624e..5cf9348 100644 --- a/jquantsapi/client_v2.py +++ b/jquantsapi/client_v2.py @@ -17,6 +17,12 @@ import tomli as tomllib from jquantsapi import __version__, constants +from jquantsapi.apis.v2.bulk import BulkGetApiV2, BulkListApiV2 +from jquantsapi.apis.v2.derivatives import ( + DrvBarsDailyFutApiV2, + DrvBarsDailyOpt225ApiV2, + DrvBarsDailyOptApiV2, +) from jquantsapi.apis.v2.equities import ( EqBarsDailyAmApiV2, EqBarsDailyApiV2, @@ -26,6 +32,7 @@ EqMasterApiV2, ) from jquantsapi.apis.v2.fins import FinDetailsApiV2, FinDividendApiV2, FinSummaryApiV2 +from jquantsapi.apis.v2.indices import IdxBarsDailyApiV2, IdxBarsDailyTopixApiV2 from jquantsapi.apis.v2.markets import ( MktBreakdownApiV2, MktCalendarApiV2, @@ -34,18 +41,11 @@ MktShortRatioApiV2, MktShortSaleReportApiV2, ) -from jquantsapi.apis.v2.indices import IdxBarsDailyApiV2, IdxBarsDailyTopixApiV2 -from jquantsapi.apis.v2.derivatives import ( - DrvBarsDailyFutApiV2, - DrvBarsDailyOptApiV2, - DrvBarsDailyOpt225ApiV2, -) -from jquantsapi.apis.v2.bulk import BulkGetApiV2, BulkListApiV2 from jquantsapi.enums import BulkEndpoint - DatetimeLike = Union[datetime, pd.Timestamp, str] + class ClientV2: """ J-Quants API v2 用のクライアント @@ -155,9 +155,7 @@ def _load_config(self) -> dict: config = {**config, **self._read_config(env_config_path)} # env var (highest priority) - config["api_key"] = os.environ.get( - "JQUANTS_API_KEY", config.get("api_key", "") - ) + config["api_key"] = os.environ.get("JQUANTS_API_KEY", config.get("api_key", "")) return config @@ -218,7 +216,9 @@ def _base_headers(self) -> dict[str, str]: f"p/{platform.python_version()}", } - def _get(self, url: str, params: Optional[dict[str, Any]] = None) -> requests.Response: + def _get( + self, url: str, params: Optional[dict[str, Any]] = None + ) -> requests.Response: """ GET リクエスト用ラッパー """ @@ -298,7 +298,9 @@ def get_17_sectors(self) -> pd.DataFrame: """ 17 業種コードと名称 (V2 カラム名) """ - df = pd.DataFrame(constants.SECTOR_17_DATA, columns=constants.SECTOR_17_COLUMNS_V2) + df = pd.DataFrame( + constants.SECTOR_17_DATA, columns=constants.SECTOR_17_COLUMNS_V2 + ) df.sort_values(constants.SECTOR_17_COLUMNS_V2[0], inplace=True) return df @@ -306,7 +308,9 @@ def get_33_sectors(self) -> pd.DataFrame: """ 33 業種コードと名称 (V2 カラム名) """ - df = pd.DataFrame(constants.SECTOR_33_DATA, columns=constants.SECTOR_33_COLUMNS_V2) + df = pd.DataFrame( + constants.SECTOR_33_DATA, columns=constants.SECTOR_33_COLUMNS_V2 + ) df.sort_values(constants.SECTOR_33_COLUMNS_V2[0], inplace=True) return df @@ -477,12 +481,12 @@ def _aggregate_bars_n_minute( "Date": "first", "Time": "first", "Code": "first", - "O": "first", # 始値: 最初の値 - "H": "max", # 高値: 最大値 - "L": "min", # 安値: 最小値 - "C": "last", # 終値: 最後の値 - "Vo": "sum", # 出来高: 合計 - "Va": "sum", # 売買代金: 合計 + "O": "first", # 始値: 最初の値 + "H": "max", # 高値: 最大値 + "L": "min", # 安値: 最小値 + "C": "last", # 終値: 最後の値 + "Vo": "sum", # 出来高: 合計 + "Va": "sum", # 売買代金: 合計 } result = ( @@ -659,9 +663,11 @@ def get_fin_summary_range( if not buff: return pd.DataFrame() - return pd.concat(buff).sort_values( - ["DiscDate", "DiscTime", "Code"] - ).reset_index(drop=True) + return ( + pd.concat(buff) + .sort_values(["DiscDate", "DiscTime", "Code"]) + .reset_index(drop=True) + ) # ------------------------------------------------------------------ # /fins/details (path_old: /fins/fs_details) @@ -738,9 +744,11 @@ def get_fin_details_range( if not buff: return pd.DataFrame() - return pd.concat(buff).sort_values( - ["DiscDate", "DiscTime", "Code"] - ).reset_index(drop=True) + return ( + pd.concat(buff) + .sort_values(["DiscDate", "DiscTime", "Code"]) + .reset_index(drop=True) + ) # ------------------------------------------------------------------ # /fins/dividend (path_old: /fins/dividend) @@ -835,9 +843,7 @@ def get_mkt_short_ratio_range( buff.append(df) if not buff: return pd.DataFrame() - return pd.concat(buff).sort_values( - ["Date", "S33"] - ).reset_index(drop=True) + return pd.concat(buff).sort_values(["Date", "S33"]).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/short-sale-report (path_old: /markets/short_selling_positions) @@ -895,9 +901,11 @@ def get_mkt_short_sale_report_range( buff.append(df) if not buff: return pd.DataFrame() - return pd.concat(buff).sort_values( - ["DiscDate", "CalcDate", "Code"] - ).reset_index(drop=True) + return ( + pd.concat(buff) + .sort_values(["DiscDate", "CalcDate", "Code"]) + .reset_index(drop=True) + ) # ------------------------------------------------------------------ # /markets/margin-interest (path_old: /markets/weekly_margin_interest) @@ -952,9 +960,7 @@ def get_mkt_margin_interest_range( buff.append(df) if not buff: return pd.DataFrame() - return pd.concat(buff).sort_values( - ["Date", "Code"] - ).reset_index(drop=True) + return pd.concat(buff).sort_values(["Date", "Code"]).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/margin-alert (path_old: /markets/daily_margin_interest) @@ -1009,9 +1015,7 @@ def get_mkt_margin_alert_range( buff.append(df) if not buff: return pd.DataFrame() - return pd.concat(buff).sort_values( - ["PubDate", "Code"] - ).reset_index(drop=True) + return pd.concat(buff).sort_values(["PubDate", "Code"]).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/breakdown (path_old: /markets/breakdown) @@ -1065,9 +1069,7 @@ def get_mkt_breakdown_range( buff.append(df) if not buff: return pd.DataFrame() - return pd.concat(buff).sort_values( - ["Code", "Date"] - ).reset_index(drop=True) + return pd.concat(buff).sort_values(["Code", "Date"]).reset_index(drop=True) # ------------------------------------------------------------------ # /markets/calendar (path_old: /markets/trading_calendar) @@ -1338,4 +1340,3 @@ def download_bulk(self, key: str, output_path: str) -> None: with open(output_path, "wb") as f: for chunk in response.iter_content(chunk_size=8192): f.write(chunk) - diff --git a/tests/test_client_v2.py b/tests/test_client_v2.py index 33fddb4..5b951b8 100644 --- a/tests/test_client_v2.py +++ b/tests/test_client_v2.py @@ -11,10 +11,7 @@ @pytest.mark.parametrize( - "api_key," - "env, isfile, load," - "exp_api_key," - "exp_raise", + "api_key," "env, isfile, load," "exp_api_key," "exp_raise", ( # Case 1: api_key未指定、設定ファイルなし、環境変数なし → エラー ( @@ -276,7 +273,18 @@ def test_aggregate_bars_n_minute(): "L": [90, 91, 92, 93, 94, 95, 96, 97, 98, 99], "C": [105, 106, 107, 108, 109, 110, 111, 112, 113, 114], "Vo": [1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900], - "Va": [100000, 110000, 120000, 130000, 140000, 150000, 160000, 170000, 180000, 190000], + "Va": [ + 100000, + 110000, + 120000, + 130000, + 140000, + 150000, + 160000, + 170000, + 180000, + 190000, + ], } df_1min = pd.DataFrame(data) @@ -387,4 +395,3 @@ def test_get_bulk(): args, _ = mock_get.call_args assert args[1] == {"key": "2024/01/01/eq_master.csv"} assert ret == "https://example.com/data.csv" - From 8e0e7f40108dba0271a35f8d272cf31355258f7b Mon Sep 17 00:00:00 2001 From: t-kizawa-digima Date: Mon, 19 Jan 2026 15:01:00 +0900 Subject: [PATCH 21/21] Fix linter and tests --- Makefile | 4 ++-- jquantsapi/apis/v1/derivatives.py | 9 +++++--- jquantsapi/apis/v1/fins.py | 4 ++++ jquantsapi/apis/v1/indices.py | 2 ++ jquantsapi/apis/v1/listed.py | 1 + jquantsapi/apis/v1/markets.py | 7 ++++++ jquantsapi/apis/v1/prices.py | 2 ++ jquantsapi/apis/v2/bulk.py | 6 ++++-- jquantsapi/apis/v2/derivatives.py | 9 +++++--- jquantsapi/apis/v2/equities.py | 9 +++++++- jquantsapi/apis/v2/fins.py | 3 +++ jquantsapi/apis/v2/indices.py | 2 ++ jquantsapi/apis/v2/markets.py | 6 ++++++ jquantsapi/client.py | 3 +-- poetry.lock | 36 ++++++++++++++++--------------- pyproject.toml | 4 ++-- tests/test_client.py | 22 ++++++++++++------- tests/test_client_v2.py | 26 +++++++++++++--------- 18 files changed, 105 insertions(+), 50 deletions(-) diff --git a/Makefile b/Makefile index 4787092..3ea7fcd 100644 --- a/Makefile +++ b/Makefile @@ -3,14 +3,14 @@ lint: poetry run isort . poetry run black . --check - poetry run pflake8 . + poetry run flake8 . poetry run mypy . .PHONY: lint-fix lint-fix: poetry run isort . poetry run black . - poetry run pflake8 . + poetry run flake8 . poetry run mypy . .PHONY: test diff --git a/jquantsapi/apis/v1/derivatives.py b/jquantsapi/apis/v1/derivatives.py index f94b1e8..693513e 100644 --- a/jquantsapi/apis/v1/derivatives.py +++ b/jquantsapi/apis/v1/derivatives.py @@ -21,9 +21,10 @@ def execute( self, client: SupportsRequest, *, - date_yyyymmdd: str, + date_yyyymmdd: str = "", category: str = "", contract_flag: str = "", + **kwargs: Any, ) -> pd.DataFrame: # 元の _get_derivatives_futures_raw の実装を統合 url = f"{client.JQUANTS_API_BASE}/derivatives/futures" # type: ignore[attr-defined] @@ -67,10 +68,11 @@ def execute( self, client: SupportsRequest, *, - date_yyyymmdd: str, + date_yyyymmdd: str = "", category: str = "", contract_flag: str = "", code: str = "", + **kwargs: Any, ) -> pd.DataFrame: # 元の _get_derivatives_options_raw の実装を統合 url = f"{client.JQUANTS_API_BASE}/derivatives/options" # type: ignore[attr-defined] @@ -115,7 +117,8 @@ def execute( self, client: SupportsRequest, *, - date_yyyymmdd: str, + date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: # 元の _get_option_index_option_raw の実装を統合 url = f"{client.JQUANTS_API_BASE}/option/index_option" # type: ignore[attr-defined] diff --git a/jquantsapi/apis/v1/fins.py b/jquantsapi/apis/v1/fins.py index 2dda639..4e43e7f 100644 --- a/jquantsapi/apis/v1/fins.py +++ b/jquantsapi/apis/v1/fins.py @@ -23,6 +23,7 @@ def execute( *, code: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/fins/statements` を実行し、財務情報を DataFrame で返す。 @@ -88,6 +89,7 @@ def execute( *, code: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/fins/fs_details` を実行し、財務諸表(BS/PL)を DataFrame で返す。 @@ -137,6 +139,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/fins/dividend` を実行し、配当金情報を DataFrame で返す。 @@ -190,6 +193,7 @@ class FinsAnnouncementApiV1(BaseApi): def execute( self, client: SupportsRequest, + **kwargs: Any, ) -> pd.DataFrame: """ `/fins/announcement` を実行し、決算発表予定データを DataFrame で返す。 diff --git a/jquantsapi/apis/v1/indices.py b/jquantsapi/apis/v1/indices.py index 357e99b..66fe01e 100644 --- a/jquantsapi/apis/v1/indices.py +++ b/jquantsapi/apis/v1/indices.py @@ -25,6 +25,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: # 元の _get_indices_raw の実装を統合 url = f"{client.JQUANTS_API_BASE}/indices" # type: ignore[attr-defined] @@ -73,6 +74,7 @@ def execute( *, from_yyyymmdd: str = "", to_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: # 元の _get_indices_topix_raw の実装を統合 url = f"{client.JQUANTS_API_BASE}/indices/topix" # type: ignore[attr-defined] diff --git a/jquantsapi/apis/v1/listed.py b/jquantsapi/apis/v1/listed.py index 24c1092..513e839 100644 --- a/jquantsapi/apis/v1/listed.py +++ b/jquantsapi/apis/v1/listed.py @@ -23,6 +23,7 @@ def execute( *, code: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ v1 `/listed/info` を実行し、上場銘柄情報を DataFrame で返す。 diff --git a/jquantsapi/apis/v1/markets.py b/jquantsapi/apis/v1/markets.py index 0081eb6..c9093e2 100644 --- a/jquantsapi/apis/v1/markets.py +++ b/jquantsapi/apis/v1/markets.py @@ -24,6 +24,7 @@ def execute( section: Union[str, enums.MARKET_API_SECTIONS] = "", from_yyyymmdd: str = "", to_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/trades_spec` を実行し、投資部門別売買状況を DataFrame で返す。 @@ -83,6 +84,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/weekly_margin_interest` を実行し、信用取引週末残高を DataFrame で返す。 @@ -134,6 +136,7 @@ def execute( holiday_division: str = "", from_yyyymmdd: str = "", to_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/trading_calendar` を実行し、取引カレンダーデータを DataFrame で返す。 @@ -177,6 +180,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/short_selling` を実行し、業種別空売り比率データを DataFrame で返す。 @@ -229,6 +233,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/breakdown` を実行し、売買内訳データを DataFrame で返す。 @@ -282,6 +287,7 @@ def execute( disclosed_date_from: str = "", disclosed_date_to: str = "", calculated_date: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/short_selling_positions` を実行し、空売り残高報告データを DataFrame で返す。 @@ -345,6 +351,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/daily_margin_interest` を実行し、日々公表信用取引残高を DataFrame で返す。 diff --git a/jquantsapi/apis/v1/prices.py b/jquantsapi/apis/v1/prices.py index 7138bde..3bfe4b5 100644 --- a/jquantsapi/apis/v1/prices.py +++ b/jquantsapi/apis/v1/prices.py @@ -25,6 +25,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/prices/daily_quotes` を実行し、株価情報を DataFrame で返す。 @@ -92,6 +93,7 @@ def execute( client: SupportsRequest, *, code: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/prices/prices_am` を実行し、前場四本値を DataFrame で返す。 diff --git a/jquantsapi/apis/v2/bulk.py b/jquantsapi/apis/v2/bulk.py index aa9814a..56cfdee 100644 --- a/jquantsapi/apis/v2/bulk.py +++ b/jquantsapi/apis/v2/bulk.py @@ -23,7 +23,8 @@ def execute( self, client: SupportsRequest, *, - endpoint: Union[str, BulkEndpoint], + endpoint: Union[str, BulkEndpoint] = "", + **kwargs: Any, ) -> pd.DataFrame: """ v2 `/bulk/list` を実行し、取得可能なデータ一覧を DataFrame で返す。 @@ -71,7 +72,8 @@ def execute( self, client: SupportsRequest, *, - key: str, + key: str = "", + **kwargs: Any, ) -> str: """ v2 `/bulk/get` を実行し、ダウンロードURLを取得する。 diff --git a/jquantsapi/apis/v2/derivatives.py b/jquantsapi/apis/v2/derivatives.py index b8f9d36..98f865f 100644 --- a/jquantsapi/apis/v2/derivatives.py +++ b/jquantsapi/apis/v2/derivatives.py @@ -21,9 +21,10 @@ def execute( self, client: SupportsRequest, *, - date_yyyymmdd: str, + date_yyyymmdd: str = "", category: str = "", contract_flag: str = "", + **kwargs: Any, ) -> pd.DataFrame: params: dict[str, Any] = {"date": date_yyyymmdd} if category: @@ -64,10 +65,11 @@ def execute( self, client: SupportsRequest, *, - date_yyyymmdd: str, + date_yyyymmdd: str = "", category: str = "", contract_flag: str = "", code: str = "", + **kwargs: Any, ) -> pd.DataFrame: params: dict[str, Any] = {"date": date_yyyymmdd} if category: @@ -111,7 +113,8 @@ def execute( self, client: SupportsRequest, *, - date_yyyymmdd: str, + date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: params: dict[str, Any] = {"date": date_yyyymmdd} diff --git a/jquantsapi/apis/v2/equities.py b/jquantsapi/apis/v2/equities.py index fcd4a24..7d9417f 100644 --- a/jquantsapi/apis/v2/equities.py +++ b/jquantsapi/apis/v2/equities.py @@ -18,7 +18,9 @@ class EqMasterApiV2(BaseApi): name = "eq_master" version = "v2" - def execute(self, client: Any, code: str = "", date: str = "") -> pd.DataFrame: + def execute( + self, client: Any, code: str = "", date: str = "", **kwargs: Any + ) -> pd.DataFrame: params: dict[str, str] = {} if code: params["code"] = code @@ -58,6 +60,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ v2 `/equities/bars/daily` を実行し、株価四本値を DataFrame で返す。 @@ -112,6 +115,7 @@ def execute( client: SupportsRequest, *, code: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ v2 `/equities/bars/daily/am` を実行し、前場四本値を DataFrame で返す。 @@ -159,6 +163,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ v2 `/equities/bars/minute` を実行し、分足を DataFrame で返す。 @@ -216,6 +221,7 @@ def execute( section: str = "", from_yyyymmdd: str = "", to_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ v2 `/equities/investor-types` を実行し、投資部門別売買状況を DataFrame で返す。 @@ -265,6 +271,7 @@ class EqEarningsCalApiV2(BaseApi): def execute( self, client: SupportsRequest, + **kwargs: Any, ) -> pd.DataFrame: """ `/equities/earnings-calendar` を実行し、決算発表予定データを DataFrame で返す。 diff --git a/jquantsapi/apis/v2/fins.py b/jquantsapi/apis/v2/fins.py index 77c55f1..efa9559 100644 --- a/jquantsapi/apis/v2/fins.py +++ b/jquantsapi/apis/v2/fins.py @@ -22,6 +22,7 @@ def execute( *, code: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/fins/summary` を実行し、財務情報サマリを DataFrame で返す。 @@ -75,6 +76,7 @@ def execute( *, code: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/fins/details` を実行し、財務諸表詳細を DataFrame で返す。 @@ -118,6 +120,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/fins/dividend` を実行し、配当金情報を DataFrame で返す。 diff --git a/jquantsapi/apis/v2/indices.py b/jquantsapi/apis/v2/indices.py index 3a3a966..f147c06 100644 --- a/jquantsapi/apis/v2/indices.py +++ b/jquantsapi/apis/v2/indices.py @@ -25,6 +25,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: params: dict[str, Any] = {} if code: @@ -69,6 +70,7 @@ def execute( *, from_yyyymmdd: str = "", to_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: params: dict[str, Any] = {} if from_yyyymmdd: diff --git a/jquantsapi/apis/v2/markets.py b/jquantsapi/apis/v2/markets.py index 6e3c47b..a5bd1e4 100644 --- a/jquantsapi/apis/v2/markets.py +++ b/jquantsapi/apis/v2/markets.py @@ -24,6 +24,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/short-ratio` を実行し、業種別空売り比率データを DataFrame で返す。 @@ -76,6 +77,7 @@ def execute( disclosed_date_from: str = "", disclosed_date_to: str = "", calculated_date: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/short-sale-report` を実行し、空売り残高報告データを DataFrame で返す。 @@ -126,6 +128,7 @@ def execute( date_yyyymmdd: str = "", from_yyyymmdd: str = "", to_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/margin-interest` を実行し、信用取引週末残高を DataFrame で返す。 @@ -173,6 +176,7 @@ def execute( from_yyyymmdd: str = "", to_yyyymmdd: str = "", date_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/breakdown` を実行し、売買内訳データを DataFrame で返す。 @@ -223,6 +227,7 @@ def execute( date_yyyymmdd: str = "", from_yyyymmdd: str = "", to_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/margin-alert` を実行し、日々公表信用取引残高を DataFrame で返す。 @@ -269,6 +274,7 @@ def execute( holiday_division: str = "", from_yyyymmdd: str = "", to_yyyymmdd: str = "", + **kwargs: Any, ) -> pd.DataFrame: """ `/markets/calendar` を実行し、取引カレンダーデータを DataFrame で返す。 diff --git a/jquantsapi/client.py b/jquantsapi/client.py index 5c29be1..7b5ddce 100644 --- a/jquantsapi/client.py +++ b/jquantsapi/client.py @@ -1,4 +1,3 @@ -import json import os import platform import sys @@ -50,7 +49,7 @@ import tomli as tomllib if sys.version_info >= (3, 13): - from warnings import deprecated + from warnings import deprecated # type: ignore[attr-defined] else: from typing_extensions import deprecated diff --git a/poetry.lock b/poetry.lock index 7eb24ff..fc95950 100644 --- a/poetry.lock +++ b/poetry.lock @@ -288,6 +288,24 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.9.0,<2.10.0" pyflakes = ">=2.5.0,<2.6.0" +[[package]] +name = "flake8-pyproject" +version = "1.2.4" +description = "Flake8 plug-in loading the configuration from pyproject.toml" +optional = false +python-versions = ">=3.6" +groups = ["dev"] +files = [ + {file = "flake8_pyproject-1.2.4-py3-none-any.whl", hash = "sha256:ea34c057f9a9329c76d98723bb2bb498cc6ba8ff9872c4d19932d48c91249a77"}, +] + +[package.dependencies] +Flake8 = ">=5" +TOMLi = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["Flit (>=3.4)", "pyTest (>=7)", "pyTest-cov (>=7) ; python_version >= \"3.10\""] + [[package]] name = "idna" version = "3.6" @@ -728,22 +746,6 @@ files = [ {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, ] -[[package]] -name = "pyproject-flake8" -version = "5.0.4.post1" -description = "pyproject-flake8 (`pflake8`), a monkey patching wrapper to connect flake8 with pyproject.toml configuration" -optional = false -python-versions = "*" -groups = ["dev"] -files = [ - {file = "pyproject-flake8-5.0.4.post1.tar.gz", hash = "sha256:c2dfdf1064f47efbb2e4faf1a32b0b6a6ea67dc4d1debb98d862b0cdee377941"}, - {file = "pyproject_flake8-5.0.4.post1-py2.py3-none-any.whl", hash = "sha256:457e52dde1b7a1f84b5230c70d61afa58ced64a44b81a609f19e972319fa68ed"}, -] - -[package.dependencies] -flake8 = "5.0.4" -tomli = {version = "*", markers = "python_version < \"3.11\""} - [[package]] name = "pytest" version = "8.1.1" @@ -948,4 +950,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" python-versions = "^3.10" -content-hash = "a74deb08794928259592a46c40adc0f0c9549deb3b52e46ec4d2d978e67b3560" +content-hash = "884f135e37a86af5f74d53a6efeb2e9848e2afb32553ba53984f137d22a115f3" diff --git a/pyproject.toml b/pyproject.toml index f9e3f5c..17895ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,8 +38,8 @@ tenacity = "^8.2.0" black = "^24.3.0" isort = "^5.10.1" flake8 = "^5.0.0" +Flake8-pyproject = "^1.2.0" mypy = "^1.9.0" -pyproject-flake8 = "^5.0.0" pytest = "8.1.1" pytest-cov = "^5.0.0" types-requests = "^2.31.0" @@ -54,7 +54,7 @@ max-line-length = 120 max-complexity = 18 ignore = "E203,E266,W503," per-file-ignores = "__init__.py:F401" -exclude = ".venv" +exclude = ".venv,tests" [tool.isort] profile = "black" diff --git a/tests/test_client.py b/tests/test_client.py index 9452026..8ad821f 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,6 +1,6 @@ from contextlib import nullcontext as does_not_raise from datetime import datetime -from unittest.mock import MagicMock, call, patch +from unittest.mock import MagicMock, patch import pandas as pd import pytest @@ -307,11 +307,17 @@ def test_get_price_range(): cli.get_price_range(start, end) # 呼び出しの履歴と、get_prices_daily_quotes()が呼ばれた際の年月日8桁の引数を比較 - assert mock.mock_calls == [ - call.get_prices_daily_quotes(date_yyyymmdd="2020-02-27"), - call.get_prices_daily_quotes(date_yyyymmdd="2020-02-28"), - call.get_prices_daily_quotes(date_yyyymmdd="2020-02-29"), - call.get_prices_daily_quotes(date_yyyymmdd="2020-03-01"), - call.get_prices_daily_quotes(date_yyyymmdd="2020-03-02"), - ] + # 並列実行のため順序は保証されないので、セットとして比較 + expected_dates = { + "2020-02-27", + "2020-02-28", + "2020-02-29", + "2020-03-01", + "2020-03-02", + } + actual_dates = { + call_obj.kwargs["date_yyyymmdd"] for call_obj in mock.mock_calls + } + assert actual_dates == expected_dates + assert len(mock.mock_calls) == 5 mock.reset_mock() diff --git a/tests/test_client_v2.py b/tests/test_client_v2.py index 5b951b8..a35df53 100644 --- a/tests/test_client_v2.py +++ b/tests/test_client_v2.py @@ -1,6 +1,6 @@ from contextlib import nullcontext as does_not_raise from datetime import datetime -from unittest.mock import MagicMock, call, patch +from unittest.mock import MagicMock, patch import pandas as pd import pytest @@ -208,8 +208,8 @@ def test_get_eq_bars_daily(code, from_yyyymmdd, to_yyyymmdd, date_yyyymmdd, exp_ to_yyyymmdd=to_yyyymmdd, date_yyyymmdd=date_yyyymmdd, ) - args, _ = mock_get.call_args - assert args[1] == exp_params + _, kwargs = mock_get.call_args + assert kwargs["params"] == exp_params assert len(ret) == exp_ret_len @@ -240,13 +240,19 @@ def test_get_eq_bars_daily_range(): for _fmt, (start, end) in formats.items(): cli.get_eq_bars_daily_range(start, end) - assert mock.mock_calls == [ - call(date_yyyymmdd="2020-02-27"), - call(date_yyyymmdd="2020-02-28"), - call(date_yyyymmdd="2020-02-29"), - call(date_yyyymmdd="2020-03-01"), - call(date_yyyymmdd="2020-03-02"), - ] + # 並列実行のため順序は保証されないので、セットとして比較 + expected_dates = { + "2020-02-27", + "2020-02-28", + "2020-02-29", + "2020-03-01", + "2020-03-02", + } + actual_dates = { + call_obj.kwargs["date_yyyymmdd"] for call_obj in mock.mock_calls + } + assert actual_dates == expected_dates + assert len(mock.mock_calls) == 5 mock.reset_mock()