kabukit 0.7.0__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.0 → kabukit-0.7.2}/PKG-INFO +6 -4
  2. {kabukit-0.7.0 → kabukit-0.7.2}/pyproject.toml +14 -11
  3. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/analysis/visualization/__init__.py +1 -1
  4. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/core/base.py +2 -1
  5. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/core/info.py +0 -5
  6. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/core/list.py +0 -5
  7. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/core/reports.py +0 -5
  8. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/edinet/client.py +13 -0
  9. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/edinet/doc.py +11 -2
  10. kabukit-0.7.2/src/kabukit/jquants/calendar.py +12 -0
  11. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/jquants/client.py +92 -32
  12. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/jquants/statements.py +3 -17
  13. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/utils/concurrent.py +1 -1
  14. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/utils/date.py +2 -1
  15. {kabukit-0.7.0 → kabukit-0.7.2}/LICENSE +0 -0
  16. {kabukit-0.7.0 → kabukit-0.7.2}/README.md +0 -0
  17. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/__init__.py +0 -0
  18. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/analysis/__init__.py +0 -0
  19. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/analysis/indicators.py +0 -0
  20. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/analysis/preprocess.py +0 -0
  21. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/analysis/screener.py +0 -0
  22. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/analysis/visualization/market.py +0 -0
  23. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/analysis/visualization/prices.py +0 -0
  24. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/cli/__init__.py +0 -0
  25. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/cli/app.py +0 -0
  26. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/cli/auth.py +0 -0
  27. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/cli/cache.py +0 -0
  28. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/cli/get.py +0 -0
  29. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/core/__init__.py +0 -0
  30. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/core/client.py +0 -0
  31. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/core/prices.py +0 -0
  32. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/core/statements.py +0 -0
  33. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/edinet/__init__.py +0 -0
  34. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/edinet/concurrent.py +0 -0
  35. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/jquants/__init__.py +0 -0
  36. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/jquants/concurrent.py +0 -0
  37. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/jquants/info.py +0 -0
  38. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/jquants/prices.py +0 -0
  39. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/jquants/schema.py +0 -0
  40. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/jquants/topix.py +0 -0
  41. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/py.typed +0 -0
  42. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/utils/__init__.py +0 -0
  43. {kabukit-0.7.0 → kabukit-0.7.2}/src/kabukit/utils/config.py +0 -0
  44. {kabukit-0.7.0 → 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.0
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,15 +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
- Requires-Dist: holidays>=0.82
33
33
  Requires-Dist: httpx>=0.28.1
34
- Requires-Dist: platformdirs>=4.4.0
34
+ Requires-Dist: platformdirs>=4.5.0
35
35
  Requires-Dist: polars>=1.34.0
36
36
  Requires-Dist: python-dotenv>=1.1.1
37
- Requires-Dist: rich>=14.1.0
37
+ Requires-Dist: rich>=14.2.0
38
+ Requires-Dist: tenacity>=9.1.2
38
39
  Requires-Dist: tqdm>=4.67.1
39
40
  Requires-Dist: typer>=0.19.2
41
+ Requires-Dist: tzdata ; sys_platform == 'win32'
40
42
  Requires-Python: >=3.13
41
43
  Project-URL: Documentation, https://daizutabi.github.io/kabukit/
42
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.0"
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,18 +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
- "holidays>=0.82",
21
21
  "httpx>=0.28.1",
22
- "platformdirs>=4.4.0",
22
+ "platformdirs>=4.5.0",
23
23
  "polars>=1.34.0",
24
24
  "python-dotenv>=1.1.1",
25
- "rich>=14.1.0",
25
+ "rich>=14.2.0",
26
+ "tenacity>=9.1.2",
26
27
  "tqdm>=4.67.1",
27
28
  "typer>=0.19.2",
29
+ "tzdata; sys_platform == 'win32'",
28
30
  ]
29
31
 
30
32
  [project.scripts]
@@ -37,17 +39,18 @@ Issues = "https://github.com/daizutabi/kabukit/issues"
37
39
 
38
40
  [dependency-groups]
39
41
  dev = [
40
- "altair>=5.5.0",
42
+ "altair @ git+https://github.com/vega/altair.git@main",
41
43
  "basedpyright>=1.31.6",
42
44
  "marimo[lsp]>=0.16.5",
43
- "numpy>=2.3.3", # polars 1.33 type hinting workaround,
45
+ "numpy>=2.3.3",
46
+ "pre-commit>=4.3.0",
44
47
  "pytest-asyncio>=1.2.0",
45
48
  "pytest-clarity>=1.0.1",
46
49
  "pytest-cov>=7.0.0",
47
50
  "pytest-mock>=3.15.1",
48
51
  "pytest-randomly>=4.0.1",
49
52
  "pytest-xdist>=3.8.0",
50
- "scipy",
53
+ "scipy>=1.16.2",
51
54
  "vegafusion-python-embed>=1.6.9",
52
55
  "vegafusion>=2.0.3",
53
56
  "vl-convert-python>=1.8.0",
@@ -69,7 +72,7 @@ skip_covered = true
69
72
  [tool.ruff]
70
73
  line-length = 88
71
74
  target-version = "py313"
72
- include = ["src", "tests"]
75
+ include = ["src/**/*.py", "tests/**/*.py"]
73
76
 
74
77
  [tool.ruff.lint]
75
78
  select = ["ALL"]
@@ -91,14 +94,14 @@ ignore = [
91
94
  ]
92
95
 
93
96
  [tool.ruff.lint.per-file-ignores]
94
- "tests/*" = ["ANN", "FBT", "S101", "S607", "PLC2401"]
97
+ "tests/**/*.py" = ["ANN", "FBT", "S101", "S607", "PLC2401"]
95
98
  "schema.py" = ["E501"]
96
- "notebooks/*" = ["F704", "PLE1142"]
99
+ "notebooks/**/*.py" = ["F704", "PLE1142"]
97
100
 
98
101
  [tool.ruff.format]
99
102
  exclude = ["schema.py"]
100
103
 
101
- [tool.basedpyright]
104
+ [tool.pyright]
102
105
  include = ["src", "tests"]
103
106
  reportAny = false
104
107
  reportExplicitAny = false
@@ -4,4 +4,4 @@ from .prices import plot_prices
4
4
  __all__ = [
5
5
  "plot_prices",
6
6
  "plot_topix_timeseries",
7
- ]
7
+ ]
@@ -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
@@ -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
@@ -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),
@@ -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,11 @@
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
8
+ from zoneinfo import ZoneInfo
7
9
 
8
10
  import polars as pl
9
11
  from polars import DataFrame
@@ -12,7 +14,7 @@ from kabukit.core.client import Client
12
14
  from kabukit.utils.config import load_dotenv, set_key
13
15
  from kabukit.utils.params import get_params
14
16
 
15
- from . import info, prices, statements, topix
17
+ from . import calendar, info, prices, statements, topix
16
18
 
17
19
  if TYPE_CHECKING:
18
20
  from collections.abc import AsyncIterator
@@ -21,6 +23,25 @@ if TYPE_CHECKING:
21
23
  from httpx import HTTPStatusError # noqa: F401
22
24
  from httpx._types import QueryParamTypes
23
25
 
26
+
27
+ @final
28
+ class _CalendarCache:
29
+ def __init__(self) -> None:
30
+ self._holidays: list[datetime.date] | None = None
31
+ self._lock = asyncio.Lock()
32
+
33
+ async def get_holidays(self, client: JQuantsClient) -> list[datetime.date]:
34
+ async with self._lock:
35
+ if self._holidays is None:
36
+ df = await client.get_calendar()
37
+ holidays = df.filter(pl.col("IsHoliday"))["Date"]
38
+ self._holidays = holidays.to_list()
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,
@@ -240,7 +261,7 @@ class JQuantsClient(Client):
240
261
  clean: bool = True,
241
262
  ) -> DataFrame:
242
263
  """直近利用可能な日付の株価を取得する。"""
243
- today = datetime.date.today() # noqa: DTZ011
264
+ today = datetime.datetime.now(ZoneInfo("Asia/Tokyo")).date()
244
265
 
245
266
  for days in range(num_days):
246
267
  date = today - datetime.timedelta(days)
@@ -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,27 +78,15 @@ 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
- 開示日が休日のとき、あるいは、開示時刻が15時以降の場合、Dateを開示日の翌営業日に設定する。
84
+ 開示日が休日のとき、あるいは、開示時刻が15時30分以降の場合、Dateを開示日の翌営業日に設定する。
97
85
  """
98
86
  is_after_hours = pl.col("DisclosedTime").is_null() | (
99
- pl.col("DisclosedTime") > datetime.time(15, 0)
87
+ pl.col("DisclosedTime") >= datetime.time(15, 30)
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))
@@ -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