kabukit 0.3.0__py3-none-any.whl → 0.4.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 +11 -1
- kabukit/cli/auth.py +2 -2
- kabukit/cli/get.py +56 -6
- kabukit/core/base.py +12 -1
- kabukit/core/client.py +1 -1
- kabukit/core/list.py +12 -0
- kabukit/core/prices.py +62 -0
- kabukit/core/reports.py +12 -0
- kabukit/core/statements.py +18 -1
- kabukit/edinet/__init__.py +2 -2
- kabukit/edinet/client.py +3 -7
- kabukit/edinet/concurrent.py +4 -2
- kabukit/edinet/doc.py +24 -10
- kabukit/jquants/__init__.py +2 -1
- kabukit/jquants/client.py +7 -2
- kabukit/jquants/concurrent.py +1 -1
- kabukit/jquants/schema.py +6 -9
- kabukit/jquants/statements.py +28 -15
- {kabukit-0.3.0.dist-info → kabukit-0.4.0.dist-info}/METADATA +2 -3
- kabukit-0.4.0.dist-info/RECORD +39 -0
- kabukit-0.3.0.dist-info/RECORD +0 -37
- {kabukit-0.3.0.dist-info → kabukit-0.4.0.dist-info}/WHEEL +0 -0
- {kabukit-0.3.0.dist-info → kabukit-0.4.0.dist-info}/entry_points.txt +0 -0
kabukit/__init__.py
CHANGED
@@ -1,7 +1,17 @@
|
|
1
1
|
from .core.info import Info
|
2
|
+
from .core.list import List
|
2
3
|
from .core.prices import Prices
|
4
|
+
from .core.reports import Reports
|
3
5
|
from .core.statements import Statements
|
4
6
|
from .edinet.client import EdinetClient
|
5
7
|
from .jquants.client import JQuantsClient
|
6
8
|
|
7
|
-
__all__ = [
|
9
|
+
__all__ = [
|
10
|
+
"EdinetClient",
|
11
|
+
"Info",
|
12
|
+
"JQuantsClient",
|
13
|
+
"List",
|
14
|
+
"Prices",
|
15
|
+
"Reports",
|
16
|
+
"Statements",
|
17
|
+
]
|
kabukit/cli/auth.py
CHANGED
@@ -20,8 +20,8 @@ async def auth_jquants(mailaddress: str, password: str) -> None:
|
|
20
20
|
async with JQuantsClient() as client:
|
21
21
|
try:
|
22
22
|
await client.auth(mailaddress, password, save=True)
|
23
|
-
except HTTPStatusError
|
24
|
-
typer.echo(
|
23
|
+
except HTTPStatusError:
|
24
|
+
typer.echo("認証に失敗しました。")
|
25
25
|
raise Exit(1) from None
|
26
26
|
|
27
27
|
typer.echo("J-Quantsのリフレッシュトークン・IDトークンを保存しました。")
|
kabukit/cli/get.py
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from typing import Annotated, Any
|
3
|
+
from typing import TYPE_CHECKING, Annotated, Any
|
4
4
|
|
5
5
|
import typer
|
6
6
|
from async_typer import AsyncTyper # pyright: ignore[reportMissingTypeStubs]
|
7
7
|
from typer import Argument
|
8
8
|
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from kabukit.core.base import Base
|
11
|
+
|
9
12
|
app = AsyncTyper(
|
10
13
|
add_completion=False,
|
11
14
|
help="J-Quantsからデータを取得します。",
|
@@ -36,7 +39,7 @@ async def info(code: Code = None) -> None:
|
|
36
39
|
async def _fetch(
|
37
40
|
code: str | None,
|
38
41
|
target: str,
|
39
|
-
|
42
|
+
cls: type[Base],
|
40
43
|
fetch_func_name: str,
|
41
44
|
message: str,
|
42
45
|
**kwargs: Any,
|
@@ -56,7 +59,7 @@ async def _fetch(
|
|
56
59
|
|
57
60
|
df = await fetch_all(target, progress=tqdm.asyncio.tqdm, **kwargs)
|
58
61
|
typer.echo(df)
|
59
|
-
path =
|
62
|
+
path = cls(df).write()
|
60
63
|
typer.echo(f"全銘柄の{message}を '{path}' に保存しました。")
|
61
64
|
|
62
65
|
|
@@ -68,7 +71,7 @@ async def statements(code: Code = None) -> None:
|
|
68
71
|
await _fetch(
|
69
72
|
code=code,
|
70
73
|
target="statements",
|
71
|
-
|
74
|
+
cls=Statements,
|
72
75
|
fetch_func_name="get_statements",
|
73
76
|
message="財務情報",
|
74
77
|
)
|
@@ -82,16 +85,55 @@ async def prices(code: Code = None) -> None:
|
|
82
85
|
await _fetch(
|
83
86
|
code=code,
|
84
87
|
target="prices",
|
85
|
-
|
88
|
+
cls=Prices,
|
86
89
|
fetch_func_name="get_prices",
|
87
90
|
message="株価情報",
|
88
91
|
max_concurrency=8,
|
89
92
|
)
|
90
93
|
|
91
94
|
|
95
|
+
@app.async_command(name="list") # pyright: ignore[reportUnknownMemberType]
|
96
|
+
async def list_() -> None:
|
97
|
+
"""報告書一覧を取得します。"""
|
98
|
+
import tqdm.asyncio
|
99
|
+
|
100
|
+
from kabukit.core.list import List
|
101
|
+
from kabukit.edinet.concurrent import fetch_list
|
102
|
+
|
103
|
+
df = await fetch_list(years=10, progress=tqdm.asyncio.tqdm)
|
104
|
+
typer.echo(df)
|
105
|
+
path = List(df).write()
|
106
|
+
typer.echo(f"報告書一覧を '{path}' に保存しました。")
|
107
|
+
|
108
|
+
|
109
|
+
@app.async_command() # pyright: ignore[reportUnknownMemberType]
|
110
|
+
async def reports() -> None:
|
111
|
+
"""報告書を取得します。"""
|
112
|
+
import polars as pl
|
113
|
+
import tqdm.asyncio
|
114
|
+
|
115
|
+
from kabukit.core.list import List
|
116
|
+
from kabukit.core.reports import Reports
|
117
|
+
from kabukit.edinet.concurrent import fetch_csv
|
118
|
+
|
119
|
+
try:
|
120
|
+
df = List.read().data
|
121
|
+
except FileNotFoundError:
|
122
|
+
await list_()
|
123
|
+
df = List.read().data
|
124
|
+
|
125
|
+
lst = df.filter(pl.col("csvFlag"), pl.col("secCode").is_not_null())
|
126
|
+
doc_ids = lst["docID"].unique()
|
127
|
+
|
128
|
+
df = await fetch_csv(doc_ids, limit=1000, progress=tqdm.asyncio.tqdm)
|
129
|
+
typer.echo(df)
|
130
|
+
path = Reports(df).write()
|
131
|
+
typer.echo(f"報告書を '{path}' に保存しました。")
|
132
|
+
|
133
|
+
|
92
134
|
@app.async_command(name="all") # pyright: ignore[reportUnknownMemberType]
|
93
135
|
async def all_(code: Code = None) -> None:
|
94
|
-
"""
|
136
|
+
"""上場銘柄一覧、財務情報、株価、報告書を連続して取得します。"""
|
95
137
|
typer.echo("上場銘柄一覧を取得します。")
|
96
138
|
await info(code)
|
97
139
|
|
@@ -102,3 +144,11 @@ async def all_(code: Code = None) -> None:
|
|
102
144
|
typer.echo("---")
|
103
145
|
typer.echo("株価を取得します。")
|
104
146
|
await prices(code)
|
147
|
+
|
148
|
+
if code is None:
|
149
|
+
typer.echo("---")
|
150
|
+
typer.echo("報告書一覧を取得します。")
|
151
|
+
await list_()
|
152
|
+
typer.echo("---")
|
153
|
+
typer.echo("報告書を取得します。")
|
154
|
+
await reports()
|
kabukit/core/base.py
CHANGED
@@ -8,9 +8,11 @@ import polars as pl
|
|
8
8
|
from platformdirs import user_cache_dir
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
|
-
from
|
11
|
+
from collections.abc import Iterable
|
12
|
+
from typing import Any, Self
|
12
13
|
|
13
14
|
from polars import DataFrame
|
15
|
+
from polars._typing import IntoExprColumn
|
14
16
|
|
15
17
|
|
16
18
|
class Base:
|
@@ -48,3 +50,12 @@ class Base:
|
|
48
50
|
|
49
51
|
data = pl.read_parquet(filename)
|
50
52
|
return cls(data)
|
53
|
+
|
54
|
+
def filter(
|
55
|
+
self,
|
56
|
+
*predicates: IntoExprColumn | Iterable[IntoExprColumn] | bool | list[bool],
|
57
|
+
**constraints: Any,
|
58
|
+
) -> Self:
|
59
|
+
"""Filter the data with given predicates and constraints."""
|
60
|
+
data = self.data.filter(*predicates, **constraints)
|
61
|
+
return self.__class__(data)
|
kabukit/core/client.py
CHANGED
@@ -12,7 +12,7 @@ class Client:
|
|
12
12
|
client: AsyncClient
|
13
13
|
|
14
14
|
def __init__(self, base_url: str = "") -> None:
|
15
|
-
self.client = AsyncClient(base_url=base_url)
|
15
|
+
self.client = AsyncClient(base_url=base_url, timeout=20)
|
16
16
|
|
17
17
|
async def aclose(self) -> None:
|
18
18
|
"""HTTPクライアントを閉じる。"""
|
kabukit/core/list.py
ADDED
kabukit/core/prices.py
CHANGED
@@ -12,6 +12,8 @@ if TYPE_CHECKING:
|
|
12
12
|
|
13
13
|
from polars import Expr
|
14
14
|
|
15
|
+
from .statements import Statements
|
16
|
+
|
15
17
|
|
16
18
|
class Prices(Base):
|
17
19
|
def truncate(self, every: str | timedelta | Expr) -> Self:
|
@@ -28,3 +30,63 @@ class Prices(Base):
|
|
28
30
|
.sort("Code", "Date")
|
29
31
|
)
|
30
32
|
return self.__class__(data)
|
33
|
+
|
34
|
+
def with_adjusted_shares(self, statements: Statements) -> Self:
|
35
|
+
"""日次の調整済み株式数を計算し、列として追加する。
|
36
|
+
|
37
|
+
決算短信で報告される株式数(例:発行済株式総数)は、四半期ごとなど
|
38
|
+
特定の日付のデータです。一方で、株式分割や併合は日々発生し、株式数を
|
39
|
+
変動させます。
|
40
|
+
このメソッドは、直近の決算で報告された株式数を、日々の調整係数
|
41
|
+
(`AdjustmentFactor`) を用いて補正し、日次ベースの時系列データとして
|
42
|
+
提供します。これにより、日々の時価総額計算などが正確に行えるようになります。
|
43
|
+
|
44
|
+
具体的には、`statements`から`TotalShares`(発行済株式総数)と
|
45
|
+
`TreasuryShares`(自己株式数)を取得し、それぞれを調整します。
|
46
|
+
計算結果は、元の列名との混同を避けるため、接頭辞`Adjusted`を付与した
|
47
|
+
新しい列(`AdjustedTotalShares`, `AdjustedTreasuryShares`)として
|
48
|
+
追加されます。
|
49
|
+
|
50
|
+
.. note::
|
51
|
+
この計算は、決算発表間の株式数の変動が、株式分割・併合
|
52
|
+
(`AdjustmentFactor`)にのみ起因すると仮定しています。
|
53
|
+
期中に行われる増資や自己株式取得など、`AdjustmentFactor`に
|
54
|
+
反映されないイベントによる株式数の変動は考慮されません。
|
55
|
+
|
56
|
+
Args:
|
57
|
+
statements (Statements): `number_of_shares()`メソッドを通じて
|
58
|
+
株式数データを提供できる`Statements`オブジェクト。
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
Self: `AdjustedTotalShares`および`AdjustedTreasuryShares`列が
|
62
|
+
追加された、新しいPricesオブジェクト。
|
63
|
+
"""
|
64
|
+
shares = statements.number_of_shares().rename({"Date": "ReportDate"})
|
65
|
+
|
66
|
+
adjusted = (
|
67
|
+
self.data.join_asof(
|
68
|
+
shares,
|
69
|
+
left_on="Date",
|
70
|
+
right_on="ReportDate",
|
71
|
+
by="Code",
|
72
|
+
check_sortedness=False,
|
73
|
+
)
|
74
|
+
.with_columns(
|
75
|
+
(1.0 / pl.col("AdjustmentFactor"))
|
76
|
+
.cum_prod()
|
77
|
+
.over("Code", "ReportDate")
|
78
|
+
.alias("CumulativeRatio"),
|
79
|
+
)
|
80
|
+
.select(
|
81
|
+
"Date",
|
82
|
+
"Code",
|
83
|
+
(pl.col("TotalShares", "TreasuryShares") * pl.col("CumulativeRatio"))
|
84
|
+
.round(0)
|
85
|
+
.cast(pl.Int64)
|
86
|
+
.name.prefix("Adjusted"),
|
87
|
+
)
|
88
|
+
)
|
89
|
+
|
90
|
+
data = self.data.join(adjusted, on=["Date", "Code"], how="left")
|
91
|
+
|
92
|
+
return self.__class__(data)
|
kabukit/core/reports.py
ADDED
kabukit/core/statements.py
CHANGED
@@ -1,7 +1,24 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
import polars as pl
|
6
|
+
|
3
7
|
from .base import Base
|
4
8
|
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from polars import DataFrame
|
11
|
+
|
5
12
|
|
6
13
|
class Statements(Base):
|
7
|
-
|
14
|
+
def number_of_shares(self) -> DataFrame:
|
15
|
+
"""発行済株式数を取得する。"""
|
16
|
+
return self.data.filter(
|
17
|
+
pl.col("TotalShares").is_not_null(),
|
18
|
+
).select(
|
19
|
+
"Date",
|
20
|
+
"Code",
|
21
|
+
"TotalShares",
|
22
|
+
"TreasuryShares",
|
23
|
+
"AverageOutstandingShares",
|
24
|
+
)
|
kabukit/edinet/__init__.py
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
from .concurrent import fetch, fetch_list
|
1
|
+
from .concurrent import fetch, fetch_csv, fetch_list
|
2
2
|
|
3
|
-
__all__ = ["fetch", "fetch_list"]
|
3
|
+
__all__ = ["fetch", "fetch_csv", "fetch_list"]
|
kabukit/edinet/client.py
CHANGED
@@ -6,14 +6,13 @@ import zipfile
|
|
6
6
|
from enum import StrEnum
|
7
7
|
from typing import TYPE_CHECKING
|
8
8
|
|
9
|
-
import polars as pl
|
10
9
|
from polars import DataFrame
|
11
10
|
|
12
11
|
from kabukit.core.client import Client
|
13
12
|
from kabukit.utils.config import load_dotenv
|
14
13
|
from kabukit.utils.params import get_params
|
15
14
|
|
16
|
-
from .doc import clean_csv, clean_list
|
15
|
+
from .doc import clean_csv, clean_list, read_csv
|
17
16
|
|
18
17
|
if TYPE_CHECKING:
|
19
18
|
import datetime
|
@@ -103,11 +102,8 @@ class EdinetClient(Client):
|
|
103
102
|
for info in zf.infolist():
|
104
103
|
if info.filename.endswith(".csv"):
|
105
104
|
with zf.open(info) as f:
|
106
|
-
|
107
|
-
|
108
|
-
separator="\t",
|
109
|
-
encoding="utf-16-le",
|
110
|
-
).pipe(clean_csv, doc_id)
|
105
|
+
df = read_csv(f.read())
|
106
|
+
return clean_csv(df, doc_id)
|
111
107
|
|
112
108
|
msg = "CSV is not available."
|
113
109
|
raise ValueError(msg)
|
kabukit/edinet/concurrent.py
CHANGED
@@ -83,13 +83,14 @@ async def fetch_list(
|
|
83
83
|
if limit is not None:
|
84
84
|
dates = dates[:limit]
|
85
85
|
|
86
|
-
|
86
|
+
df = await fetch(
|
87
87
|
"list",
|
88
88
|
dates,
|
89
89
|
max_concurrency=max_concurrency,
|
90
90
|
progress=progress,
|
91
91
|
callback=callback,
|
92
92
|
)
|
93
|
+
return df.sort("Date")
|
93
94
|
|
94
95
|
|
95
96
|
async def fetch_csv(
|
@@ -121,10 +122,11 @@ async def fetch_csv(
|
|
121
122
|
if limit is not None:
|
122
123
|
doc_ids = doc_ids[:limit]
|
123
124
|
|
124
|
-
|
125
|
+
df = await fetch(
|
125
126
|
"csv",
|
126
127
|
doc_ids,
|
127
128
|
max_concurrency=max_concurrency,
|
128
129
|
progress=progress,
|
129
130
|
callback=callback,
|
130
131
|
)
|
132
|
+
return df.sort("docID")
|
kabukit/edinet/doc.py
CHANGED
@@ -13,16 +13,30 @@ def clean_list(df: DataFrame, date: str | datetime.date) -> DataFrame:
|
|
13
13
|
if isinstance(date, str):
|
14
14
|
date = datetime.datetime.strptime(date, "%Y-%m-%d").date() # noqa: DTZ007
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
16
|
+
null_columns = [c for c in df.columns if df[c].dtype == pl.Null]
|
17
|
+
|
18
|
+
return (
|
19
|
+
df.with_columns(
|
20
|
+
pl.col(null_columns).cast(pl.String),
|
21
|
+
)
|
22
|
+
.with_columns(
|
23
|
+
pl.lit(date).alias("Date"),
|
24
|
+
pl.col("^.+DateTime$").str.to_datetime("%Y-%m-%d %H:%M", strict=False),
|
25
|
+
pl.col("^period.+$").str.to_date("%Y-%m-%d", strict=False),
|
26
|
+
pl.col("^.+Flag$").cast(pl.Int8).cast(pl.Boolean),
|
27
|
+
pl.col("^.+Code$").cast(pl.String),
|
28
|
+
)
|
29
|
+
.select("Date", pl.exclude("Date"))
|
30
|
+
)
|
31
|
+
|
32
|
+
|
33
|
+
def read_csv(data: bytes) -> DataFrame:
|
34
|
+
return pl.read_csv(
|
35
|
+
data,
|
36
|
+
separator="\t",
|
37
|
+
encoding="utf-16-le",
|
38
|
+
infer_schema_length=None,
|
39
|
+
)
|
26
40
|
|
27
41
|
|
28
42
|
def clean_csv(df: DataFrame, doc_id: str) -> DataFrame:
|
kabukit/jquants/__init__.py
CHANGED
kabukit/jquants/client.py
CHANGED
@@ -257,6 +257,7 @@ class JQuantsClient(Client):
|
|
257
257
|
date: str | datetime.date | None = None,
|
258
258
|
*,
|
259
259
|
clean: bool = True,
|
260
|
+
with_date: bool = True,
|
260
261
|
) -> DataFrame:
|
261
262
|
"""四半期毎の決算短信サマリーおよび業績・配当の修正に関する開示情報を取得する。
|
262
263
|
|
@@ -264,6 +265,8 @@ class JQuantsClient(Client):
|
|
264
265
|
code (str, optional): 財務情報を取得する銘柄のコード。
|
265
266
|
date (str | datetime.date, optional): 財務情報を取得する日付。
|
266
267
|
clean (bool, optional): 取得したデータをクリーンアップするかどうか。
|
268
|
+
with_date (bool, optional): クリーンアップ後に営業日ベースで開示日の翌日を
|
269
|
+
計算して`Date`列を追加するかどうか。
|
267
270
|
|
268
271
|
Returns:
|
269
272
|
財務情報を含むDataFrame。
|
@@ -283,10 +286,12 @@ class JQuantsClient(Client):
|
|
283
286
|
dfs = [df async for df in self.iter_pages(url, params, name)]
|
284
287
|
df = pl.concat(dfs)
|
285
288
|
|
286
|
-
if df.is_empty():
|
289
|
+
if df.is_empty() or not clean:
|
287
290
|
return df
|
288
291
|
|
289
|
-
|
292
|
+
df = statements.clean(df)
|
293
|
+
|
294
|
+
return statements.with_date(df) if with_date else df
|
290
295
|
|
291
296
|
async def get_announcement(self) -> DataFrame:
|
292
297
|
"""翌日発表予定の決算情報を取得する。
|
kabukit/jquants/concurrent.py
CHANGED
@@ -23,7 +23,7 @@ async def fetch(
|
|
23
23
|
progress: Progress | None = None,
|
24
24
|
callback: Callback | None = None,
|
25
25
|
) -> DataFrame:
|
26
|
-
"""
|
26
|
+
"""複数の銘柄の各種データを取得し、単一のDataFrameにまとめて返す。
|
27
27
|
|
28
28
|
Args:
|
29
29
|
resource (str): 取得するデータの種類。JQuantsClientのメソッド名から"get_"を
|
kabukit/jquants/schema.py
CHANGED
@@ -45,8 +45,9 @@ class PriceColumns(BaseColumns):
|
|
45
45
|
|
46
46
|
|
47
47
|
class StatementColumns(BaseColumns):
|
48
|
-
Date = "
|
49
|
-
|
48
|
+
Date = "日付"
|
49
|
+
DisclosedDate = "開示日"
|
50
|
+
DisclosedTime = "開示時刻"
|
50
51
|
Code = "銘柄コード"
|
51
52
|
DisclosureNumber = "開示番号"
|
52
53
|
TypeOfDocument = "開示書類種別"
|
@@ -129,13 +130,10 @@ class StatementColumns(BaseColumns):
|
|
129
130
|
ChangesInAccountingEstimates = "会計上の見積りの変更"
|
130
131
|
RetrospectiveRestatement = "修正再表示"
|
131
132
|
|
132
|
-
# NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock
|
133
|
-
|
134
|
-
#
|
135
|
-
NumberOfTreasuryStock = "期末自己株式数"
|
136
|
-
AverageNumberOfShares = "期中平均株式数"
|
133
|
+
TotalShares = "期末発行済株式数" # 自己株式を含む (NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock)
|
134
|
+
TreasuryShares = "期末自己株式数" # (NumberOfTreasuryStockAtTheEndOfFiscalYear)
|
135
|
+
AverageOutstandingShares = "期中平均株式数" # 自己株式を除く。EPSなどの計算に使用される (AverageNumberOfShares)
|
137
136
|
|
138
|
-
"""
|
139
137
|
NonConsolidatedNetSales = "売上高_非連結"
|
140
138
|
NonConsolidatedOperatingProfit = "営業利益_非連結"
|
141
139
|
NonConsolidatedOrdinaryProfit = "経常利益_非連結"
|
@@ -170,7 +168,6 @@ class StatementColumns(BaseColumns):
|
|
170
168
|
NextYearForecastNonConsolidatedOrdinaryProfit = "経常利益_予想_翌事業年度期末_非連結"
|
171
169
|
NextYearForecastNonConsolidatedProfit = "当期純利益_予想_翌事業年度期末_非連結"
|
172
170
|
NextYearForecastNonConsolidatedEarningsPerShare = "一株あたり当期純利益_予想_翌事業年度期末_非連結"
|
173
|
-
"""
|
174
171
|
|
175
172
|
|
176
173
|
def rename(df: DataFrame, *, strict: bool = False) -> DataFrame:
|
kabukit/jquants/statements.py
CHANGED
@@ -13,19 +13,18 @@ if TYPE_CHECKING:
|
|
13
13
|
|
14
14
|
def clean(df: DataFrame) -> DataFrame:
|
15
15
|
return (
|
16
|
-
df.select(pl.exclude(r"^.*\(REIT\)
|
16
|
+
df.select(pl.exclude(r"^.*\(REIT\)$"))
|
17
17
|
.rename(
|
18
18
|
{
|
19
|
-
"DisclosedDate": "Date",
|
20
|
-
"DisclosedTime": "Time",
|
21
19
|
"LocalCode": "Code",
|
22
|
-
"NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock": "
|
23
|
-
"NumberOfTreasuryStockAtTheEndOfFiscalYear": "
|
20
|
+
"NumberOfIssuedAndOutstandingSharesAtTheEndOfFiscalYearIncludingTreasuryStock": "TotalShares", # noqa: E501
|
21
|
+
"NumberOfTreasuryStockAtTheEndOfFiscalYear": "TreasuryShares",
|
22
|
+
"AverageNumberOfShares": "AverageOutstandingShares",
|
24
23
|
},
|
25
24
|
)
|
26
25
|
.with_columns(
|
27
26
|
pl.col("^.*Date$").str.to_date("%Y-%m-%d", strict=False),
|
28
|
-
pl.col("
|
27
|
+
pl.col("DisclosedTime").str.to_time("%H:%M:%S", strict=False),
|
29
28
|
pl.col("TypeOfCurrentPeriod").cast(pl.Categorical),
|
30
29
|
)
|
31
30
|
.pipe(_cast_float)
|
@@ -45,10 +44,17 @@ def _cast_float(df: DataFrame) -> DataFrame:
|
|
45
44
|
"Earnings",
|
46
45
|
"Equity",
|
47
46
|
"NetSales",
|
48
|
-
"NumberOf",
|
49
47
|
"PayoutRatio",
|
50
48
|
"Profit",
|
51
49
|
]
|
50
|
+
).with_columns(
|
51
|
+
pl.col(
|
52
|
+
"TotalShares",
|
53
|
+
"TreasuryShares",
|
54
|
+
).cast(pl.Int64, strict=False),
|
55
|
+
pl.col(
|
56
|
+
"AverageOutstandingShares",
|
57
|
+
).cast(pl.Float64, strict=False),
|
52
58
|
)
|
53
59
|
|
54
60
|
|
@@ -77,15 +83,22 @@ def get_holidays(year: int | None = None, n: int = 10) -> list[datetime.date]:
|
|
77
83
|
return sorted(dates.keys())
|
78
84
|
|
79
85
|
|
80
|
-
def
|
81
|
-
"""
|
82
|
-
holidays = get_holidays(year=year)
|
86
|
+
def with_date(df: DataFrame, year: int | None = None) -> DataFrame:
|
87
|
+
"""`Date`列を追加する。
|
83
88
|
|
84
|
-
|
89
|
+
開示日が休日のとき、あるいは、開示時刻が15時以降の場合、Dateを開示日の翌営業日に設定する。
|
90
|
+
"""
|
91
|
+
is_after_hours = pl.col("DisclosedTime").is_null() | (
|
92
|
+
pl.col("DisclosedTime") > datetime.time(15, 0)
|
93
|
+
)
|
85
94
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
95
|
+
holidays = get_holidays(year=year)
|
96
|
+
|
97
|
+
return df.select(
|
98
|
+
pl.when(is_after_hours)
|
99
|
+
.then(pl.col("DisclosedDate") + datetime.timedelta(days=1))
|
100
|
+
.otherwise(pl.col("DisclosedDate"))
|
101
|
+
.dt.add_business_days(0, holidays=holidays, roll="forward")
|
90
102
|
.alias("Date"),
|
103
|
+
pl.all(),
|
91
104
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: kabukit
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
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,10 +28,9 @@ 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
|
-
Requires-Dist: altair>=5
|
32
31
|
Requires-Dist: async-typer>=0.1
|
33
32
|
Requires-Dist: holidays>=0.81
|
34
|
-
Requires-Dist: httpx>=0.28
|
33
|
+
Requires-Dist: httpx>=0.28
|
35
34
|
Requires-Dist: platformdirs>=4
|
36
35
|
Requires-Dist: polars>=1
|
37
36
|
Requires-Dist: python-dotenv>=1
|
@@ -0,0 +1,39 @@
|
|
1
|
+
kabukit/__init__.py,sha256=zO4BQg4wvNkK8fqDfMwd22KTIPAgXb79iNDldKfTWf4,371
|
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=HKzxE2ue_VvdVazCFcU25PmDs3LJIjKvO7l-jdYJV_E,531
|
9
|
+
kabukit/cli/auth.py,sha256=E4M9ChDrtCb1DvYrAds33k6eKlXZUQCL9yCNPyTSnUk,2727
|
10
|
+
kabukit/cli/get.py,sha256=5t_FXNT64tfP4m_mWGfZdOXEQcRea1UIcPIHk4QC4Nc,4405
|
11
|
+
kabukit/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
+
kabukit/core/base.py,sha256=YQfGw3P2QshRdut3KhtrAleVvnFsIPOw5oGA9azypAA,1695
|
13
|
+
kabukit/core/client.py,sha256=tVq1r3zpOfjmOtnRI1KPZHgTgBZYIpJzfw15i2kAM48,676
|
14
|
+
kabukit/core/info.py,sha256=5BX7mDavF6g-b0KzHgKIFHUS5701BoaHtw1JcHSsy94,174
|
15
|
+
kabukit/core/list.py,sha256=AjnXzC9XIu21l6IBEHHAS5VAnfxTfkAA9m1WAOZJNa8,174
|
16
|
+
kabukit/core/prices.py,sha256=9v_eu-ylYJmDNLgruUeH6clW-mtwO-VRxrlLkWXp8VE,3601
|
17
|
+
kabukit/core/reports.py,sha256=ch_xe84GbB17JTfmY3ArQqneQ2XOuvrAykBTAyNmWuM,177
|
18
|
+
kabukit/core/statements.py,sha256=iHa3zHH4HNLbOJF-GgC4HbpyrfFi-KUmrY1M3wzECZc,535
|
19
|
+
kabukit/edinet/__init__.py,sha256=PKa4D-jVpeoOkdVp9NwwpgAiGEBjqvmJLmpzF-9SlVk,101
|
20
|
+
kabukit/edinet/client.py,sha256=BlM7pjXpKweOo-ses41JiKinqRn4qerMN5NF7hgYKgg,3275
|
21
|
+
kabukit/edinet/concurrent.py,sha256=2YPzIFuuOB8-gL3CnHIlP486QH5d21qjKNKGCFK7Hzk,4707
|
22
|
+
kabukit/edinet/doc.py,sha256=6ZDgmm8DHmEMOA4NjNz-dHLMc7IzzYn-nVyMQGLWb8I,1220
|
23
|
+
kabukit/jquants/__init__.py,sha256=xY0H6x4_51ZBqbrT2TdmGGFbTD-hosZiDzVIz4UXPv0,112
|
24
|
+
kabukit/jquants/client.py,sha256=shAyJoOHUa3h1fFfHdlm2ZYdQL3Oauy2S9VTJfNb_RI,11523
|
25
|
+
kabukit/jquants/concurrent.py,sha256=VGpWRq2NoDJCpyLAXegsJYPXvkY1hdjuuxxAprQzwEc,3294
|
26
|
+
kabukit/jquants/info.py,sha256=AUymo6EtLdJ_L-0yNG1gd7rJ8Jkjw7tvsDM5Vw4QNFA,843
|
27
|
+
kabukit/jquants/prices.py,sha256=oApQpdgzHwPw11XHpdg8ccZS7kybGtV8APZlpD2L3Yw,882
|
28
|
+
kabukit/jquants/schema.py,sha256=dFbjkTglVcwR0YjQskLrphHIkM85Tc03yFJKm9xBL7g,9767
|
29
|
+
kabukit/jquants/statements.py,sha256=tdxGVIchJImAqTL-lNYLD0yPsSVm-mDzXA8jPMRRne4,3067
|
30
|
+
kabukit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
31
|
+
kabukit/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
32
|
+
kabukit/utils/concurrent.py,sha256=m2zVqJUz4xFVpNbEc9YEaj3Jmbl82njupdm4_rBOoYQ,4510
|
33
|
+
kabukit/utils/config.py,sha256=Jp-2TCnIj_QqA71FzYCkHXbvXvhw_1JVl4PR0foA1vM,618
|
34
|
+
kabukit/utils/date.py,sha256=DEC6Ac5LS8eiW6JtrmcD3U1pX4qzXtx4ale0swpO4Ag,937
|
35
|
+
kabukit/utils/params.py,sha256=qcaJbf6CWPUoZAZsYDTaZSnBUWeAersbWnR_iiYW9GM,1108
|
36
|
+
kabukit-0.4.0.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
|
37
|
+
kabukit-0.4.0.dist-info/entry_points.txt,sha256=vvX771TemoM-35vVizW3JJ70HvRXnd2tX4P1Btzyoxs,46
|
38
|
+
kabukit-0.4.0.dist-info/METADATA,sha256=Ku0jupeK7kRZUq_4OX-N5RXE-CJM02CzdnswxpOvd_g,3187
|
39
|
+
kabukit-0.4.0.dist-info/RECORD,,
|
kabukit-0.3.0.dist-info/RECORD
DELETED
@@ -1,37 +0,0 @@
|
|
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=HKzxE2ue_VvdVazCFcU25PmDs3LJIjKvO7l-jdYJV_E,531
|
9
|
-
kabukit/cli/auth.py,sha256=MWLxEiINrqMGJSiVHcv09EuPlyGeCitHqJq7kumzbr8,2735
|
10
|
-
kabukit/cli/get.py,sha256=-qeoy6XRqj6fdGcTRPaA-ZAJlGFWMK7QOFNVeEtSPeM,2913
|
11
|
-
kabukit/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
|
-
kabukit/core/base.py,sha256=SSUPzcFot0shN5nRY4cKN_Rt13TBBcyxHwqqwT-vcac,1280
|
13
|
-
kabukit/core/client.py,sha256=lvjrshzOjyea2jRmc11Dl5OAJUGGN3PfKXWxoveG6hY,664
|
14
|
-
kabukit/core/info.py,sha256=5BX7mDavF6g-b0KzHgKIFHUS5701BoaHtw1JcHSsy94,174
|
15
|
-
kabukit/core/prices.py,sha256=Axm9cceVxnHIWCW6IHqEuCC4M73JtWCRzjXA42Qd7JE,781
|
16
|
-
kabukit/core/statements.py,sha256=18vD5WRjBGVSXvmCfTvGAwgjnajLbN-tH6V91sX44iI,94
|
17
|
-
kabukit/edinet/__init__.py,sha256=9bAWDIgVZKw84GF4z6FilUP_B22ih-ixLm8cQHxaoS8,77
|
18
|
-
kabukit/edinet/client.py,sha256=OSz8BmSBzzZMD-wk_9WbhZLfDY901s5CN1UissoDtyc,3410
|
19
|
-
kabukit/edinet/concurrent.py,sha256=RCNiD1hCP0Yat78IzfF4709tn_QgD1CTS17g_fNl7ZQ,4656
|
20
|
-
kabukit/edinet/doc.py,sha256=ieze_886Tzkp6QYRJD3GuSXj6-LS43MqmA72zEx1kjA,959
|
21
|
-
kabukit/jquants/__init__.py,sha256=n-sg0pLajSfyJ8snU6I_Q52rwmuaGCwAyz1yVbeMSw4,75
|
22
|
-
kabukit/jquants/client.py,sha256=cszePJwByMgDJ4hFGrkEQeWchRb2HsNS48dzePKl7Kk,11257
|
23
|
-
kabukit/jquants/concurrent.py,sha256=5XObYKMMYNmrq-8JLtVg0uBGoEJwI9Luvy6ndamiTz8,3288
|
24
|
-
kabukit/jquants/info.py,sha256=AUymo6EtLdJ_L-0yNG1gd7rJ8Jkjw7tvsDM5Vw4QNFA,843
|
25
|
-
kabukit/jquants/prices.py,sha256=oApQpdgzHwPw11XHpdg8ccZS7kybGtV8APZlpD2L3Yw,882
|
26
|
-
kabukit/jquants/schema.py,sha256=HIoKDoIWrT0XNDHz5Fm4DtDGLLRqrq6TIkMTvq_Jheg,9647
|
27
|
-
kabukit/jquants/statements.py,sha256=H-Ymat2gqAOsTaBNPtqLj8-5MLda72UkJW8SK2PvD5Q,2674
|
28
|
-
kabukit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
|
-
kabukit/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
|
-
kabukit/utils/concurrent.py,sha256=m2zVqJUz4xFVpNbEc9YEaj3Jmbl82njupdm4_rBOoYQ,4510
|
31
|
-
kabukit/utils/config.py,sha256=Jp-2TCnIj_QqA71FzYCkHXbvXvhw_1JVl4PR0foA1vM,618
|
32
|
-
kabukit/utils/date.py,sha256=DEC6Ac5LS8eiW6JtrmcD3U1pX4qzXtx4ale0swpO4Ag,937
|
33
|
-
kabukit/utils/params.py,sha256=qcaJbf6CWPUoZAZsYDTaZSnBUWeAersbWnR_iiYW9GM,1108
|
34
|
-
kabukit-0.3.0.dist-info/WHEEL,sha256=-neZj6nU9KAMg2CnCY6T3w8J53nx1kFGw_9HfoSzM60,79
|
35
|
-
kabukit-0.3.0.dist-info/entry_points.txt,sha256=vvX771TemoM-35vVizW3JJ70HvRXnd2tX4P1Btzyoxs,46
|
36
|
-
kabukit-0.3.0.dist-info/METADATA,sha256=UiJ_0PCOg-ghUW5iCdh7Tpxy4-NtgGChhzGI7gkoncM,3214
|
37
|
-
kabukit-0.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|