kabukit 0.7.1__py3-none-any.whl → 0.7.2__py3-none-any.whl

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/core/base.py CHANGED
@@ -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
kabukit/edinet/client.py CHANGED
@@ -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()
kabukit/edinet/doc.py CHANGED
@@ -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),
kabukit/jquants/client.py CHANGED
@@ -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()
kabukit/utils/date.py CHANGED
@@ -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)
@@ -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
@@ -12,7 +12,7 @@ kabukit/cli/auth.py,sha256=E4M9ChDrtCb1DvYrAds33k6eKlXZUQCL9yCNPyTSnUk,2727
12
12
  kabukit/cli/cache.py,sha256=cHjpNPi1BnaPwXXIFEpbmjCA8Cvtov9yNcbRm12109M,1745
13
13
  kabukit/cli/get.py,sha256=z_bo1EdABkO0T4Mpdmb0qSIMVdWRWyopN7IX9VoTxxE,4815
14
14
  kabukit/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- kabukit/core/base.py,sha256=kHLTbqTX-ZFPpNjkIpe_fxsBzY4vD3Tjyf7zpyK20CY,1691
15
+ kabukit/core/base.py,sha256=WVFvv70n99GTqLkhkRnibLRgYP-eMh6lYY3R__q_YXY,1725
16
16
  kabukit/core/client.py,sha256=tVq1r3zpOfjmOtnRI1KPZHgTgBZYIpJzfw15i2kAM48,676
17
17
  kabukit/core/info.py,sha256=knL0rX18RNfra7iBhMK9H0GuaIapzYbxMeizkfuM1QM,88
18
18
  kabukit/core/list.py,sha256=JQHE6aoYreE2Oik_LJ9x0xE7nmkGsx688XYASaVqooE,88
@@ -20,25 +20,25 @@ kabukit/core/prices.py,sha256=dPNgCTFf-eE7-C-e_2vy9uqVZT7O55k2JKUNG1CPFX0,17728
20
20
  kabukit/core/reports.py,sha256=G7p8IcUOHDopZJMxMLnYhss9hIq4gCKEXFixINQI_7w,91
21
21
  kabukit/core/statements.py,sha256=_n4-8G284e6y_MwhvUq4xBK4bapQHk-Zmwu09C_r7wU,2790
22
22
  kabukit/edinet/__init__.py,sha256=PKa4D-jVpeoOkdVp9NwwpgAiGEBjqvmJLmpzF-9SlVk,101
23
- kabukit/edinet/client.py,sha256=BlM7pjXpKweOo-ses41JiKinqRn4qerMN5NF7hgYKgg,3275
23
+ kabukit/edinet/client.py,sha256=HbxUvl6vKmfJ4-quIwCGspVOuHTMFbSolZXbhfG891U,3760
24
24
  kabukit/edinet/concurrent.py,sha256=2YPzIFuuOB8-gL3CnHIlP486QH5d21qjKNKGCFK7Hzk,4707
25
- kabukit/edinet/doc.py,sha256=6ZDgmm8DHmEMOA4NjNz-dHLMc7IzzYn-nVyMQGLWb8I,1220
25
+ kabukit/edinet/doc.py,sha256=plxJ-o5-8NzllHY7Sn0M8KbyIenDAfh5XISLfpq27Ag,1410
26
26
  kabukit/jquants/__init__.py,sha256=xY0H6x4_51ZBqbrT2TdmGGFbTD-hosZiDzVIz4UXPv0,112
27
27
  kabukit/jquants/calendar.py,sha256=Vz4Degedgx8qENHWri2MTkIbkuIRfO28CXRq7bZaHGE,333
28
- kabukit/jquants/client.py,sha256=lDqh8ysvzC9dKU9ervO7GOjDQuxtLOo5npTu7t32V0g,14229
28
+ kabukit/jquants/client.py,sha256=kp3xr8LH8NxO4lerzVseFPBvnUZw4grrpULExN89UoU,14241
29
29
  kabukit/jquants/concurrent.py,sha256=86xYD_zPLnR24xZhfSS0mAcUM-dvUvpozzyKASSHiwo,3345
30
30
  kabukit/jquants/info.py,sha256=MZbtg0L-YIkegWnCd3AvTs1AopV3z58ImgOnxgJgJbw,997
31
31
  kabukit/jquants/prices.py,sha256=oApQpdgzHwPw11XHpdg8ccZS7kybGtV8APZlpD2L3Yw,882
32
32
  kabukit/jquants/schema.py,sha256=aILl9cp9JDOaT2o3UlfavPGQC5s7n0ZkVBGKiTzdogs,9768
33
- kabukit/jquants/statements.py,sha256=6O03xx4LJtCkk7LgEN1zHOADZzkJJDC-x3se6ICo4mw,2745
33
+ kabukit/jquants/statements.py,sha256=CHJKwM44-TAUPMrieA1VVwHT89KpcXxEfJcbh9y-dWI,2752
34
34
  kabukit/jquants/topix.py,sha256=oU84WAbmd4U77fuKvj_EUpZv3Pu_Sf0HEW4Y0dB8508,326
35
35
  kabukit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  kabukit/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- kabukit/utils/concurrent.py,sha256=aTiCS6nqQtWBy29xykLHSU1LTr6LuGraxEnZco99EIM,4692
37
+ kabukit/utils/concurrent.py,sha256=qR3gvhUBYOjwtVT_Hluy1g-XaaFL9ysHvZz3WUE8G1k,4720
38
38
  kabukit/utils/config.py,sha256=fqUdlhVjgiWEcsLFmPckp-dXUleVz8Ypdy_NnrMIBfY,708
39
- kabukit/utils/date.py,sha256=DEC6Ac5LS8eiW6JtrmcD3U1pX4qzXtx4ale0swpO4Ag,937
39
+ kabukit/utils/date.py,sha256=eB5ONCkqnvOiAg1Hvg1pN4OkrxLR47urvOAD5BF5yL0,982
40
40
  kabukit/utils/params.py,sha256=qcaJbf6CWPUoZAZsYDTaZSnBUWeAersbWnR_iiYW9GM,1108
41
- kabukit-0.7.1.dist-info/WHEEL,sha256=I8-bO5cg2sb8TH6ZM6EgCP87Y1cV_f9UGgWnfAhVOZI,78
42
- kabukit-0.7.1.dist-info/entry_points.txt,sha256=vvX771TemoM-35vVizW3JJ70HvRXnd2tX4P1Btzyoxs,46
43
- kabukit-0.7.1.dist-info/METADATA,sha256=l322FK4twD7OLOHI0NIdC3wX7dlR3Jyps_EQpcm6fI0,3233
44
- kabukit-0.7.1.dist-info/RECORD,,
41
+ kabukit-0.7.2.dist-info/WHEEL,sha256=X16MKk8bp2DRsAuyteHJ-9qOjzmnY0x1aj0P1ftqqWA,78
42
+ kabukit-0.7.2.dist-info/entry_points.txt,sha256=vvX771TemoM-35vVizW3JJ70HvRXnd2tX4P1Btzyoxs,46
43
+ kabukit-0.7.2.dist-info/METADATA,sha256=bOioHG440DSt2ywamoIM3vqD87lp1qMUPgkZUZy1u30,3363
44
+ kabukit-0.7.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.0
2
+ Generator: uv 0.9.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any