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.
Files changed (44) hide show
  1. {kabukit-0.7.0 → kabukit-0.7.1}/PKG-INFO +1 -2
  2. {kabukit-0.7.0 → kabukit-0.7.1}/pyproject.toml +4 -5
  3. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/visualization/__init__.py +1 -1
  4. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/info.py +0 -5
  5. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/list.py +0 -5
  6. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/reports.py +0 -5
  7. kabukit-0.7.1/src/kabukit/jquants/calendar.py +12 -0
  8. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/client.py +91 -31
  9. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/statements.py +1 -15
  10. {kabukit-0.7.0 → kabukit-0.7.1}/LICENSE +0 -0
  11. {kabukit-0.7.0 → kabukit-0.7.1}/README.md +0 -0
  12. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/__init__.py +0 -0
  13. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/__init__.py +0 -0
  14. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/indicators.py +0 -0
  15. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/preprocess.py +0 -0
  16. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/screener.py +0 -0
  17. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/visualization/market.py +0 -0
  18. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/analysis/visualization/prices.py +0 -0
  19. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/__init__.py +0 -0
  20. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/app.py +0 -0
  21. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/auth.py +0 -0
  22. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/cache.py +0 -0
  23. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/cli/get.py +0 -0
  24. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/__init__.py +0 -0
  25. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/base.py +0 -0
  26. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/client.py +0 -0
  27. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/prices.py +0 -0
  28. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/core/statements.py +0 -0
  29. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/edinet/__init__.py +0 -0
  30. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/edinet/client.py +0 -0
  31. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/edinet/concurrent.py +0 -0
  32. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/edinet/doc.py +0 -0
  33. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/__init__.py +0 -0
  34. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/concurrent.py +0 -0
  35. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/info.py +0 -0
  36. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/prices.py +0 -0
  37. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/schema.py +0 -0
  38. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/jquants/topix.py +0 -0
  39. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/py.typed +0 -0
  40. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/__init__.py +0 -0
  41. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/concurrent.py +0 -0
  42. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/config.py +0 -0
  43. {kabukit-0.7.0 → kabukit-0.7.1}/src/kabukit/utils/date.py +0 -0
  44. {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.0
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.0"
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/*" = ["ANN", "FBT", "S101", "S607", "PLC2401"]
93
+ "tests/**/*.py" = ["ANN", "FBT", "S101", "S607", "PLC2401"]
95
94
  "schema.py" = ["E501"]
96
- "notebooks/*" = ["F704", "PLE1142"]
95
+ "notebooks/**/*.py" = ["F704", "PLE1142"]
97
96
 
98
97
  [tool.ruff.format]
99
98
  exclude = ["schema.py"]
@@ -4,4 +4,4 @@ from .prices import plot_prices
4
4
  __all__ = [
5
5
  "plot_prices",
6
6
  "plot_topix_timeseries",
7
- ]
7
+ ]
@@ -1,12 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
4
-
5
3
  from .base import Base
6
4
 
7
- if TYPE_CHECKING:
8
- from polars import DataFrame
9
-
10
5
 
11
6
  class Info(Base):
12
7
  pass
@@ -1,12 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
4
-
5
3
  from .base import Base
6
4
 
7
- if TYPE_CHECKING:
8
- from polars import DataFrame
9
-
10
5
 
11
6
  class List(Base):
12
7
  pass
@@ -1,12 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING
4
-
5
3
  from .base import Base
6
4
 
7
- if TYPE_CHECKING:
8
- from polars import DataFrame
9
-
10
5
 
11
6
  class Reports(Base):
12
7
  pass
@@ -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
- return statements.with_date(df) if with_date else df
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", strict=False))
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", strict=False))
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
- @cache
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