kabukit 0.6.0__py3-none-any.whl → 0.7.1__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.
@@ -0,0 +1,7 @@
1
+ from .market import plot_topix_timeseries
2
+ from .prices import plot_prices
3
+
4
+ __all__ = [
5
+ "plot_prices",
6
+ "plot_topix_timeseries",
7
+ ]
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import altair as alt
6
+
7
+ if TYPE_CHECKING:
8
+ from polars import DataFrame
9
+
10
+ # pyright: reportUnknownMemberType=false
11
+
12
+
13
+ def plot_topix_timeseries(df: DataFrame) -> alt.Chart:
14
+ """TOPIXの時系列データを折れ線グラフでプロットする。"""
15
+ return (
16
+ alt.Chart(df, title="TOPIX 時系列チャート")
17
+ .mark_line()
18
+ .encode(
19
+ x=alt.X("Date:T", title="日付"),
20
+ y=alt.Y("Close:Q", title="終値", scale=alt.Scale(zero=False)),
21
+ tooltip=[
22
+ alt.Tooltip("Date:T", title="日付"),
23
+ alt.Tooltip("Open:Q", title="始値"),
24
+ alt.Tooltip("High:Q", title="高値"),
25
+ alt.Tooltip("Low:Q", title="安値"),
26
+ alt.Tooltip("Close:Q", title="終値"),
27
+ ],
28
+ )
29
+ )
kabukit/cli/app.py CHANGED
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import typer
6
6
  from async_typer import AsyncTyper # pyright: ignore[reportMissingTypeStubs]
7
7
 
8
- from . import auth, get
8
+ from . import auth, cache, get
9
9
 
10
10
  app = AsyncTyper(
11
11
  add_completion=False,
@@ -13,6 +13,7 @@ app = AsyncTyper(
13
13
  )
14
14
  app.add_typer(auth.app, name="auth")
15
15
  app.add_typer(get.app, name="get")
16
+ app.add_typer(cache.app, name="cache")
16
17
 
17
18
 
18
19
  @app.command()
kabukit/cli/cache.py ADDED
@@ -0,0 +1,61 @@
1
+ """Cache management commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import shutil
6
+ from typing import TYPE_CHECKING
7
+
8
+ import typer
9
+ from rich.console import Console
10
+ from rich.tree import Tree
11
+
12
+ from kabukit.utils.config import get_cache_dir
13
+
14
+ if TYPE_CHECKING:
15
+ from pathlib import Path
16
+
17
+ app = typer.Typer(add_completion=False, help="キャッシュを管理します。")
18
+
19
+
20
+ def add_to_tree(tree: Tree, path: Path) -> None:
21
+ for p in sorted(path.iterdir()):
22
+ if p.is_dir():
23
+ branch = tree.add(p.name)
24
+ add_to_tree(branch, p)
25
+ else:
26
+ tree.add(p.name)
27
+
28
+
29
+ @app.command()
30
+ def tree() -> None:
31
+ """キャッシュディレクトリのツリー構造を表示します。"""
32
+ cache_dir = get_cache_dir()
33
+
34
+ if not cache_dir.exists():
35
+ typer.echo(f"キャッシュディレクトリ '{cache_dir}' は存在しません。")
36
+ return
37
+
38
+ console = Console()
39
+ tree_view = Tree(str(cache_dir))
40
+ add_to_tree(tree_view, cache_dir)
41
+ console.print(tree_view)
42
+
43
+
44
+ @app.command()
45
+ def clean() -> None:
46
+ """キャッシュディレクトリを削除します。"""
47
+ cache_dir = get_cache_dir()
48
+
49
+ if not cache_dir.exists():
50
+ typer.echo(f"キャッシュディレクトリ '{cache_dir}' は存在しません。")
51
+ return
52
+
53
+ try:
54
+ shutil.rmtree(cache_dir)
55
+ msg = f"キャッシュディレクトリ '{cache_dir}' を正常にクリーンアップしました。"
56
+ typer.echo(msg)
57
+ except OSError:
58
+ msg = f"キャッシュディレクトリ '{cache_dir}' のクリーンアップ中に"
59
+ msg += "エラーが発生しました。"
60
+ typer.secho(msg, fg=typer.colors.RED, bold=True)
61
+ raise typer.Exit(1) from None
kabukit/cli/get.py CHANGED
@@ -57,7 +57,12 @@ async def _fetch(
57
57
 
58
58
  from kabukit.jquants.concurrent import fetch_all
59
59
 
60
- df = await fetch_all(target, progress=tqdm.asyncio.tqdm, **kwargs)
60
+ try:
61
+ df = await fetch_all(target, progress=tqdm.asyncio.tqdm, **kwargs)
62
+ except KeyboardInterrupt:
63
+ typer.echo("中断しました。")
64
+ raise typer.Exit(1) from None
65
+
61
66
  typer.echo(df)
62
67
  path = cls(df).write()
63
68
  typer.echo(f"全銘柄の{message}を '{path}' に保存しました。")
@@ -100,7 +105,12 @@ async def list_() -> None:
100
105
  from kabukit.core.list import List
101
106
  from kabukit.edinet.concurrent import fetch_list
102
107
 
103
- df = await fetch_list(years=10, progress=tqdm.asyncio.tqdm)
108
+ try:
109
+ df = await fetch_list(years=10, progress=tqdm.asyncio.tqdm)
110
+ except (KeyboardInterrupt, RuntimeError):
111
+ typer.echo("中断しました。")
112
+ raise typer.Exit(1) from None
113
+
104
114
  typer.echo(df)
105
115
  path = List(df).write()
106
116
  typer.echo(f"報告書一覧を '{path}' に保存しました。")
@@ -125,7 +135,12 @@ async def reports() -> None:
125
135
  lst = df.filter(pl.col("csvFlag"), pl.col("secCode").is_not_null())
126
136
  doc_ids = lst["docID"].unique()
127
137
 
128
- df = await fetch_csv(doc_ids, limit=1000, progress=tqdm.asyncio.tqdm)
138
+ try:
139
+ df = await fetch_csv(doc_ids, limit=1000, progress=tqdm.asyncio.tqdm)
140
+ except (KeyboardInterrupt, RuntimeError):
141
+ typer.echo("中断しました。")
142
+ raise typer.Exit(1) from None
143
+
129
144
  typer.echo(df)
130
145
  path = Reports(df).write()
131
146
  typer.echo(f"報告書を '{path}' に保存しました。")
kabukit/core/base.py CHANGED
@@ -1,14 +1,15 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import datetime
4
- from pathlib import Path
5
4
  from typing import TYPE_CHECKING
6
5
 
7
6
  import polars as pl
8
- from platformdirs import user_cache_dir
7
+
8
+ from kabukit.utils.config import get_cache_dir
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from collections.abc import Iterable
12
+ from pathlib import Path
12
13
  from typing import Any, Self
13
14
 
14
15
  from polars import DataFrame
@@ -24,7 +25,7 @@ class Base:
24
25
  @classmethod
25
26
  def data_dir(cls) -> Path:
26
27
  clsname = cls.__name__.lower()
27
- return Path(user_cache_dir("kabukit")) / clsname
28
+ return get_cache_dir() / clsname
28
29
 
29
30
  def write(self) -> Path:
30
31
  data_dir = self.data_dir()
kabukit/core/info.py CHANGED
@@ -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
kabukit/core/list.py CHANGED
@@ -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
kabukit/core/reports.py CHANGED
@@ -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
+ )
kabukit/jquants/client.py CHANGED
@@ -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)
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING
5
5
  from kabukit.utils import concurrent
6
6
 
7
7
  from .client import JQuantsClient
8
- from .info import get_codes
8
+ from .info import get_target_codes
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from collections.abc import Iterable
@@ -80,7 +80,7 @@ async def fetch_all(
80
80
  すべての銘柄の財務情報を含む単一のDataFrame。
81
81
  """
82
82
 
83
- codes = await get_codes()
83
+ codes = await get_target_codes()
84
84
  codes = codes[:limit]
85
85
 
86
86
  return await fetch(
kabukit/jquants/info.py CHANGED
@@ -11,10 +11,13 @@ def clean(df: DataFrame) -> DataFrame:
11
11
  ).drop("^.+Code$", "CompanyNameEnglish")
12
12
 
13
13
 
14
- async def get_codes() -> list[str]:
15
- """銘柄コードのリストを返す。
14
+ async def get_target_codes() -> list[str]:
15
+ """分析対象となる銘柄コードのリストを返す。
16
16
 
17
- 市場「TOKYO PRO MARKET」と業種「その他」を除外した銘柄を対象とする。
17
+ 以下の条件を満たす銘柄は対象外とする。
18
+ - 市場: TOKYO PRO MARKET
19
+ - 業種: その他 -- (投資信託など)
20
+ - 優先株式
18
21
  """
19
22
  from .client import JQuantsClient
20
23
 
@@ -25,6 +28,7 @@ async def get_codes() -> list[str]:
25
28
  info.filter(
26
29
  pl.col("MarketCodeName") != "TOKYO PRO MARKET",
27
30
  pl.col("Sector17CodeName") != "その他",
31
+ ~pl.col("CompanyName").str.contains("優先株式"),
28
32
  )
29
33
  .get_column("Code")
30
34
  .to_list()
@@ -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))
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- from dataclasses import dataclass
4
+ import contextlib
5
5
  from typing import TYPE_CHECKING, Any, Protocol
6
6
 
7
7
  import polars as pl
@@ -48,10 +48,17 @@ async def collect[R](
48
48
  async with semaphore:
49
49
  return await awaitable
50
50
 
51
- futures = (run(awaitable) for awaitable in awaitables)
51
+ tasks = {asyncio.create_task(run(awaitable)) for awaitable in awaitables}
52
52
 
53
- async for future in asyncio.as_completed(futures):
54
- yield await future
53
+ try:
54
+ async for future in asyncio.as_completed(tasks):
55
+ with contextlib.suppress(asyncio.CancelledError):
56
+ yield await future
57
+ finally:
58
+ for task in tasks:
59
+ task.cancel()
60
+ if tasks:
61
+ await asyncio.gather(*tasks, return_exceptions=True)
55
62
 
56
63
 
57
64
  async def collect_fn[T, R](
@@ -92,19 +99,16 @@ type Callback = Callable[[DataFrame], DataFrame | None]
92
99
  type Progress = type[progress_bar[Any] | tqdm[Any]] | _Progress
93
100
 
94
101
 
95
- @dataclass
96
- class Stream:
97
- cls: type[Client]
98
- resource: str
99
- args: list[Any]
100
- max_concurrency: int | None = None
101
-
102
- async def __aiter__(self) -> AsyncIterator[DataFrame]:
103
- async with self.cls() as client:
104
- fn = getattr(client, f"get_{self.resource}")
102
+ async def get_stream(
103
+ client: Client,
104
+ resource: str,
105
+ args: list[Any],
106
+ max_concurrency: int | None = None,
107
+ ) -> AsyncIterator[DataFrame]:
108
+ fn = getattr(client, f"get_{resource}")
105
109
 
106
- async for df in collect_fn(fn, self.args, self.max_concurrency):
107
- yield df
110
+ async for df in collect_fn(fn, args, max_concurrency):
111
+ yield df
108
112
 
109
113
 
110
114
  async def fetch(
@@ -137,13 +141,14 @@ async def fetch(
137
141
  すべての情報を含む単一のDataFrame。
138
142
  """
139
143
  args = list(args)
140
- stream = Stream(cls, resource, args, max_concurrency)
144
+ async with cls() as client:
145
+ stream = get_stream(client, resource, args, max_concurrency)
141
146
 
142
- if progress:
143
- stream = progress(aiter(stream), total=len(args))
147
+ if progress:
148
+ stream = progress(stream, total=len(args))
144
149
 
145
- if callback:
146
- stream = (x if (r := callback(x)) is None else r async for x in stream)
150
+ if callback:
151
+ stream = (x if (r := callback(x)) is None else r async for x in stream)
147
152
 
148
- dfs = [df async for df in stream if not df.is_empty()]
149
- return pl.concat(dfs) if dfs else pl.DataFrame()
153
+ dfs = [df async for df in stream if not df.is_empty()]
154
+ return pl.concat(dfs) if dfs else pl.DataFrame()
kabukit/utils/config.py CHANGED
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from pathlib import Path
4
4
 
5
5
  import dotenv
6
- from platformdirs import user_config_dir
6
+ from platformdirs import user_cache_dir, user_config_dir
7
7
 
8
8
 
9
9
  def get_dotenv_path() -> Path:
@@ -21,3 +21,7 @@ def set_key(key: str, value: str) -> tuple[bool | None, str, str]:
21
21
  def load_dotenv() -> bool:
22
22
  dotenv_path = get_dotenv_path()
23
23
  return dotenv.load_dotenv(dotenv_path)
24
+
25
+
26
+ def get_cache_dir() -> Path:
27
+ return Path(user_cache_dir("kabukit"))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: kabukit
3
- Version: 0.6.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,11 +29,12 @@ 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.81
33
32
  Requires-Dist: httpx>=0.28.1
34
33
  Requires-Dist: platformdirs>=4.4.0
35
34
  Requires-Dist: polars>=1.34.0
36
35
  Requires-Dist: python-dotenv>=1.1.1
36
+ Requires-Dist: rich>=14.1.0
37
+ Requires-Dist: tqdm>=4.67.1
37
38
  Requires-Dist: typer>=0.19.2
38
39
  Requires-Python: >=3.13
39
40
  Project-URL: Documentation, https://daizutabi.github.io/kabukit/
@@ -3,38 +3,42 @@ kabukit/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
3
3
  kabukit/analysis/indicators.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  kabukit/analysis/preprocess.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  kabukit/analysis/screener.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- kabukit/analysis/visualization.py,sha256=VS9WXBKcdOYsnVe-OR3cWP_nru3Oa-rBOK5SUwsxfyQ,1857
6
+ kabukit/analysis/visualization/__init__.py,sha256=4ommaflk8WK9plu5HecUQuK9ORLZA050M7E1fGAbAeE,137
7
+ kabukit/analysis/visualization/market.py,sha256=M45dPi7BUsYxl8JEy_aE_uIi4r7Y6Hv842QM7hEz0PM,886
8
+ kabukit/analysis/visualization/prices.py,sha256=VS9WXBKcdOYsnVe-OR3cWP_nru3Oa-rBOK5SUwsxfyQ,1857
7
9
  kabukit/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- kabukit/cli/app.py,sha256=HKzxE2ue_VvdVazCFcU25PmDs3LJIjKvO7l-jdYJV_E,531
10
+ kabukit/cli/app.py,sha256=B5aiSWDTmouiz4aagBbHL67z4bdTqUDGGGQkDxlaCyU,577
9
11
  kabukit/cli/auth.py,sha256=E4M9ChDrtCb1DvYrAds33k6eKlXZUQCL9yCNPyTSnUk,2727
10
- kabukit/cli/get.py,sha256=5t_FXNT64tfP4m_mWGfZdOXEQcRea1UIcPIHk4QC4Nc,4405
12
+ kabukit/cli/cache.py,sha256=cHjpNPi1BnaPwXXIFEpbmjCA8Cvtov9yNcbRm12109M,1745
13
+ kabukit/cli/get.py,sha256=z_bo1EdABkO0T4Mpdmb0qSIMVdWRWyopN7IX9VoTxxE,4815
11
14
  kabukit/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- kabukit/core/base.py,sha256=YQfGw3P2QshRdut3KhtrAleVvnFsIPOw5oGA9azypAA,1695
15
+ kabukit/core/base.py,sha256=kHLTbqTX-ZFPpNjkIpe_fxsBzY4vD3Tjyf7zpyK20CY,1691
13
16
  kabukit/core/client.py,sha256=tVq1r3zpOfjmOtnRI1KPZHgTgBZYIpJzfw15i2kAM48,676
14
- kabukit/core/info.py,sha256=5BX7mDavF6g-b0KzHgKIFHUS5701BoaHtw1JcHSsy94,174
15
- kabukit/core/list.py,sha256=AjnXzC9XIu21l6IBEHHAS5VAnfxTfkAA9m1WAOZJNa8,174
17
+ kabukit/core/info.py,sha256=knL0rX18RNfra7iBhMK9H0GuaIapzYbxMeizkfuM1QM,88
18
+ kabukit/core/list.py,sha256=JQHE6aoYreE2Oik_LJ9x0xE7nmkGsx688XYASaVqooE,88
16
19
  kabukit/core/prices.py,sha256=dPNgCTFf-eE7-C-e_2vy9uqVZT7O55k2JKUNG1CPFX0,17728
17
- kabukit/core/reports.py,sha256=ch_xe84GbB17JTfmY3ArQqneQ2XOuvrAykBTAyNmWuM,177
20
+ kabukit/core/reports.py,sha256=G7p8IcUOHDopZJMxMLnYhss9hIq4gCKEXFixINQI_7w,91
18
21
  kabukit/core/statements.py,sha256=_n4-8G284e6y_MwhvUq4xBK4bapQHk-Zmwu09C_r7wU,2790
19
22
  kabukit/edinet/__init__.py,sha256=PKa4D-jVpeoOkdVp9NwwpgAiGEBjqvmJLmpzF-9SlVk,101
20
23
  kabukit/edinet/client.py,sha256=BlM7pjXpKweOo-ses41JiKinqRn4qerMN5NF7hgYKgg,3275
21
24
  kabukit/edinet/concurrent.py,sha256=2YPzIFuuOB8-gL3CnHIlP486QH5d21qjKNKGCFK7Hzk,4707
22
25
  kabukit/edinet/doc.py,sha256=6ZDgmm8DHmEMOA4NjNz-dHLMc7IzzYn-nVyMQGLWb8I,1220
23
26
  kabukit/jquants/__init__.py,sha256=xY0H6x4_51ZBqbrT2TdmGGFbTD-hosZiDzVIz4UXPv0,112
24
- kabukit/jquants/client.py,sha256=eznqnueUMF2_px5dpCwry53FLyE2nKkI-L8nCu31rhk,12323
25
- kabukit/jquants/concurrent.py,sha256=n_HMIQEkj1OqDo75iybzcpzVJkP-pNzjNnv3XTA5sxQ,3331
26
- kabukit/jquants/info.py,sha256=AUymo6EtLdJ_L-0yNG1gd7rJ8Jkjw7tvsDM5Vw4QNFA,843
27
+ kabukit/jquants/calendar.py,sha256=Vz4Degedgx8qENHWri2MTkIbkuIRfO28CXRq7bZaHGE,333
28
+ kabukit/jquants/client.py,sha256=lDqh8ysvzC9dKU9ervO7GOjDQuxtLOo5npTu7t32V0g,14229
29
+ kabukit/jquants/concurrent.py,sha256=86xYD_zPLnR24xZhfSS0mAcUM-dvUvpozzyKASSHiwo,3345
30
+ kabukit/jquants/info.py,sha256=MZbtg0L-YIkegWnCd3AvTs1AopV3z58ImgOnxgJgJbw,997
27
31
  kabukit/jquants/prices.py,sha256=oApQpdgzHwPw11XHpdg8ccZS7kybGtV8APZlpD2L3Yw,882
28
32
  kabukit/jquants/schema.py,sha256=aILl9cp9JDOaT2o3UlfavPGQC5s7n0ZkVBGKiTzdogs,9768
29
- kabukit/jquants/statements.py,sha256=fW9YGuqK5W64aWdYtVFzgF14L9H68HPdIfYWxSjc9yg,3173
33
+ kabukit/jquants/statements.py,sha256=6O03xx4LJtCkk7LgEN1zHOADZzkJJDC-x3se6ICo4mw,2745
30
34
  kabukit/jquants/topix.py,sha256=oU84WAbmd4U77fuKvj_EUpZv3Pu_Sf0HEW4Y0dB8508,326
31
35
  kabukit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
36
  kabukit/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
- kabukit/utils/concurrent.py,sha256=m2zVqJUz4xFVpNbEc9YEaj3Jmbl82njupdm4_rBOoYQ,4510
34
- kabukit/utils/config.py,sha256=Jp-2TCnIj_QqA71FzYCkHXbvXvhw_1JVl4PR0foA1vM,618
37
+ kabukit/utils/concurrent.py,sha256=aTiCS6nqQtWBy29xykLHSU1LTr6LuGraxEnZco99EIM,4692
38
+ kabukit/utils/config.py,sha256=fqUdlhVjgiWEcsLFmPckp-dXUleVz8Ypdy_NnrMIBfY,708
35
39
  kabukit/utils/date.py,sha256=DEC6Ac5LS8eiW6JtrmcD3U1pX4qzXtx4ale0swpO4Ag,937
36
40
  kabukit/utils/params.py,sha256=qcaJbf6CWPUoZAZsYDTaZSnBUWeAersbWnR_iiYW9GM,1108
37
- kabukit-0.6.0.dist-info/WHEEL,sha256=n2u5OFBbdZvCiUKAmfnY1Po2j3FB_NWfuUlt5WiAjrk,79
38
- kabukit-0.6.0.dist-info/entry_points.txt,sha256=vvX771TemoM-35vVizW3JJ70HvRXnd2tX4P1Btzyoxs,46
39
- kabukit-0.6.0.dist-info/METADATA,sha256=fLwKGZDziB2cNx9-YnP5EVeI9W3oqoZuUPDMVIkF-zg,3207
40
- kabukit-0.6.0.dist-info/RECORD,,
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,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.8.23
2
+ Generator: uv 0.9.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any