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.
- {kabukit-0.7.1 → kabukit-0.7.2}/PKG-INFO +6 -3
- {kabukit-0.7.1 → kabukit-0.7.2}/pyproject.toml +11 -7
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/base.py +2 -1
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/edinet/client.py +13 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/edinet/doc.py +11 -2
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/client.py +5 -5
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/statements.py +2 -2
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/utils/concurrent.py +1 -1
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/utils/date.py +2 -1
- {kabukit-0.7.1 → kabukit-0.7.2}/LICENSE +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/README.md +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/__init__.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/__init__.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/indicators.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/preprocess.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/screener.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/visualization/__init__.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/visualization/market.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/analysis/visualization/prices.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/__init__.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/app.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/auth.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/cache.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/cli/get.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/__init__.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/client.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/info.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/list.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/prices.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/reports.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/core/statements.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/edinet/__init__.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/edinet/concurrent.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/__init__.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/calendar.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/concurrent.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/info.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/prices.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/schema.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/jquants/topix.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/py.typed +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/utils/__init__.py +0 -0
- {kabukit-0.7.1 → kabukit-0.7.2}/src/kabukit/utils/config.py +0 -0
- {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.
|
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.
|
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.
|
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.
|
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.
|
22
|
+
"platformdirs>=4.5.0",
|
22
23
|
"polars>=1.34.0",
|
23
24
|
"python-dotenv>=1.1.1",
|
24
|
-
"rich>=14.
|
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
|
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",
|
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.
|
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.
|
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 =
|
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(
|
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
|
-
|
36
|
-
|
37
|
-
|
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.
|
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
|
84
|
+
開示日が休日のとき、あるいは、開示時刻が15時30分以降の場合、Dateを開示日の翌営業日に設定する。
|
85
85
|
"""
|
86
86
|
is_after_hours = pl.col("DisclosedTime").is_null() | (
|
87
|
-
pl.col("DisclosedTime")
|
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.
|
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
|
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
|