kabukit 0.3.1__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kabukit-0.3.1 → kabukit-0.5.0}/PKG-INFO +2 -3
- {kabukit-0.3.1 → kabukit-0.5.0}/pyproject.toml +4 -10
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/cli/auth.py +2 -2
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/core/base.py +12 -1
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/core/client.py +1 -1
- kabukit-0.5.0/src/kabukit/core/prices.py +104 -0
- kabukit-0.5.0/src/kabukit/core/statements.py +24 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/jquants/client.py +7 -2
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/jquants/concurrent.py +1 -1
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/jquants/schema.py +6 -9
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/jquants/statements.py +28 -15
- kabukit-0.3.1/src/kabukit/core/prices.py +0 -30
- kabukit-0.3.1/src/kabukit/core/statements.py +0 -7
- {kabukit-0.3.1 → kabukit-0.5.0}/LICENSE +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/README.md +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/__init__.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/analysis/__init__.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/analysis/indicators.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/analysis/preprocess.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/analysis/screener.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/analysis/visualization.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/cli/__init__.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/cli/app.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/cli/get.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/core/__init__.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/core/info.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/core/list.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/core/reports.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/edinet/__init__.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/edinet/client.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/edinet/concurrent.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/edinet/doc.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/jquants/__init__.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/jquants/info.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/jquants/prices.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/py.typed +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/utils/__init__.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/utils/concurrent.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/utils/config.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/utils/date.py +0 -0
- {kabukit-0.3.1 → kabukit-0.5.0}/src/kabukit/utils/params.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: kabukit
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.5.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
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "kabukit"
|
7
|
-
version = "0.
|
7
|
+
version = "0.5.0"
|
8
8
|
description = "A Python toolkit for Japanese financial market data, supporting J-Quants and EDINET APIs."
|
9
9
|
readme = "README.md"
|
10
10
|
license = { file = "LICENSE" }
|
@@ -16,10 +16,9 @@ classifiers = [
|
|
16
16
|
]
|
17
17
|
requires-python = ">=3.13"
|
18
18
|
dependencies = [
|
19
|
-
"altair>=5",
|
20
19
|
"async-typer>=0.1",
|
21
20
|
"holidays>=0.81",
|
22
|
-
"httpx>=0.28
|
21
|
+
"httpx>=0.28",
|
23
22
|
"platformdirs>=4",
|
24
23
|
"polars>=1",
|
25
24
|
"python-dotenv>=1",
|
@@ -36,6 +35,7 @@ Issues = "https://github.com/daizutabi/kabukit/issues"
|
|
36
35
|
|
37
36
|
[dependency-groups]
|
38
37
|
dev = [
|
38
|
+
"altair>=5",
|
39
39
|
"basedpyright>=1.31.4",
|
40
40
|
"marimo[lsp]>=0.16",
|
41
41
|
"numpy>=2.3.3", # polars 1.33 type hinting workaround,
|
@@ -54,13 +54,7 @@ dev = [
|
|
54
54
|
docs = ["mkapi>=4.4", "mkdocs-marimo", "mkdocs-material"]
|
55
55
|
|
56
56
|
[tool.pytest.ini_options]
|
57
|
-
addopts = [
|
58
|
-
"--cov=kabukit",
|
59
|
-
"--cov-report=lcov:lcov.info",
|
60
|
-
"--doctest-modules",
|
61
|
-
"-m",
|
62
|
-
"not integration and not validation",
|
63
|
-
]
|
57
|
+
addopts = ["--cov=kabukit", "--cov-report=lcov:lcov.info", "--doctest-modules"]
|
64
58
|
testpaths = ["tests/unit", "tests/integration", "tests/validation"]
|
65
59
|
markers = [
|
66
60
|
"integration: marks tests as integration tests",
|
@@ -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トークンを保存しました。")
|
@@ -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)
|
@@ -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クライアントを閉じる。"""
|
@@ -0,0 +1,104 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
import polars as pl
|
6
|
+
|
7
|
+
from .base import Base
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from datetime import timedelta
|
11
|
+
from typing import Self
|
12
|
+
|
13
|
+
from polars import Expr
|
14
|
+
|
15
|
+
from .statements import Statements
|
16
|
+
|
17
|
+
|
18
|
+
class Prices(Base):
|
19
|
+
def truncate(self, every: str | timedelta | Expr) -> Self:
|
20
|
+
data = (
|
21
|
+
self.data.group_by(pl.col("Date").dt.truncate(every), "Code")
|
22
|
+
.agg(
|
23
|
+
pl.col("Open").drop_nulls().first(),
|
24
|
+
pl.col("High").max(),
|
25
|
+
pl.col("Low").min(),
|
26
|
+
pl.col("Close").drop_nulls().last(),
|
27
|
+
pl.col("Volume").sum(),
|
28
|
+
pl.col("TurnoverValue").sum(),
|
29
|
+
)
|
30
|
+
.sort("Code", "Date")
|
31
|
+
)
|
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`から`IssuedShares`(発行済株式総数)と
|
45
|
+
`TreasuryShares`(自己株式数)を取得し、それぞれを調整します。
|
46
|
+
計算結果は、元の列名との混同を避けるため、接頭辞`Adjusted`を付与した
|
47
|
+
新しい列(`AdjustedIssuedShares`, `AdjustedTreasuryShares`)として
|
48
|
+
追加されます。
|
49
|
+
|
50
|
+
.. note::
|
51
|
+
この計算は、決算発表間の株式数の変動が、株式分割・併合
|
52
|
+
(`AdjustmentFactor`)にのみ起因すると仮定しています。
|
53
|
+
期中に行われる増資や自己株式取得など、`AdjustmentFactor`に
|
54
|
+
反映されないイベントによる株式数の変動は考慮されません。
|
55
|
+
|
56
|
+
Args:
|
57
|
+
statements (Statements): 財務データを提供する`Statements`オブジェクト。
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
Self: `AdjustedIssuedShares`および`AdjustedTreasuryShares`列が
|
61
|
+
追加された、新しいPricesオブジェクト。
|
62
|
+
"""
|
63
|
+
shares = statements.number_of_shares().rename({"Date": "ReportDate"})
|
64
|
+
|
65
|
+
adjusted = (
|
66
|
+
self.data.join_asof(
|
67
|
+
shares,
|
68
|
+
left_on="Date",
|
69
|
+
right_on="ReportDate",
|
70
|
+
by="Code",
|
71
|
+
check_sortedness=False,
|
72
|
+
)
|
73
|
+
.with_columns(
|
74
|
+
(1.0 / pl.col("AdjustmentFactor"))
|
75
|
+
.cum_prod()
|
76
|
+
.over("Code", "ReportDate")
|
77
|
+
.alias("CumulativeRatio"),
|
78
|
+
)
|
79
|
+
.select(
|
80
|
+
"Date",
|
81
|
+
"Code",
|
82
|
+
(pl.col("IssuedShares", "TreasuryShares") * pl.col("CumulativeRatio"))
|
83
|
+
.round(0)
|
84
|
+
.cast(pl.Int64)
|
85
|
+
.name.prefix("Adjusted"),
|
86
|
+
)
|
87
|
+
)
|
88
|
+
|
89
|
+
data = self.data.join(adjusted, on=["Date", "Code"], how="left")
|
90
|
+
|
91
|
+
return self.__class__(data)
|
92
|
+
|
93
|
+
# def with_yields(self, statements: Statements) -> Self:
|
94
|
+
# """各種利回り指標(収益利回り、純資産利回り、配当利回り)を計算し、列として追加する。
|
95
|
+
|
96
|
+
# Args:
|
97
|
+
# statements (Statements): 財務データを提供する`Statements`オブジェクト。
|
98
|
+
|
99
|
+
# Returns:
|
100
|
+
# Self: 各種利回り指標が追加された、新しいPricesオブジェクト。
|
101
|
+
# """
|
102
|
+
# prices_with_adjusted_shares = self.with_adjusted_shares(statements)
|
103
|
+
# data_with_yields = calculate_all_yields(prices_with_adjusted_shares, statements)
|
104
|
+
# return self.__class__(data_with_yields)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
import polars as pl
|
6
|
+
|
7
|
+
from .base import Base
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from polars import DataFrame
|
11
|
+
|
12
|
+
|
13
|
+
class Statements(Base):
|
14
|
+
def number_of_shares(self) -> DataFrame:
|
15
|
+
"""発行済株式数を取得する。"""
|
16
|
+
return self.data.filter(
|
17
|
+
pl.col("IssuedShares").is_not_null(),
|
18
|
+
).select(
|
19
|
+
"Date",
|
20
|
+
"Code",
|
21
|
+
"IssuedShares",
|
22
|
+
"TreasuryShares",
|
23
|
+
"AverageOutstandingShares",
|
24
|
+
)
|
@@ -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
|
"""翌日発表予定の決算情報を取得する。
|
@@ -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_"を
|
@@ -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
|
+
IssuedShares = "期末発行済株式数" # 自己株式を含む (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:
|
@@ -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": "IssuedShares", # 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
|
+
"IssuedShares",
|
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,30 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from typing import TYPE_CHECKING
|
4
|
-
|
5
|
-
import polars as pl
|
6
|
-
|
7
|
-
from .base import Base
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from datetime import timedelta
|
11
|
-
from typing import Self
|
12
|
-
|
13
|
-
from polars import Expr
|
14
|
-
|
15
|
-
|
16
|
-
class Prices(Base):
|
17
|
-
def truncate(self, every: str | timedelta | Expr) -> Self:
|
18
|
-
data = (
|
19
|
-
self.data.group_by(pl.col("Date").dt.truncate(every), "Code")
|
20
|
-
.agg(
|
21
|
-
pl.col("Open").drop_nulls().first(),
|
22
|
-
pl.col("High").max(),
|
23
|
-
pl.col("Low").min(),
|
24
|
-
pl.col("Close").drop_nulls().last(),
|
25
|
-
pl.col("Volume").sum(),
|
26
|
-
pl.col("TurnoverValue").sum(),
|
27
|
-
)
|
28
|
-
.sort("Code", "Date")
|
29
|
-
)
|
30
|
-
return self.__class__(data)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|