kabukit 0.7.1__tar.gz → 0.7.2__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.1 → kabukit-0.7.2}/PKG-INFO +6 -3
  2. {kabukit-0.7.1 → kabukit-0.7.2}/pyproject.toml +11 -7
  3. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/base.py +2 -1
  4. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/edinet/client.py +13 -0
  5. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/edinet/doc.py +11 -2
  6. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/client.py +5 -5
  7. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/statements.py +2 -2
  8. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/utils/concurrent.py +1 -1
  9. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/utils/date.py +2 -1
  10. {kabukit-0.7.1 → kabukit-0.7.2}/LICENSE +0 -0
  11. {kabukit-0.7.1 → kabukit-0.7.2}/README.md +0 -0
  12. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/__init__.py +0 -0
  13. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/__init__.py +0 -0
  14. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/indicators.py +0 -0
  15. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/preprocess.py +0 -0
  16. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/screener.py +0 -0
  17. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/visualization/__init__.py +0 -0
  18. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/visualization/market.py +0 -0
  19. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/visualization/prices.py +0 -0
  20. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/__init__.py +0 -0
  21. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/app.py +0 -0
  22. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/auth.py +0 -0
  23. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/cache.py +0 -0
  24. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/get.py +0 -0
  25. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/__init__.py +0 -0
  26. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/client.py +0 -0
  27. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/info.py +0 -0
  28. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/list.py +0 -0
  29. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/prices.py +0 -0
  30. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/reports.py +0 -0
  31. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/statements.py +0 -0
  32. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/edinet/__init__.py +0 -0
  33. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/edinet/concurrent.py +0 -0
  34. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/__init__.py +0 -0
  35. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/calendar.py +0 -0
  36. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/concurrent.py +0 -0
  37. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/info.py +0 -0
  38. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/prices.py +0 -0
  39. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/schema.py +0 -0
  40. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/topix.py +0 -0
  41. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/py.typed +0 -0
  42. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/utils/__init__.py +0 -0
  43. {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/utils/config.py +0 -0
  44. {kabukit-0.7.1 → kabukit-0.7.2}/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.1
3
+ Version: 0.7.2
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>
@@ -28,14 +28,17 @@ License: MIT License
28
28
  Classifier: Development Status :: 4 - Beta
29
29
  Classifier: Programming Language :: Python
30
30
  Classifier: Programming Language :: Python :: 3.13
31
+ Classifier: Programming Language :: Python :: 3.14
31
32
  Requires-Dist: async-typer>=0.1.10
32
33
  Requires-Dist: httpx>=0.28.1
33
- Requires-Dist: platformdirs>=4.4.0
34
+ Requires-Dist: platformdirs>=4.5.0
34
35
  Requires-Dist: polars>=1.34.0
35
36
  Requires-Dist: python-dotenv>=1.1.1
36
- Requires-Dist: rich>=14.1.0
37
+ Requires-Dist: rich>=14.2.0
38
+ Requires-Dist: tenacity>=9.1.2
37
39
  Requires-Dist: tqdm>=4.67.1
38
40
  Requires-Dist: typer>=0.19.2
41
+ Requires-Dist: tzdata ; sys_platform == 'win32'
39
42
  Requires-Python: >=3.13
40
43
  Project-URL: Documentation, https://daizutabi.github.io/kabukit/
41
44
  Project-URL: Issues, https://github.com/daizutabi/kabukit/issues
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "kabukit"
7
- version = "0.7.1"
7
+ version = "0.7.2"
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" }
@@ -13,17 +13,20 @@ classifiers = [
13
13
  "Development Status :: 4 - Beta",
14
14
  "Programming Language :: Python",
15
15
  "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
16
17
  ]
17
18
  requires-python = ">=3.13"
18
19
  dependencies = [
19
20
  "async-typer>=0.1.10",
20
21
  "httpx>=0.28.1",
21
- "platformdirs>=4.4.0",
22
+ "platformdirs>=4.5.0",
22
23
  "polars>=1.34.0",
23
24
  "python-dotenv>=1.1.1",
24
- "rich>=14.1.0",
25
+ "rich>=14.2.0",
26
+ "tenacity>=9.1.2",
25
27
  "tqdm>=4.67.1",
26
28
  "typer>=0.19.2",
29
+ "tzdata; sys_platform == 'win32'",
27
30
  ]
28
31
 
29
32
  [project.scripts]
@@ -36,17 +39,18 @@ Issues = "https://github.com/daizutabi/kabukit/issues"
36
39
 
37
40
  [dependency-groups]
38
41
  dev = [
39
- "altair>=5.5.0",
42
+ "altair @ git+https://github.com/vega/altair.git@main",
40
43
  "basedpyright>=1.31.6",
41
44
  "marimo[lsp]>=0.16.5",
42
- "numpy>=2.3.3", # polars 1.33 type hinting workaround,
45
+ "numpy>=2.3.3",
46
+ "pre-commit>=4.3.0",
43
47
  "pytest-asyncio>=1.2.0",
44
48
  "pytest-clarity>=1.0.1",
45
49
  "pytest-cov>=7.0.0",
46
50
  "pytest-mock>=3.15.1",
47
51
  "pytest-randomly>=4.0.1",
48
52
  "pytest-xdist>=3.8.0",
49
- "scipy",
53
+ "scipy>=1.16.2",
50
54
  "vegafusion-python-embed>=1.6.9",
51
55
  "vegafusion>=2.0.3",
52
56
  "vl-convert-python>=1.8.0",
@@ -97,7 +101,7 @@ ignore = [
97
101
  [tool.ruff.format]
98
102
  exclude = ["schema.py"]
99
103
 
100
- [tool.basedpyright]
104
+ [tool.pyright]
101
105
  include = ["src", "tests"]
102
106
  reportAny = false
103
107
  reportExplicitAny = false
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import datetime
4
4
  from typing import TYPE_CHECKING
5
+ from zoneinfo import ZoneInfo
5
6
 
6
7
  import polars as pl
7
8
 
@@ -30,7 +31,7 @@ class Base:
30
31
  def write(self) -> Path:
31
32
  data_dir = self.data_dir()
32
33
  data_dir.mkdir(parents=True, exist_ok=True)
33
- path = datetime.datetime.today().strftime("%Y%m%d") # noqa: DTZ002
34
+ path = datetime.datetime.now(ZoneInfo("Asia/Tokyo")).strftime("%Y%m%d")
34
35
  filename = data_dir / f"{path}.parquet"
35
36
  self.data.write_parquet(filename)
36
37
  return filename
@@ -6,7 +6,9 @@ import zipfile
6
6
  from enum import StrEnum
7
7
  from typing import TYPE_CHECKING
8
8
 
9
+ import httpx
9
10
  from polars import DataFrame
11
+ from tenacity import retry, retry_if_exception, stop_after_attempt, wait_exponential
10
12
 
11
13
  from kabukit.core.client import Client
12
14
  from kabukit.utils.config import load_dotenv
@@ -24,6 +26,11 @@ API_VERSION = "v2"
24
26
  BASE_URL = f"https://api.edinet-fsa.go.jp/api/{API_VERSION}"
25
27
 
26
28
 
29
+ def is_retryable(e: BaseException) -> bool:
30
+ """Return True if the exception is a retryable network error."""
31
+ return isinstance(e, (httpx.ConnectTimeout, httpx.ReadTimeout, httpx.ConnectError))
32
+
33
+
27
34
  class AuthKey(StrEnum):
28
35
  """Environment variable keys for EDINET authentication."""
29
36
 
@@ -43,6 +50,12 @@ class EdinetClient(Client):
43
50
  if api_key:
44
51
  self.client.params = {"Subscription-Key": api_key}
45
52
 
53
+ @retry(
54
+ reraise=True,
55
+ stop=stop_after_attempt(3),
56
+ wait=wait_exponential(multiplier=1, min=2, max=10),
57
+ retry=retry_if_exception(is_retryable),
58
+ )
46
59
  async def get(self, url: str, params: QueryParamTypes) -> Response:
47
60
  resp = await self.client.get(url, params=params)
48
61
  resp.raise_for_status()
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import datetime
4
4
  from typing import TYPE_CHECKING
5
+ from zoneinfo import ZoneInfo
5
6
 
6
7
  import polars as pl
7
8
 
@@ -11,7 +12,11 @@ if TYPE_CHECKING:
11
12
 
12
13
  def clean_list(df: DataFrame, date: str | datetime.date) -> DataFrame:
13
14
  if isinstance(date, str):
14
- date = datetime.datetime.strptime(date, "%Y-%m-%d").date() # noqa: DTZ007
15
+ date = (
16
+ datetime.datetime.strptime(date, "%Y-%m-%d")
17
+ .replace(tzinfo=ZoneInfo("Asia/Tokyo"))
18
+ .date()
19
+ )
15
20
 
16
21
  null_columns = [c for c in df.columns if df[c].dtype == pl.Null]
17
22
 
@@ -21,7 +26,11 @@ def clean_list(df: DataFrame, date: str | datetime.date) -> DataFrame:
21
26
  )
22
27
  .with_columns(
23
28
  pl.lit(date).alias("Date"),
24
- pl.col("^.+DateTime$").str.to_datetime("%Y-%m-%d %H:%M", strict=False),
29
+ pl.col("^.+DateTime$").str.to_datetime(
30
+ "%Y-%m-%d %H:%M",
31
+ strict=False,
32
+ time_zone="Asia/Tokyo",
33
+ ),
25
34
  pl.col("^period.+$").str.to_date("%Y-%m-%d", strict=False),
26
35
  pl.col("^.+Flag$").cast(pl.Int8).cast(pl.Boolean),
27
36
  pl.col("^.+Code$").cast(pl.String),
@@ -5,6 +5,7 @@ import datetime
5
5
  import os
6
6
  from enum import StrEnum
7
7
  from typing import TYPE_CHECKING, final
8
+ from zoneinfo import ZoneInfo
8
9
 
9
10
  import polars as pl
10
11
  from polars import DataFrame
@@ -32,10 +33,9 @@ class _CalendarCache:
32
33
  async def get_holidays(self, client: JQuantsClient) -> list[datetime.date]:
33
34
  async with self._lock:
34
35
  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
- )
36
+ df = await client.get_calendar()
37
+ holidays = df.filter(pl.col("IsHoliday"))["Date"]
38
+ self._holidays = holidays.to_list()
39
39
  return self._holidays
40
40
 
41
41
 
@@ -261,7 +261,7 @@ class JQuantsClient(Client):
261
261
  clean: bool = True,
262
262
  ) -> DataFrame:
263
263
  """直近利用可能な日付の株価を取得する。"""
264
- today = datetime.date.today() # noqa: DTZ011
264
+ today = datetime.datetime.now(ZoneInfo("Asia/Tokyo")).date()
265
265
 
266
266
  for days in range(num_days):
267
267
  date = today - datetime.timedelta(days)
@@ -81,10 +81,10 @@ def _cast_bool(df: DataFrame) -> DataFrame:
81
81
  def with_date(df: DataFrame, holidays: list[datetime.date]) -> DataFrame:
82
82
  """`Date`列を追加する。
83
83
 
84
- 開示日が休日のとき、あるいは、開示時刻が15時以降の場合、Dateを開示日の翌営業日に設定する。
84
+ 開示日が休日のとき、あるいは、開示時刻が15時30分以降の場合、Dateを開示日の翌営業日に設定する。
85
85
  """
86
86
  is_after_hours = pl.col("DisclosedTime").is_null() | (
87
- pl.col("DisclosedTime") > datetime.time(15, 0)
87
+ pl.col("DisclosedTime") >= datetime.time(15, 30)
88
88
  )
89
89
 
90
90
  return df.select(
@@ -150,5 +150,5 @@ async def fetch(
150
150
  if callback:
151
151
  stream = (x if (r := callback(x)) is None else r async for x in stream)
152
152
 
153
- dfs = [df async for df in stream if not df.is_empty()]
153
+ dfs = [df async for df in stream if not df.is_empty()] # ty: ignore[not-iterable]
154
154
  return pl.concat(dfs) if dfs else pl.DataFrame()
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime
4
+ from zoneinfo import ZoneInfo
4
5
 
5
6
 
6
7
  def get_dates(days: int | None = None, years: int | None = None) -> list[datetime.date]:
@@ -11,7 +12,7 @@ def get_dates(days: int | None = None, years: int | None = None) -> list[datetim
11
12
  years (int | None): 過去years年の日付リストを取得する。
12
13
  daysが指定されている場合は無視される。
13
14
  """
14
- end_date = datetime.date.today() # noqa: DTZ011
15
+ end_date = datetime.datetime.now(ZoneInfo("Asia/Tokyo")).date()
15
16
 
16
17
  if days is not None:
17
18
  start_date = end_date - datetime.timedelta(days=days)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes