kabukit 0.1.0__py3-none-any.whl → 0.2.0__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,29 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import polars as pl
6
+
7
+ if TYPE_CHECKING:
8
+ from polars import DataFrame
9
+
10
+
11
+ def clean(df: DataFrame) -> DataFrame:
12
+ return df.select(
13
+ pl.col("Date").str.to_date("%Y-%m-%d"),
14
+ "Code",
15
+ Open=pl.col("AdjustmentOpen"),
16
+ High=pl.col("AdjustmentHigh"),
17
+ Low=pl.col("AdjustmentLow"),
18
+ Close=pl.col("AdjustmentClose"),
19
+ UpperLimit=pl.col("UpperLimit").cast(pl.Int8).cast(pl.Boolean),
20
+ LowerLimit=pl.col("LowerLimit").cast(pl.Int8).cast(pl.Boolean),
21
+ Volume=pl.col("AdjustmentVolume"),
22
+ TurnoverValue=pl.col("TurnoverValue"),
23
+ AdjustmentFactor=pl.col("AdjustmentFactor"),
24
+ RawOpen=pl.col("Open"),
25
+ RawHigh=pl.col("High"),
26
+ RawLow=pl.col("Low"),
27
+ RawClose=pl.col("Close"),
28
+ RawVolume=pl.col("Volume"),
29
+ )
@@ -0,0 +1,180 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from polars import DataFrame
8
+
9
+
10
+ class BaseColumns(Enum):
11
+ @classmethod
12
+ def rename(cls, df: DataFrame, *, strict: bool = False) -> DataFrame:
13
+ """DataFrameの列名を日本語から英語に変換する。"""
14
+ return df.rename({x.name: x.value for x in cls}, strict=strict)
15
+
16
+
17
+ class InfoColumns(BaseColumns):
18
+ Date = "日付"
19
+ Code = "銘柄コード"
20
+ CompanyName = "会社名"
21
+ Sector17CodeName = "17業種コード名"
22
+ Sector33CodeName = "33業種コード名"
23
+ ScaleCategory = "規模コード"
24
+ MarketCodeName = "市場区分名"
25
+ MarginCodeName = "貸借信用区分名"
26
+
27
+
28
+ class PriceColumns(BaseColumns):
29
+ Date = "日付"
30
+ Code = "銘柄コード"
31
+ Open = "始値"
32
+ High = "高値"
33
+ Low = "安値"
34
+ Close = "終値"
35
+ UpperLimit = "ストップ高?"
36
+ LowerLimit = "ストップ安?"
37
+ Volume = "出来高"
38
+ TurnoverValue = "売買代金"
39
+ AdjustmentFactor = "調整係数"
40
+ RawOpen = "調整前始値"
41
+ RawHigh = "調整前高値"
42
+ RawLow = "調整前安値"
43
+ RawClose = "調整前終値"
44
+ RawVolume = "調整前出来高"
45
+
46
+
47
+ class StatementColumns(BaseColumns):
48
+ Date = "開示日"
49
+ Time = "開示時刻"
50
+ Code = "銘柄コード"
51
+ DisclosureNumber = "開示番号"
52
+ TypeOfDocument = "開示書類種別"
53
+ TypeOfCurrentPeriod = "当会計期間の種類"
54
+ CurrentPeriodStartDate = "当会計期間開始日"
55
+ CurrentPeriodEndDate = "当会計期間終了日"
56
+ CurrentFiscalYearStartDate = "当事業年度開始日"
57
+ CurrentFiscalYearEndDate = "当事業年度終了日"
58
+ NextFiscalYearStartDate = "翌事業年度開始日"
59
+ NextFiscalYearEndDate = "翌事業年度終了日"
60
+
61
+ NetSales = "売上高"
62
+ OperatingProfit = "営業利益"
63
+ OrdinaryProfit = "経常利益"
64
+ Profit = "当期純利益"
65
+ EarningsPerShare = "一株あたり当期純利益"
66
+ DilutedEarningsPerShare = "潜在株式調整後一株あたり当期純利益"
67
+
68
+ TotalAssets = "総資産"
69
+ Equity = "純資産"
70
+ EquityToAssetRatio = "自己資本比率"
71
+ BookValuePerShare = "一株あたり純資産"
72
+
73
+ CashFlowsFromOperatingActivities = "営業活動によるキャッシュフロー"
74
+ CashFlowsFromInvestingActivities = "投資活動によるキャッシュフロー"
75
+ CashFlowsFromFinancingActivities = "財務活動によるキャッシュフロー"
76
+ CashAndEquivalents = "現金及び現金同等物期末残高"
77
+
78
+ ResultDividendPerShare1stQuarter = "一株あたり配当実績_第1四半期末"
79
+ ResultDividendPerShare2ndQuarter = "一株あたり配当実績_第2四半期末"
80
+ ResultDividendPerShare3rdQuarter = "一株あたり配当実績_第3四半期末"
81
+ ResultDividendPerShareFiscalYearEnd = "一株あたり配当実績_期末"
82
+ ResultDividendPerShareAnnual = "一株あたり配当実績_合計"
83
+ ResultTotalDividendPaidAnnual = "配当金総額"
84
+ ResultPayoutRatioAnnual = "配当性向"
85
+
86
+ ForecastDividendPerShare1stQuarter = "一株あたり配当予想_第1四半期末"
87
+ ForecastDividendPerShare2ndQuarter = "一株あたり配当予想_第2四半期末"
88
+ ForecastDividendPerShare3rdQuarter = "一株あたり配当予想_第3四半期末"
89
+ ForecastDividendPerShareFiscalYearEnd = "一株あたり配当予想_期末"
90
+ ForecastDividendPerShareAnnual = "一株あたり配当予想_合計"
91
+ ForecastTotalDividendPaidAnnual = "予想配当金総額"
92
+ ForecastPayoutRatioAnnual = "予想配当性向"
93
+
94
+ NextYearForecastDividendPerShare1stQuarter = "一株あたり配当予想_翌事業年度第1四半期末"
95
+ NextYearForecastDividendPerShare2ndQuarter = "一株あたり配当予想_翌事業年度第2四半期末"
96
+ NextYearForecastDividendPerShare3rdQuarter = "一株あたり配当予想_翌事業年度第3四半期末"
97
+ NextYearForecastDividendPerShareFiscalYearEnd = "一株あたり配当予想_翌事業年度期末"
98
+ NextYearForecastDividendPerShareAnnual = "一株あたり配当予想_翌事業年度合計"
99
+ NextYearForecastPayoutRatioAnnual = "翌事業年度予想配当性向"
100
+
101
+ ForecastNetSales2ndQuarter = "売上高_予想_第2四半期末"
102
+ ForecastOperatingProfit2ndQuarter = "営業利益_予想_第2四半期末"
103
+ ForecastOrdinaryProfit2ndQuarter = "経常利益_予想_第2四半期末"
104
+ ForecastProfit2ndQuarter = "当期純利益_予想_第2四半期末"
105
+ ForecastEarningsPerShare2ndQuarter = "一株あたり当期純利益_予想_第2四半期末"
106
+
107
+ NextYearForecastNetSales2ndQuarter = "売上高_予想_翌事業年度第2四半期末"
108
+ NextYearForecastOperatingProfit2ndQuarter = "営業利益_予想_翌事業年度第2四半期末"
109
+ NextYearForecastOrdinaryProfit2ndQuarter = "経常利益_予想_翌事業年度第2四半期末"
110
+ NextYearForecastProfit2ndQuarter = "当期純利益_予想_翌事業年度第2四半期末"
111
+ NextYearForecastEarningsPerShare2ndQuarter = "一株あたり当期純利益_予想_翌事業年度第2四半期末"
112
+
113
+ ForecastNetSales = "売上高_予想_期末"
114
+ ForecastOperatingProfit = "営業利益_予想_期末"
115
+ ForecastOrdinaryProfit = "経常利益_予想_期末"
116
+ ForecastProfit = "当期純利益_予想_期末"
117
+ ForecastEarningsPerShare = "一株あたり当期純利益_予想_期末"
118
+
119
+ NextYearForecastNetSales = "売上高_予想_翌事業年度期末"
120
+ NextYearForecastOperatingProfit = "営業利益_予想_翌事業年度期末"
121
+ NextYearForecastOrdinaryProfit = "経常利益_予想_翌事業年度期末"
122
+ NextYearForecastProfit = "当期純利益_予想_翌事業年度期末"
123
+ NextYearForecastEarningsPerShare = "一株あたり当期純利益_予想_翌事業年度期末"
124
+
125
+ MaterialChangesInSubsidiaries = "期中における重要な子会社の異動"
126
+ SignificantChangesInTheScopeOfConsolidation = "期中における連結範囲の重要な変更"
127
+ ChangesBasedOnRevisionsOfAccountingStandard = "会計基準等の改正に伴う会計方針の変更"
128
+ ChangesOtherThanOnesBasedOnRevisionsOfAccountingStandard = "会計基準等の改正に伴う変更以外の会計方針の変更"
129
+ ChangesInAccountingEstimates = "会計上の見積りの変更"
130
+ RetrospectiveRestatement = "修正再表示"
131
+
132
+ # NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock
133
+ NumberOfShares = "期末発行済株式数"
134
+ # NumberOfTreasuryStockAtTheEndOfFiscalYear
135
+ NumberOfTreasuryStock = "期末自己株式数"
136
+ AverageNumberOfShares = "期中平均株式数"
137
+
138
+ """
139
+ NonConsolidatedNetSales = "売上高_非連結"
140
+ NonConsolidatedOperatingProfit = "営業利益_非連結"
141
+ NonConsolidatedOrdinaryProfit = "経常利益_非連結"
142
+ NonConsolidatedProfit = "当期純利益_非連結"
143
+ NonConsolidatedEarningsPerShare = "一株あたり当期純利益_非連結"
144
+
145
+ NonConsolidatedTotalAssets = "総資産_非連結"
146
+ NonConsolidatedEquity = "純資産_非連結"
147
+ NonConsolidatedEquityToAssetRatio = "自己資本比率_非連結"
148
+ NonConsolidatedBookValuePerShare = "一株あたり純資産_非連結"
149
+
150
+ ForecastNonConsolidatedNetSales2ndQuarter = "売上高_予想_第2四半期末_非連結"
151
+ ForecastNonConsolidatedOperatingProfit2ndQuarter = "営業利益_予想_第2四半期末_非連結"
152
+ ForecastNonConsolidatedOrdinaryProfit2ndQuarter = "経常利益_予想_第2四半期末_非連結"
153
+ ForecastNonConsolidatedProfit2ndQuarter = "当期純利益_予想_第2四半期末_非連結"
154
+ ForecastNonConsolidatedEarningsPerShare2ndQuarter = "一株あたり当期純利益_予想_第2四半期末_非連結"
155
+
156
+ NextYearForecastNonConsolidatedNetSales2ndQuarter = "売上高_予想_翌事業年度第2四半期末_非連結"
157
+ NextYearForecastNonConsolidatedOperatingProfit2ndQuarter = "営業利益_予想_翌事業年度第2四半期末_非連結"
158
+ NextYearForecastNonConsolidatedOrdinaryProfit2ndQuarter = "経常利益_予想_翌事業年度第2四半期末_非連結"
159
+ NextYearForecastNonConsolidatedProfit2ndQuarter = "当期純利益_予想_翌事業年度第2四半期末_非連結"
160
+ NextYearForecastNonConsolidatedEarningsPerShare2ndQuarter = "一株あたり当期純利益_予想_翌事業年度第2四半期末_非連結"
161
+
162
+ ForecastNonConsolidatedNetSales = "売上高_予想_期末_非連結"
163
+ ForecastNonConsolidatedOperatingProfit = "営業利益_予想_期末_非連結"
164
+ ForecastNonConsolidatedOrdinaryProfit = "経常利益_予想_期末_非連結"
165
+ ForecastNonConsolidatedProfit = "当期純利益_予想_期末_非連結"
166
+ ForecastNonConsolidatedEarningsPerShare = "一株あたり当期純利益_予想_期末_非連結"
167
+
168
+ NextYearForecastNonConsolidatedNetSales = "売上高_予想_翌事業年度期末_非連結"
169
+ NextYearForecastNonConsolidatedOperatingProfit = "営業利益_予想_翌事業年度期末_非連結"
170
+ NextYearForecastNonConsolidatedOrdinaryProfit = "経常利益_予想_翌事業年度期末_非連結"
171
+ NextYearForecastNonConsolidatedProfit = "当期純利益_予想_翌事業年度期末_非連結"
172
+ NextYearForecastNonConsolidatedEarningsPerShare = "一株あたり当期純利益_予想_翌事業年度期末_非連結"
173
+ """
174
+
175
+
176
+ def rename(df: DataFrame, *, strict: bool = False) -> DataFrame:
177
+ """DataFrameの列名を日本語から英語に変換する。"""
178
+ for enum in (InfoColumns, PriceColumns, StatementColumns):
179
+ df = enum.rename(df, strict=strict)
180
+ return df
@@ -0,0 +1,102 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+ from functools import cache
5
+ from typing import TYPE_CHECKING, Any, Protocol
6
+
7
+ import holidays
8
+ import polars as pl
9
+
10
+ if TYPE_CHECKING:
11
+ from collections.abc import AsyncIterable, AsyncIterator
12
+
13
+ from polars import DataFrame
14
+
15
+ class Progress(Protocol):
16
+ def __call__[T](
17
+ self,
18
+ async_iterable: AsyncIterable[T],
19
+ total: int | None = None,
20
+ *args: Any,
21
+ **kwargs: Any,
22
+ ) -> AsyncIterator[T]: ...
23
+
24
+
25
+ def clean(df: DataFrame) -> DataFrame:
26
+ return (
27
+ df.select(pl.exclude(r"^.*\(REIT\)|.*NonConsolidated.*$"))
28
+ .rename(
29
+ {
30
+ "DisclosedDate": "Date",
31
+ "DisclosedTime": "Time",
32
+ "LocalCode": "Code",
33
+ "NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock": "NumberOfShares", # noqa: E501
34
+ "NumberOfTreasuryStockAtTheEndOfFiscalYear": "NumberOfTreasuryStock",
35
+ },
36
+ )
37
+ .with_columns(
38
+ pl.col("^.*Date$").str.to_date("%Y-%m-%d", strict=False),
39
+ pl.col("Time").str.to_time("%H:%M:%S", strict=False),
40
+ pl.col("TypeOfCurrentPeriod").cast(pl.Categorical),
41
+ )
42
+ .pipe(_cast_float)
43
+ .pipe(_cast_bool)
44
+ )
45
+
46
+
47
+ def _cast_float(df: DataFrame) -> DataFrame:
48
+ return df.with_columns(
49
+ pl.col(f"^.*{name}.*$").cast(pl.Float64, strict=False)
50
+ for name in [
51
+ "Assets",
52
+ "BookValue",
53
+ "Cash",
54
+ "Distributions",
55
+ "Dividend",
56
+ "Earnings",
57
+ "Equity",
58
+ "NetSales",
59
+ "NumberOf",
60
+ "PayoutRatio",
61
+ "Profit",
62
+ ]
63
+ )
64
+
65
+
66
+ def _cast_bool(df: DataFrame) -> DataFrame:
67
+ columns = df.select(pl.col("^.*Changes.*$")).columns
68
+ columns.append("RetrospectiveRestatement")
69
+
70
+ return df.with_columns(
71
+ pl.when(pl.col(col) == "true")
72
+ .then(True) # noqa: FBT003
73
+ .when(pl.col(col) == "false")
74
+ .then(False) # noqa: FBT003
75
+ .otherwise(None)
76
+ .alias(col)
77
+ for col in columns
78
+ )
79
+
80
+
81
+ @cache
82
+ def get_holidays(year: int | None = None, n: int = 10) -> list[datetime.date]:
83
+ """指定した過去年数の日本の祝日を取得する。"""
84
+ if year is None:
85
+ year = datetime.datetime.now().year # noqa: DTZ005
86
+
87
+ dates = holidays.country_holidays("JP", years=range(year - n, year + 1))
88
+ return sorted(dates.keys())
89
+
90
+
91
+ def update_effective_date(df: DataFrame, year: int | None = None) -> DataFrame:
92
+ """開示日が休日や15時以降の場合、翌営業日に更新する。"""
93
+ holidays = get_holidays(year=year)
94
+
95
+ cond = pl.col("Time").is_null() | (pl.col("Time") > datetime.time(15, 0))
96
+
97
+ return df.with_columns(
98
+ pl.when(cond)
99
+ .then(pl.col("Date").dt.add_business_days(1, holidays=holidays))
100
+ .otherwise(pl.col("Date"))
101
+ .alias("Date"),
102
+ )
kabukit/py.typed ADDED
File without changes
File without changes
@@ -0,0 +1,148 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Any, Protocol
6
+
7
+ import polars as pl
8
+
9
+ if TYPE_CHECKING:
10
+ from collections.abc import (
11
+ AsyncIterable,
12
+ AsyncIterator,
13
+ Awaitable,
14
+ Callable,
15
+ Iterable,
16
+ )
17
+ from typing import Any
18
+
19
+ from marimo._plugins.stateless.status import progress_bar
20
+ from polars import DataFrame
21
+ from tqdm.asyncio import tqdm
22
+
23
+ from kabukit.core.client import Client
24
+
25
+ class _Progress(Protocol):
26
+ def __call__[T](
27
+ self,
28
+ aiterable: AsyncIterable[T],
29
+ total: int | None = None,
30
+ *args: Any,
31
+ **kwargs: Any,
32
+ ) -> AsyncIterator[T]: ...
33
+
34
+
35
+ MAX_CONCURRENCY = 12
36
+
37
+
38
+ async def collect[R](
39
+ awaitables: Iterable[Awaitable[R]],
40
+ /,
41
+ max_concurrency: int | None = None,
42
+ ) -> AsyncIterator[R]:
43
+ max_concurrency = max_concurrency or MAX_CONCURRENCY
44
+ semaphore = asyncio.Semaphore(max_concurrency)
45
+
46
+ async def run(awaitable: Awaitable[R]) -> R:
47
+ async with semaphore:
48
+ return await awaitable
49
+
50
+ futures = (run(awaitable) for awaitable in awaitables)
51
+
52
+ async for future in asyncio.as_completed(futures):
53
+ yield await future
54
+
55
+
56
+ async def collect_fn[T, R](
57
+ function: Callable[[T], Awaitable[R]],
58
+ args: Iterable[T],
59
+ /,
60
+ max_concurrency: int | None = None,
61
+ ) -> AsyncIterator[R]:
62
+ max_concurrency = max_concurrency or MAX_CONCURRENCY
63
+ awaitables = (function(arg) for arg in args)
64
+
65
+ async for item in collect(awaitables, max_concurrency=max_concurrency):
66
+ yield item
67
+
68
+
69
+ async def concat(
70
+ awaitables: Iterable[Awaitable[DataFrame]],
71
+ /,
72
+ max_concurrency: int | None = None,
73
+ ) -> DataFrame:
74
+ dfs = collect(awaitables, max_concurrency=max_concurrency)
75
+ dfs = [df async for df in dfs]
76
+ return pl.concat(df for df in dfs if not df.is_empty())
77
+
78
+
79
+ async def concat_fn[T](
80
+ function: Callable[[T], Awaitable[DataFrame]],
81
+ args: Iterable[T],
82
+ /,
83
+ max_concurrency: int | None = None,
84
+ ) -> DataFrame:
85
+ dfs = collect_fn(function, args, max_concurrency=max_concurrency)
86
+ dfs = [df async for df in dfs]
87
+ return pl.concat(df for df in dfs if not df.is_empty())
88
+
89
+
90
+ type Callback = Callable[[DataFrame], DataFrame | None]
91
+ type Progress = type[progress_bar[Any] | tqdm[Any]] | _Progress
92
+
93
+
94
+ @dataclass
95
+ class Stream:
96
+ cls: type[Client]
97
+ resource: str
98
+ args: list[Any]
99
+ max_concurrency: int | None = None
100
+
101
+ async def __aiter__(self) -> AsyncIterator[DataFrame]:
102
+ async with self.cls() as client:
103
+ fn = getattr(client, f"get_{self.resource}")
104
+
105
+ async for df in collect_fn(fn, self.args, self.max_concurrency):
106
+ yield df
107
+
108
+
109
+ async def fetch(
110
+ cls: type[Client],
111
+ resource: str,
112
+ args: Iterable[Any],
113
+ /,
114
+ max_concurrency: int | None = None,
115
+ progress: Progress | None = None,
116
+ callback: Callback | None = None,
117
+ ) -> DataFrame:
118
+ """各種データを取得し、単一のDataFrameにまとめて返す。
119
+
120
+ Args:
121
+ cls (type[Client]): 使用するClientクラス。
122
+ JQuantsClientやEdinetClientなど、Clientを継承したクラス
123
+ resource (str): 取得するデータの種類。Clientのメソッド名から"get_"を
124
+ 除いたものを指定する。
125
+ args (Iterable[Any]): 取得対象の引数のリスト。
126
+ max_concurrency (int | None, optional): 同時に実行するリクエストの最大数。
127
+ 指定しないときはデフォルト値が使用される。
128
+ progress (Progress | None, optional): 進捗表示のための関数。
129
+ tqdm, marimoなどのライブラリを使用できる。
130
+ 指定しないときは進捗表示は行われない。
131
+ callback (Callback | None, optional): 各DataFrameに対して適用する
132
+ コールバック関数。指定しないときはそのままのDataFrameが使用される。
133
+
134
+ Returns:
135
+ DataFrame:
136
+ すべての情報を含む単一のDataFrame。
137
+ """
138
+ args = list(args)
139
+ stream = Stream(cls, resource, args, max_concurrency)
140
+
141
+ if progress:
142
+ stream = progress(aiter(stream), total=len(args))
143
+
144
+ if callback:
145
+ stream = (x if (r := callback(x)) is None else r async for x in stream)
146
+
147
+ dfs = [df async for df in stream if not df.is_empty()]
148
+ return pl.concat(dfs) if dfs else pl.DataFrame()
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import cache
4
+ from pathlib import Path
5
+
6
+ import dotenv
7
+ from platformdirs import user_config_dir
8
+
9
+
10
+ @cache
11
+ def get_dotenv_path() -> Path:
12
+ """Return the path to the .env file in the user config directory."""
13
+ config_dir = Path(user_config_dir("kabukit"))
14
+ config_dir.mkdir(parents=True, exist_ok=True)
15
+ return config_dir / ".env"
16
+
17
+
18
+ @cache
19
+ def load_dotenv() -> bool:
20
+ dotenv_path = get_dotenv_path()
21
+ return dotenv.load_dotenv(dotenv_path)
22
+
23
+
24
+ def set_key(key: str, value: str) -> tuple[bool | None, str, str]:
25
+ dotenv_path = get_dotenv_path()
26
+ return dotenv.set_key(dotenv_path, key, value)
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ import datetime
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ if TYPE_CHECKING:
7
+ from collections.abc import Iterator
8
+
9
+
10
+ def iter_items(kwargs: dict[str, Any]) -> Iterator[tuple[str, Any]]:
11
+ for key, value in kwargs.items():
12
+ if value is None:
13
+ continue
14
+
15
+ if key == "from_":
16
+ yield "from", value
17
+ else:
18
+ yield key, value
19
+
20
+
21
+ def get_params(**kwargs: Any) -> dict[str, str]:
22
+ params: dict[str, str] = {}
23
+
24
+ for key, value in iter_items(kwargs):
25
+ if isinstance(value, datetime.date):
26
+ params[key] = date_to_str(value)
27
+ elif not isinstance(value, str):
28
+ params[key] = str(value)
29
+ else:
30
+ params[key] = value
31
+
32
+ return params
33
+
34
+
35
+ def date_to_str(date: str | datetime.date) -> str:
36
+ """Convert a date object or string to a YYYY-MM-DD string.
37
+
38
+ Args:
39
+ date: The date to convert.
40
+
41
+ Returns:
42
+ The date as a YYYY-MM-DD string.
43
+ """
44
+ if isinstance(date, datetime.date):
45
+ return date.strftime("%Y-%m-%d")
46
+
47
+ return date
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.3
2
+ Name: kabukit
3
+ Version: 0.2.0
4
+ Summary: A Python toolkit for Japanese financial market data, supporting J-Quants and EDINET APIs.
5
+ Author: daizutabi
6
+ Author-email: daizutabi <daizutabi@gmail.com>
7
+ License: MIT License
8
+
9
+ Copyright (c) 2025 Daizu
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in all
19
+ copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ SOFTWARE.
28
+ Classifier: Development Status :: 4 - Beta
29
+ Classifier: Programming Language :: Python
30
+ Classifier: Programming Language :: Python :: 3.12
31
+ Classifier: Programming Language :: Python :: 3.13
32
+ Requires-Dist: altair>=5
33
+ Requires-Dist: async-typer>=0.1
34
+ Requires-Dist: holidays>=0.81
35
+ Requires-Dist: httpx>=0.28.1
36
+ Requires-Dist: marimo[lsp]>=0.16
37
+ Requires-Dist: platformdirs>=4
38
+ Requires-Dist: polars>=1
39
+ Requires-Dist: python-dotenv>=1
40
+ Requires-Dist: typer>=0.19
41
+ Requires-Dist: vegafusion-python-embed>=1.6
42
+ Requires-Dist: vegafusion>=2
43
+ Requires-Dist: vl-convert-python>=1.8
44
+ Requires-Python: >=3.12
45
+ Description-Content-Type: text/markdown
46
+
47
+ # kabukit
48
+
49
+ A Python toolkit for Japanese financial market data, supporting J-Quants and EDINET APIs.
50
+
51
+ [![PyPI Version][pypi-v-image]][pypi-v-link]
52
+ [![Python Version][python-v-image]][python-v-link]
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install kabukit
58
+ ```
59
+
60
+ <!-- Badges -->
61
+ [pypi-v-image]: https://img.shields.io/pypi/v/kabukit.svg
62
+ [pypi-v-link]: https://pypi.org/project/kabukit/
63
+ [python-v-image]: https://img.shields.io/pypi/pyversions/kabukit.svg
64
+ [python-v-link]: https://pypi.org/project/kabukit
@@ -0,0 +1,35 @@
1
+ kabukit/__init__.py,sha256=ooNlFhIEMttAeNYZIZTBmOYwj3MQEG11dq0sXhkFf74,259
2
+ kabukit/analysis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ kabukit/analysis/indicators.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ kabukit/analysis/preprocess.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ kabukit/analysis/screener.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ kabukit/analysis/visualization.py,sha256=VS9WXBKcdOYsnVe-OR3cWP_nru3Oa-rBOK5SUwsxfyQ,1857
7
+ kabukit/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ kabukit/cli/app.py,sha256=LjAq_Pj5_bokL4bCZ2R8OoP_zEBA-a2EG0Pm1ZCn7o8,491
9
+ kabukit/cli/auth.py,sha256=MWLxEiINrqMGJSiVHcv09EuPlyGeCitHqJq7kumzbr8,2735
10
+ kabukit/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ kabukit/core/base.py,sha256=kqaqtYHHFK8xC3z8QkLFv8CAV6FEZDDoiVY2Qwe9iM8,1125
12
+ kabukit/core/client.py,sha256=lvjrshzOjyea2jRmc11Dl5OAJUGGN3PfKXWxoveG6hY,664
13
+ kabukit/core/info.py,sha256=5BX7mDavF6g-b0KzHgKIFHUS5701BoaHtw1JcHSsy94,174
14
+ kabukit/core/prices.py,sha256=Axm9cceVxnHIWCW6IHqEuCC4M73JtWCRzjXA42Qd7JE,781
15
+ kabukit/core/statements.py,sha256=18vD5WRjBGVSXvmCfTvGAwgjnajLbN-tH6V91sX44iI,94
16
+ kabukit/edinet/__init__.py,sha256=9bAWDIgVZKw84GF4z6FilUP_B22ih-ixLm8cQHxaoS8,77
17
+ kabukit/edinet/client.py,sha256=OSz8BmSBzzZMD-wk_9WbhZLfDY901s5CN1UissoDtyc,3410
18
+ kabukit/edinet/concurrent.py,sha256=du4TG0WwfkiWvxznXh-P0u1E_8mjTHBNivCIVmpieUg,5496
19
+ kabukit/edinet/doc.py,sha256=ieze_886Tzkp6QYRJD3GuSXj6-LS43MqmA72zEx1kjA,959
20
+ kabukit/jquants/__init__.py,sha256=n-sg0pLajSfyJ8snU6I_Q52rwmuaGCwAyz1yVbeMSw4,75
21
+ kabukit/jquants/client.py,sha256=nyBAD2hspz_bLtkTLUgFGGRz9oZXXkXONV1exjW9O1s,10704
22
+ kabukit/jquants/concurrent.py,sha256=5XObYKMMYNmrq-8JLtVg0uBGoEJwI9Luvy6ndamiTz8,3288
23
+ kabukit/jquants/info.py,sha256=AUymo6EtLdJ_L-0yNG1gd7rJ8Jkjw7tvsDM5Vw4QNFA,843
24
+ kabukit/jquants/prices.py,sha256=oApQpdgzHwPw11XHpdg8ccZS7kybGtV8APZlpD2L3Yw,882
25
+ kabukit/jquants/schema.py,sha256=HIoKDoIWrT0XNDHz5Fm4DtDGLLRqrq6TIkMTvq_Jheg,9647
26
+ kabukit/jquants/statements.py,sha256=rPfIVJjWeEviWEtjPRUGesHyIpQKMfBxDLTXJzkYBdA,2995
27
+ kabukit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ kabukit/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ kabukit/utils/concurrent.py,sha256=7eXk29IhDLGroCCMIb8ucVqLMV9tYh0o2jYCoAU5tVM,4494
30
+ kabukit/utils/config.py,sha256=Bsa_BGxNsHyaLWkC99oliRRPmkv5K0YKSdYF-ADGjck,660
31
+ kabukit/utils/params.py,sha256=a5QLxTyB6jegeXDerQNmpYEKdZplhL3GnJuK0WRbBtQ,1091
32
+ kabukit-0.2.0.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
33
+ kabukit-0.2.0.dist-info/entry_points.txt,sha256=vvX771TemoM-35vVizW3JJ70HvRXnd2tX4P1Btzyoxs,46
34
+ kabukit-0.2.0.dist-info/METADATA,sha256=SuzdQfFqziFhEsJq_8v4UQ91XIkQhgp3qKRiayZh3uk,2584
35
+ kabukit-0.2.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.8.14
2
+ Generator: uv 0.8.22
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ kabu = kabukit.cli.app:app
3
+