kabukit 0.7.0__tar.gz → 0.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kabukit-0.7.0 → kabukit-0.7.1}/PKG-INFO +1 -2
- {kabukit-0.7.0 → kabukit-0.7.1}/pyproject.toml +4 -5
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/visualization/__init__.py +1 -1
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/info.py +0 -5
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/list.py +0 -5
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/reports.py +0 -5
- kabukit-0.7.1/src/kabukit/jquants/calendar.py +12 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/client.py +91 -31
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/statements.py +1 -15
- {kabukit-0.7.0 → kabukit-0.7.1}/LICENSE +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/README.md +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/__init__.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/__init__.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/indicators.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/preprocess.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/screener.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/visualization/market.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/visualization/prices.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/__init__.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/app.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/auth.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/cache.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/get.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/__init__.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/base.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/client.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/prices.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/statements.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/edinet/__init__.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/edinet/client.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/edinet/concurrent.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/edinet/doc.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/__init__.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/concurrent.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/info.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/prices.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/schema.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/topix.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/py.typed +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/__init__.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/concurrent.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/config.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/date.py +0 -0
- {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/params.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: kabukit
|
3
|
-
Version: 0.7.
|
3
|
+
Version: 0.7.1
|
4
4
|
Summary: A Python toolkit for Japanese financial market data, supporting J-Quants and EDINET APIs.
|
5
5
|
Author: daizutabi
|
6
6
|
Author-email: daizutabi <daizutabi@gmail.com>
|
@@ -29,7 +29,6 @@ Classifier: Development Status :: 4 - Beta
|
|
29
29
|
Classifier: Programming Language :: Python
|
30
30
|
Classifier: Programming Language :: Python :: 3.13
|
31
31
|
Requires-Dist: async-typer>=0.1.10
|
32
|
-
Requires-Dist: holidays>=0.82
|
33
32
|
Requires-Dist: httpx>=0.28.1
|
34
33
|
Requires-Dist: platformdirs>=4.4.0
|
35
34
|
Requires-Dist: polars>=1.34.0
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "kabukit"
|
7
|
-
version = "0.7.
|
7
|
+
version = "0.7.1"
|
8
8
|
description = "A Python toolkit for Japanese financial market data, supporting J-Quants and EDINET APIs."
|
9
9
|
readme = "README.md"
|
10
10
|
license = { file = "LICENSE" }
|
@@ -17,7 +17,6 @@ classifiers = [
|
|
17
17
|
requires-python = ">=3.13"
|
18
18
|
dependencies = [
|
19
19
|
"async-typer>=0.1.10",
|
20
|
-
"holidays>=0.82",
|
21
20
|
"httpx>=0.28.1",
|
22
21
|
"platformdirs>=4.4.0",
|
23
22
|
"polars>=1.34.0",
|
@@ -69,7 +68,7 @@ skip_covered = true
|
|
69
68
|
[tool.ruff]
|
70
69
|
line-length = 88
|
71
70
|
target-version = "py313"
|
72
|
-
include = ["src", "tests"]
|
71
|
+
include = ["src/**/*.py", "tests/**/*.py"]
|
73
72
|
|
74
73
|
[tool.ruff.lint]
|
75
74
|
select = ["ALL"]
|
@@ -91,9 +90,9 @@ ignore = [
|
|
91
90
|
]
|
92
91
|
|
93
92
|
[tool.ruff.lint.per-file-ignores]
|
94
|
-
"tests
|
93
|
+
"tests/**/*.py" = ["ANN", "FBT", "S101", "S607", "PLC2401"]
|
95
94
|
"schema.py" = ["E501"]
|
96
|
-
"notebooks
|
95
|
+
"notebooks/**/*.py" = ["F704", "PLE1142"]
|
97
96
|
|
98
97
|
[tool.ruff.format]
|
99
98
|
exclude = ["schema.py"]
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import polars as pl
|
4
|
+
from polars import DataFrame
|
5
|
+
|
6
|
+
|
7
|
+
def clean(df: DataFrame) -> DataFrame:
|
8
|
+
return df.with_columns(
|
9
|
+
pl.col("Date").str.to_date("%Y-%m-%d"),
|
10
|
+
pl.col("HolidayDivision").cast(pl.Categorical),
|
11
|
+
pl.col("HolidayDivision").eq("1").not_().alias("IsHoliday"),
|
12
|
+
)
|
@@ -1,9 +1,10 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import asyncio
|
3
4
|
import datetime
|
4
5
|
import os
|
5
6
|
from enum import StrEnum
|
6
|
-
from typing import TYPE_CHECKING
|
7
|
+
from typing import TYPE_CHECKING, final
|
7
8
|
|
8
9
|
import polars as pl
|
9
10
|
from polars import DataFrame
|
@@ -12,7 +13,7 @@ from kabukit.core.client import Client
|
|
12
13
|
from kabukit.utils.config import load_dotenv, set_key
|
13
14
|
from kabukit.utils.params import get_params
|
14
15
|
|
15
|
-
from . import info, prices, statements, topix
|
16
|
+
from . import calendar, info, prices, statements, topix
|
16
17
|
|
17
18
|
if TYPE_CHECKING:
|
18
19
|
from collections.abc import AsyncIterator
|
@@ -21,6 +22,26 @@ if TYPE_CHECKING:
|
|
21
22
|
from httpx import HTTPStatusError # noqa: F401
|
22
23
|
from httpx._types import QueryParamTypes
|
23
24
|
|
25
|
+
|
26
|
+
@final
|
27
|
+
class _CalendarCache:
|
28
|
+
def __init__(self) -> None:
|
29
|
+
self._holidays: list[datetime.date] | None = None
|
30
|
+
self._lock = asyncio.Lock()
|
31
|
+
|
32
|
+
async def get_holidays(self, client: JQuantsClient) -> list[datetime.date]:
|
33
|
+
async with self._lock:
|
34
|
+
if self._holidays is None:
|
35
|
+
calendar_df = await client.get_calendar()
|
36
|
+
self._holidays = (
|
37
|
+
calendar_df.filter(pl.col("IsHoliday")).get_column("Date").to_list()
|
38
|
+
)
|
39
|
+
return self._holidays
|
40
|
+
|
41
|
+
|
42
|
+
_calendar_cache_manager = _CalendarCache()
|
43
|
+
|
44
|
+
|
24
45
|
API_VERSION = "v1"
|
25
46
|
BASE_URL = f"https://api.jquants.com/{API_VERSION}"
|
26
47
|
|
@@ -128,32 +149,6 @@ class JQuantsClient(Client):
|
|
128
149
|
self.set_id_token(id_token)
|
129
150
|
return self
|
130
151
|
|
131
|
-
async def get_info(
|
132
|
-
self,
|
133
|
-
code: str | None = None,
|
134
|
-
date: str | datetime.date | None = None,
|
135
|
-
*,
|
136
|
-
clean: bool = True,
|
137
|
-
) -> DataFrame:
|
138
|
-
"""銘柄情報を取得する。
|
139
|
-
|
140
|
-
Args:
|
141
|
-
code (str, optional): 情報を取得する銘柄のコード。
|
142
|
-
date (str | datetime.date, optional): 情報を取得する日付。
|
143
|
-
clean (bool, optional): 取得したデータをクリーンアップするかどうか。
|
144
|
-
|
145
|
-
Returns:
|
146
|
-
銘柄情報を含むDataFrame。
|
147
|
-
|
148
|
-
Raises:
|
149
|
-
HTTPStatusError: APIリクエストが失敗した場合。
|
150
|
-
"""
|
151
|
-
params = get_params(code=code, date=date)
|
152
|
-
url = "/listed/info"
|
153
|
-
data = await self.get(url, params)
|
154
|
-
df = DataFrame(data["info"])
|
155
|
-
return info.clean(df) if clean else df
|
156
|
-
|
157
152
|
async def iter_pages(
|
158
153
|
self,
|
159
154
|
url: str,
|
@@ -183,6 +178,32 @@ class JQuantsClient(Client):
|
|
183
178
|
else:
|
184
179
|
break
|
185
180
|
|
181
|
+
async def get_info(
|
182
|
+
self,
|
183
|
+
code: str | None = None,
|
184
|
+
date: str | datetime.date | None = None,
|
185
|
+
*,
|
186
|
+
clean: bool = True,
|
187
|
+
) -> DataFrame:
|
188
|
+
"""銘柄情報を取得する。
|
189
|
+
|
190
|
+
Args:
|
191
|
+
code (str, optional): 情報を取得する銘柄のコード。
|
192
|
+
date (str | datetime.date, optional): 情報を取得する日付。
|
193
|
+
clean (bool, optional): 取得したデータをクリーンアップするかどうか。
|
194
|
+
|
195
|
+
Returns:
|
196
|
+
銘柄情報を含むDataFrame。
|
197
|
+
|
198
|
+
Raises:
|
199
|
+
HTTPStatusError: APIリクエストが失敗した場合。
|
200
|
+
"""
|
201
|
+
params = get_params(code=code, date=date)
|
202
|
+
url = "/listed/info"
|
203
|
+
data = await self.get(url, params)
|
204
|
+
df = DataFrame(data["info"])
|
205
|
+
return info.clean(df) if clean else df
|
206
|
+
|
186
207
|
async def get_prices(
|
187
208
|
self,
|
188
209
|
code: str | None = None,
|
@@ -291,7 +312,11 @@ class JQuantsClient(Client):
|
|
291
312
|
|
292
313
|
df = statements.clean(df)
|
293
314
|
|
294
|
-
|
315
|
+
if not with_date:
|
316
|
+
return df
|
317
|
+
|
318
|
+
holidays = await _calendar_cache_manager.get_holidays(self)
|
319
|
+
return statements.with_date(df, holidays=holidays)
|
295
320
|
|
296
321
|
async def get_announcement(self) -> DataFrame:
|
297
322
|
"""翌日発表予定の決算情報を取得する。
|
@@ -310,7 +335,7 @@ class JQuantsClient(Client):
|
|
310
335
|
if df.is_empty():
|
311
336
|
return df
|
312
337
|
|
313
|
-
return df.with_columns(pl.col("Date").str.to_date("%Y-%m-%d"
|
338
|
+
return df.with_columns(pl.col("Date").str.to_date("%Y-%m-%d"))
|
314
339
|
|
315
340
|
async def get_trades_spec(
|
316
341
|
self,
|
@@ -341,7 +366,7 @@ class JQuantsClient(Client):
|
|
341
366
|
if df.is_empty():
|
342
367
|
return df
|
343
368
|
|
344
|
-
return df.with_columns(pl.col("^.*Date$").str.to_date("%Y-%m-%d"
|
369
|
+
return df.with_columns(pl.col("^.*Date$").str.to_date("%Y-%m-%d"))
|
345
370
|
|
346
371
|
async def get_topix(
|
347
372
|
self,
|
@@ -371,3 +396,38 @@ class JQuantsClient(Client):
|
|
371
396
|
return df
|
372
397
|
|
373
398
|
return topix.clean(df)
|
399
|
+
|
400
|
+
async def get_calendar(
|
401
|
+
self,
|
402
|
+
holidaydivision: str | None = None,
|
403
|
+
from_: str | datetime.date | None = None,
|
404
|
+
to: str | datetime.date | None = None,
|
405
|
+
) -> DataFrame:
|
406
|
+
"""東証およびOSEにおける営業日、休業日、ならびにOSEにおける祝日取引の有無の情報を取得する。
|
407
|
+
|
408
|
+
Args:
|
409
|
+
holidaydivision: 祝日区分。
|
410
|
+
- 非営業日: "0"
|
411
|
+
- 営業日: "1"
|
412
|
+
- 東証半日立会日: "2"
|
413
|
+
- 非営業日(祝日取引あり): "3"
|
414
|
+
from_: 取得期間の開始日。
|
415
|
+
to: 取得期間の終了日。
|
416
|
+
|
417
|
+
Returns:
|
418
|
+
営業日・非営業日データを含むPolars DataFrame。
|
419
|
+
|
420
|
+
Raises:
|
421
|
+
HTTPStatusError: APIリクエストが失敗した場合。
|
422
|
+
"""
|
423
|
+
params = get_params(holidaydivision=holidaydivision, from_=from_, to=to)
|
424
|
+
|
425
|
+
url = "/markets/trading_calendar"
|
426
|
+
name = "trading_calendar"
|
427
|
+
|
428
|
+
dfs = [df async for df in self.iter_pages(url, params, name)]
|
429
|
+
df = pl.concat(dfs)
|
430
|
+
if df.is_empty():
|
431
|
+
return df
|
432
|
+
|
433
|
+
return calendar.clean(df)
|
@@ -1,10 +1,8 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import datetime
|
4
|
-
from functools import cache
|
5
4
|
from typing import TYPE_CHECKING
|
6
5
|
|
7
|
-
import holidays
|
8
6
|
import polars as pl
|
9
7
|
|
10
8
|
if TYPE_CHECKING:
|
@@ -80,17 +78,7 @@ def _cast_bool(df: DataFrame) -> DataFrame:
|
|
80
78
|
)
|
81
79
|
|
82
80
|
|
83
|
-
|
84
|
-
def get_holidays(year: int | None = None, n: int = 10) -> list[datetime.date]:
|
85
|
-
"""指定した過去年数の日本の祝日を取得する。"""
|
86
|
-
if year is None:
|
87
|
-
year = datetime.datetime.now().year # noqa: DTZ005
|
88
|
-
|
89
|
-
dates = holidays.country_holidays("JP", years=range(year - n, year + 1))
|
90
|
-
return sorted(dates.keys())
|
91
|
-
|
92
|
-
|
93
|
-
def with_date(df: DataFrame, year: int | None = None) -> DataFrame:
|
81
|
+
def with_date(df: DataFrame, holidays: list[datetime.date]) -> DataFrame:
|
94
82
|
"""`Date`列を追加する。
|
95
83
|
|
96
84
|
開示日が休日のとき、あるいは、開示時刻が15時以降の場合、Dateを開示日の翌営業日に設定する。
|
@@ -99,8 +87,6 @@ def with_date(df: DataFrame, year: int | None = None) -> DataFrame:
|
|
99
87
|
pl.col("DisclosedTime") > datetime.time(15, 0)
|
100
88
|
)
|
101
89
|
|
102
|
-
holidays = get_holidays(year=year)
|
103
|
-
|
104
90
|
return df.select(
|
105
91
|
pl.when(is_after_hours)
|
106
92
|
.then(pl.col("DisclosedDate") + datetime.timedelta(days=1))
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|