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.
- kabukit/__init__.py +6 -4
- kabukit/analysis/__init__.py +0 -0
- kabukit/analysis/indicators.py +0 -0
- kabukit/analysis/preprocess.py +0 -0
- kabukit/analysis/screener.py +0 -0
- kabukit/analysis/visualization.py +57 -0
- kabukit/cli/__init__.py +0 -0
- kabukit/cli/app.py +22 -0
- kabukit/cli/auth.py +86 -0
- kabukit/core/__init__.py +0 -0
- kabukit/core/base.py +45 -0
- kabukit/core/client.py +25 -0
- kabukit/core/info.py +12 -0
- kabukit/core/prices.py +30 -0
- kabukit/core/statements.py +7 -0
- kabukit/edinet/__init__.py +3 -0
- kabukit/edinet/client.py +113 -0
- kabukit/edinet/concurrent.py +153 -0
- kabukit/edinet/doc.py +32 -0
- kabukit/jquants/__init__.py +3 -0
- kabukit/jquants/client.py +197 -197
- kabukit/jquants/concurrent.py +91 -0
- kabukit/jquants/info.py +31 -0
- kabukit/jquants/prices.py +29 -0
- kabukit/jquants/schema.py +180 -0
- kabukit/jquants/statements.py +102 -0
- kabukit/py.typed +0 -0
- kabukit/utils/__init__.py +0 -0
- kabukit/utils/concurrent.py +148 -0
- kabukit/utils/config.py +26 -0
- kabukit/utils/params.py +47 -0
- kabukit-0.2.0.dist-info/METADATA +64 -0
- kabukit-0.2.0.dist-info/RECORD +35 -0
- {kabukit-0.1.0.dist-info → kabukit-0.2.0.dist-info}/WHEEL +1 -1
- kabukit-0.2.0.dist-info/entry_points.txt +3 -0
- kabukit/cli.py +0 -40
- kabukit-0.1.0.dist-info/METADATA +0 -33
- kabukit-0.1.0.dist-info/RECORD +0 -8
- kabukit-0.1.0.dist-info/entry_points.txt +0 -3
@@ -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()
|
kabukit/utils/config.py
ADDED
@@ -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)
|
kabukit/utils/params.py
ADDED
@@ -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,,
|