fin68 0.1.3__cp310-cp310-macosx_10_9_universal2.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.
- fin68/__init__.py +323 -0
- fin68/__init__.pyi +81 -0
- fin68/_core/__init__.py +0 -0
- fin68/_version.py +17 -0
- fin68/auth/__init__.py +0 -0
- fin68/auth/api_key.py +0 -0
- fin68/clients/__init__.py +3 -0
- fin68/clients/base.py +17 -0
- fin68/clients/eod/MarketEod.py +142 -0
- fin68/clients/eod/MarketEod.pyi +95 -0
- fin68/clients/eod/SectorEod.py +146 -0
- fin68/clients/eod/SectorEod.pyi +86 -0
- fin68/clients/eod/StockEod.py +134 -0
- fin68/clients/eod/StockEod.pyi +92 -0
- fin68/clients/eod/__init__.py +6 -0
- fin68/clients/eod/helper.py +65 -0
- fin68/clients/eod/icb_types.py +111 -0
- fin68/clients/eod_main.py +169 -0
- fin68/clients/eod_main.pyi +102 -0
- fin68/clients/financials.py +0 -0
- fin68/clients/index.py +0 -0
- fin68/clients/info.py +0 -0
- fin68/config.py +0 -0
- fin68/exceptions.py +28 -0
- fin68/models/__init__.py +0 -0
- fin68/models/base.py +0 -0
- fin68/models/eod.py +0 -0
- fin68/models/financials.py +0 -0
- fin68/models/index.py +0 -0
- fin68/models/meta.py +0 -0
- fin68/transport/__init__.py +0 -0
- fin68/transport/cache.py +0 -0
- fin68/transport/http.py +0 -0
- fin68/transport/rate_limiter.py +0 -0
- fin68/types.py +90 -0
- fin68/utils/__init__.py +5 -0
- fin68/utils/_validate_date_eod.py +9 -0
- fin68/utils/convert.py +0 -0
- fin68/utils/pagination.py +0 -0
- fin68/utils/time.py +0 -0
- fin68/validators.py +0 -0
- fin68-0.1.3.dist-info/METADATA +129 -0
- fin68-0.1.3.dist-info/RECORD +55 -0
- fin68-0.1.3.dist-info/WHEEL +6 -0
- fin68-0.1.3.dist-info/licenses/LICENSE +21 -0
- fin68-0.1.3.dist-info/top_level.txt +2 -0
- private_core/__init__.py +1 -0
- private_core/auth_core.cpython-310-darwin.so +0 -0
- private_core/eod_core.cpython-310-darwin.so +0 -0
- private_core/financials_core.cpython-310-darwin.so +0 -0
- private_core/helpers.cpython-310-darwin.so +0 -0
- private_core/http_core.cpython-310-darwin.so +0 -0
- private_core/index_core.cpython-310-darwin.so +0 -0
- private_core/progress.cpython-310-darwin.so +0 -0
- private_core/utils_core.cpython-310-darwin.so +0 -0
@@ -0,0 +1,146 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""
|
4
|
+
fin68.clients.eod.sector
|
5
|
+
========================
|
6
|
+
|
7
|
+
Cung cấp lớp `SectorEod` — các endpoint EOD (End-of-Day) cho dữ liệu ngành (ICB Sector Aggregates).
|
8
|
+
|
9
|
+
Dữ liệu thể hiện hiệu suất, biến động, và khối lượng giao dịch trung bình của từng ngành
|
10
|
+
(ICB Code) hoặc nhóm ngành (Sub-sector). Phù hợp cho phân tích xoay vòng ngành (sector rotation)
|
11
|
+
và đánh giá xu hướng thị trường theo nhóm.
|
12
|
+
|
13
|
+
Notes
|
14
|
+
-----
|
15
|
+
- Dữ liệu được lấy qua `private_core.eod_core.fetch_ohlcv()`.
|
16
|
+
- Tất cả kết quả trả về dạng `pandas.DataFrame`.
|
17
|
+
- Cho phép truy vấn nhiều mã ngành cùng lúc.
|
18
|
+
"""
|
19
|
+
|
20
|
+
from typing import TYPE_CHECKING, Sequence, Union
|
21
|
+
from private_core import eod_core
|
22
|
+
from ..base import BaseClient
|
23
|
+
from .helper import DateStr, Interval, _EodMixin
|
24
|
+
from .icb_types import SectorArg,icbMap
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from pandas import DataFrame
|
27
|
+
from private_core.http_core import HttpSession
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
class SectorEod(BaseClient, _EodMixin):
|
33
|
+
"""
|
34
|
+
Cung cấp các endpoint EOD cho nhóm ngành (ICB Sector/Industry).
|
35
|
+
|
36
|
+
Lớp này giúp truy vấn dữ liệu tổng hợp cho từng ngành (ICB Code) theo ngày, tuần, tháng, v.v.
|
37
|
+
|
38
|
+
Parameters
|
39
|
+
----------
|
40
|
+
session : HttpSession
|
41
|
+
Phiên HTTP đã xác thực, được truyền từ `EodClient`.
|
42
|
+
|
43
|
+
Notes
|
44
|
+
-----
|
45
|
+
- Cho phép lấy dữ liệu nhiều mã ngành cùng lúc (list[str]).
|
46
|
+
- Hỗ trợ chuẩn hóa khoảng thời gian và kiểm tra `interval`.
|
47
|
+
- Trả về OHLCV (Open, High, Low, Close, Volume) trung bình của từng ngành.
|
48
|
+
- Các mã ngành tuân theo chuẩn phân loại ICB (ví dụ: `ICB01010`, `ICB03020`...).
|
49
|
+
"""
|
50
|
+
|
51
|
+
def __init__(self, session: "HttpSession") -> None:
|
52
|
+
"""
|
53
|
+
Khởi tạo `SectorEod` với session HTTP đã xác thực.
|
54
|
+
|
55
|
+
Parameters
|
56
|
+
----------
|
57
|
+
session : HttpSession
|
58
|
+
Phiên HTTP dùng chung từ `EodClient`.
|
59
|
+
"""
|
60
|
+
super().__init__(session=session)
|
61
|
+
|
62
|
+
def ohlcv(
|
63
|
+
self,
|
64
|
+
symbol: SectorArg,
|
65
|
+
start: DateStr = None,
|
66
|
+
end: DateStr = None,
|
67
|
+
interval: Interval = "1D",
|
68
|
+
) -> "DataFrame":
|
69
|
+
"""
|
70
|
+
Lấy dữ liệu OHLCV (Open, High, Low, Close, Volume) cho một hoặc nhiều ngành ICB.
|
71
|
+
|
72
|
+
Parameters
|
73
|
+
----------
|
74
|
+
symbol : str or Sequence[str]
|
75
|
+
Mã ngành hoặc danh sách mã ngành (ví dụ: `"ICB01010"`, `["ICB01010", "ICB03020"]`).
|
76
|
+
start : str, optional
|
77
|
+
Ngày bắt đầu, định dạng `"YYYY-MM-DD"`.
|
78
|
+
Nếu không truyền, hệ thống sẽ chọn mặc định (thường là 1 năm gần nhất).
|
79
|
+
end : str, optional
|
80
|
+
Ngày kết thúc, định dạng `"YYYY-MM-DD"`.
|
81
|
+
Nếu không truyền, mặc định là ngày hiện tại.
|
82
|
+
interval : {"1D", "1W", "1M", "3M", "6M", "1Y"}, default "1D"
|
83
|
+
Khoảng thời gian dữ liệu:
|
84
|
+
- `"1D"`: theo ngày
|
85
|
+
- `"1W"`: theo tuần
|
86
|
+
- `"1M"`: theo tháng
|
87
|
+
- `"3M"`, `"6M"`, `"1Y"`: theo quý, nửa năm hoặc năm
|
88
|
+
|
89
|
+
Returns
|
90
|
+
-------
|
91
|
+
DataFrame
|
92
|
+
Bảng dữ liệu OHLCV cho từng mã ngành, gồm:
|
93
|
+
- `icbCode`: mã ngành
|
94
|
+
- `Date`: ngày giao dịch
|
95
|
+
- `Open`, `High`, `Low`, `Close`, `Volume`
|
96
|
+
- Có thể có thêm `MarketCap`, `TurnoverRatio` nếu backend hỗ trợ.
|
97
|
+
|
98
|
+
Raises
|
99
|
+
------
|
100
|
+
ValueError
|
101
|
+
Nếu ngày bắt đầu > ngày kết thúc hoặc `interval` không hợp lệ.
|
102
|
+
ApiRequestError
|
103
|
+
Nếu backend trả lỗi khi gọi API.
|
104
|
+
|
105
|
+
Examples
|
106
|
+
--------
|
107
|
+
>>> from fin68 import client
|
108
|
+
>>> cli = client(api_key="sk_live_...")
|
109
|
+
>>> df = cli.eod.sector.ohlcv("ICB01010", start="2024-01-01", end="2024-06-30")
|
110
|
+
>>> df.head()
|
111
|
+
icbCode Date Open High Low Close Volume
|
112
|
+
0 ICB01010 2024-01-02 1320.25 1334.11 1305.21 1310.55 9.32e6
|
113
|
+
|
114
|
+
>>> # Lấy đồng thời nhiều ngành
|
115
|
+
>>> cli.eod.sector.ohlcv(["ICB01010", "ICB03020"])
|
116
|
+
|
117
|
+
Notes
|
118
|
+
-----
|
119
|
+
- Phù hợp để phân tích hiệu suất ngành theo thời gian.
|
120
|
+
- Dữ liệu thường được tổng hợp từ trung bình trọng số theo vốn hóa.
|
121
|
+
- Wrapper của `private_core.eod_core.fetch_ohlcv`.
|
122
|
+
"""
|
123
|
+
icb_codes=[]
|
124
|
+
if isinstance(symbol, list):
|
125
|
+
for sec in symbol:
|
126
|
+
if sec.lower() not in icbMap:
|
127
|
+
raise ValueError(f"Không tìm thấy mã ngành cho tên '{sec}'")
|
128
|
+
icb_codes.append(icbMap.get(sec.lower()))
|
129
|
+
else:
|
130
|
+
if symbol.lower() not in icbMap:
|
131
|
+
raise ValueError(f"Không tìm thấy mã ngành cho tên '{symbol}'")
|
132
|
+
icb_codes.append(icbMap.get(symbol.lower()))
|
133
|
+
|
134
|
+
|
135
|
+
if not symbol:
|
136
|
+
raise ValueError(f"Không tìm thấy mã ngành cho tên '{symbol}'")
|
137
|
+
start_date, end_date = self._normalize_range(start, end)
|
138
|
+
interval_value = self._validate_interval(interval)
|
139
|
+
payload = eod_core.fetch_ohlcv(
|
140
|
+
self.session,
|
141
|
+
icb_codes,
|
142
|
+
start=start_date,
|
143
|
+
end=end_date,
|
144
|
+
interval=interval_value,
|
145
|
+
)
|
146
|
+
return self._to_dataframe(payload)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""
|
4
|
+
fin68.clients.eod.sector (stub)
|
5
|
+
==============================
|
6
|
+
|
7
|
+
Type stub cho lớp `SectorEod` — phục vụ autocomplete/typing trong IDE và
|
8
|
+
static type checker. Không thực thi logic, chỉ khai báo kiểu & docstring ngắn.
|
9
|
+
"""
|
10
|
+
|
11
|
+
from typing import TYPE_CHECKING, Sequence, Union
|
12
|
+
from ..base import BaseClient
|
13
|
+
from .helper import DateStr, Interval,IcbName, _EodMixin
|
14
|
+
from .icb_types import SectorArg
|
15
|
+
if TYPE_CHECKING:
|
16
|
+
from pandas import DataFrame
|
17
|
+
from private_core.http_core import HttpSession
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
class SectorEod(BaseClient, _EodMixin):
|
22
|
+
"""
|
23
|
+
Endpoint EOD cho dữ liệu ngành (ICB Sector Aggregates).
|
24
|
+
|
25
|
+
Parameters
|
26
|
+
----------
|
27
|
+
session : HttpSession
|
28
|
+
Phiên HTTP đã xác thực, truyền từ `EodClient`.
|
29
|
+
|
30
|
+
Notes
|
31
|
+
-----
|
32
|
+
- Hỗ trợ nhiều mã ngành cùng lúc (list[str]).
|
33
|
+
- Chuẩn hóa khoảng thời gian, validate `interval`.
|
34
|
+
- Trả về OHLCV trung bình/tổng hợp cho từng ngành.
|
35
|
+
"""
|
36
|
+
|
37
|
+
def __init__(self, session: "HttpSession") -> None: ...
|
38
|
+
"""
|
39
|
+
Khởi tạo `SectorEod` với session HTTP đã xác thực.
|
40
|
+
|
41
|
+
Parameters
|
42
|
+
----------
|
43
|
+
session : HttpSession
|
44
|
+
Phiên HTTP dùng chung từ `EodClient`.
|
45
|
+
"""
|
46
|
+
|
47
|
+
def ohlcv(
|
48
|
+
self,
|
49
|
+
symbol: SectorArg,
|
50
|
+
start: DateStr = None,
|
51
|
+
end: DateStr = None,
|
52
|
+
interval: Interval = "1D",
|
53
|
+
) -> "DataFrame": ...
|
54
|
+
"""
|
55
|
+
Lấy dữ liệu OHLCV (Open, High, Low, Close, Volume) cho một hoặc nhiều ngành ICB.
|
56
|
+
|
57
|
+
Parameters
|
58
|
+
----------
|
59
|
+
symbol : str or Sequence[str]
|
60
|
+
Mã ngành hoặc danh sách mã ngành (VD: "ICB01010", ["ICB01010", "ICB03020"]).
|
61
|
+
start : str, optional
|
62
|
+
Ngày bắt đầu "YYYY-MM-DD". Nếu None, dùng mặc định hệ thống.
|
63
|
+
end : str, optional
|
64
|
+
Ngày kết thúc "YYYY-MM-DD". Nếu None, mặc định là hôm nay.
|
65
|
+
interval : {"1D", "1W", "1M", "3M", "6M", "1Y"}, default "1D"
|
66
|
+
Khoảng thời gian lấy dữ liệu.
|
67
|
+
|
68
|
+
Returns
|
69
|
+
-------
|
70
|
+
DataFrame
|
71
|
+
Gồm các cột cơ bản: `icbCode`, `Date`, `Open`, `High`, `Low`, `Close`, `Volume`.
|
72
|
+
|
73
|
+
Raises
|
74
|
+
------
|
75
|
+
ValueError
|
76
|
+
Nếu `start > end` hoặc `interval` không hợp lệ.
|
77
|
+
ApiRequestError
|
78
|
+
Nếu backend trả lỗi khi gọi API.
|
79
|
+
|
80
|
+
Examples
|
81
|
+
--------
|
82
|
+
>>> from fin68 import client
|
83
|
+
>>> cli = client(api_key="sk_live_...")
|
84
|
+
>>> cli.eod.sector.ohlcv("ICB01010", start="2024-01-01", end="2024-06-30")
|
85
|
+
>>> cli.eod.sector.ohlcv(["ICB01010", "ICB03020"])
|
86
|
+
"""
|
@@ -0,0 +1,134 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""
|
4
|
+
fin68.clients.eod.stock
|
5
|
+
=======================
|
6
|
+
|
7
|
+
Cung cấp lớp `StockEod` — tập trung các endpoint EOD dành cho từng mã cổ phiếu riêng lẻ.
|
8
|
+
Bao gồm dữ liệu OHLCV (Open, High, Low, Close, Volume), điều chỉnh giá (adjusted) và
|
9
|
+
hỗ trợ các khoảng thời gian khác nhau (ngày, tuần, tháng...).
|
10
|
+
|
11
|
+
Notes
|
12
|
+
-----
|
13
|
+
- Dữ liệu lấy từ `private_core.eod_core.fetch_ohlcv()`.
|
14
|
+
- Tất cả kết quả được trả về dưới dạng `pandas.DataFrame`.
|
15
|
+
- Tự động kiểm tra và chuẩn hóa ngày bắt đầu/kết thúc, validate interval.
|
16
|
+
"""
|
17
|
+
|
18
|
+
from typing import TYPE_CHECKING, Sequence, Union
|
19
|
+
|
20
|
+
from private_core import eod_core
|
21
|
+
from ..base import BaseClient
|
22
|
+
from .helper import DateStr, Interval, _EodMixin
|
23
|
+
|
24
|
+
if TYPE_CHECKING:
|
25
|
+
from pandas import DataFrame
|
26
|
+
from private_core.http_core import HttpSession
|
27
|
+
|
28
|
+
SymbolArg = Union[str, Sequence[str]]
|
29
|
+
|
30
|
+
|
31
|
+
class StockEod(BaseClient, _EodMixin):
|
32
|
+
"""
|
33
|
+
Cung cấp các endpoint EOD cho từng mã cổ phiếu (equity symbol).
|
34
|
+
|
35
|
+
Lớp này cho phép truy vấn dữ liệu giá, khối lượng và chỉ báo kỹ thuật
|
36
|
+
ở cấp mã cổ phiếu, tương tự như chức năng `get_price_data()` trong các
|
37
|
+
hệ thống tài chính chuyên nghiệp.
|
38
|
+
|
39
|
+
Parameters
|
40
|
+
----------
|
41
|
+
session : HttpSession
|
42
|
+
Phiên HTTP đã xác thực, được truyền từ `EodClient`.
|
43
|
+
|
44
|
+
Notes
|
45
|
+
-----
|
46
|
+
- Toàn bộ phương thức đều hỗ trợ truy vấn nhiều mã cùng lúc (`list[str]`).
|
47
|
+
- Có thể chọn chế độ `adjusted=True` để lấy dữ liệu điều chỉnh cổ tức/chia tách.
|
48
|
+
- Hàm `_EodMixin` cung cấp các helper cho việc chuẩn hóa khoảng thời gian
|
49
|
+
(`_normalize_range`) và kiểm tra tính hợp lệ của `interval`.
|
50
|
+
"""
|
51
|
+
|
52
|
+
def __init__(self, session: "HttpSession") -> None:
|
53
|
+
"""
|
54
|
+
Khởi tạo lớp `StockEod` với session HTTP đã được xác thực.
|
55
|
+
|
56
|
+
Parameters
|
57
|
+
----------
|
58
|
+
session : HttpSession
|
59
|
+
Phiên HTTP dùng chung từ `EodClient`.
|
60
|
+
"""
|
61
|
+
super().__init__(session=session)
|
62
|
+
|
63
|
+
def ohlcv(
|
64
|
+
self,
|
65
|
+
symbol: SymbolArg,
|
66
|
+
start: DateStr = None,
|
67
|
+
end: DateStr = None,
|
68
|
+
adjusted: bool = True,
|
69
|
+
interval: Interval = "1D",
|
70
|
+
) -> "DataFrame":
|
71
|
+
"""
|
72
|
+
Lấy dữ liệu OHLCV (Open, High, Low, Close, Volume) cho một hoặc nhiều mã cổ phiếu.
|
73
|
+
|
74
|
+
Parameters
|
75
|
+
----------
|
76
|
+
symbol : str or Sequence[str]
|
77
|
+
Mã cổ phiếu hoặc danh sách mã cổ phiếu cần lấy dữ liệu (VD: `"HPG"`, `["HPG", "VCB"]`).
|
78
|
+
start : str, optional
|
79
|
+
Ngày bắt đầu, định dạng `"YYYY-MM-DD"`.
|
80
|
+
Nếu không truyền, hệ thống sẽ tự động lấy ngược về theo khoảng mặc định.
|
81
|
+
end : str, optional
|
82
|
+
Ngày kết thúc, định dạng `"YYYY-MM-DD"`.
|
83
|
+
Nếu không truyền, mặc định là ngày hiện tại.
|
84
|
+
adjusted : bool, default True
|
85
|
+
Nếu `True`, trả về giá đã điều chỉnh theo cổ tức và chia tách cổ phiếu.
|
86
|
+
interval : {"1D", "1W", "1M", "3M", "6M", "1Y"}, default "1D"
|
87
|
+
Khoảng thời gian dữ liệu cần lấy:
|
88
|
+
- `"1D"`: theo ngày
|
89
|
+
- `"1W"`: theo tuần
|
90
|
+
- `"1M"`: theo tháng
|
91
|
+
- `"3M"`, `"6M"`, `"1Y"`: theo quý, nửa năm, hoặc năm
|
92
|
+
|
93
|
+
Returns
|
94
|
+
-------
|
95
|
+
DataFrame
|
96
|
+
Bảng dữ liệu gồm các cột:
|
97
|
+
- `symbol`: mã cổ phiếu
|
98
|
+
- `Date`: ngày giao dịch
|
99
|
+
- `Open`, `High`, `Low`, `Close`, `Volume`
|
100
|
+
- Các cột bổ sung nếu backend cung cấp (ví dụ: `Adj Close`)
|
101
|
+
|
102
|
+
Raises
|
103
|
+
------
|
104
|
+
ValueError
|
105
|
+
Khi tham số `start` > `end`, hoặc `interval` không hợp lệ.
|
106
|
+
ApiRequestError
|
107
|
+
Khi backend trả lỗi trong quá trình gọi API.
|
108
|
+
|
109
|
+
Examples
|
110
|
+
--------
|
111
|
+
>>> from fin68 import client
|
112
|
+
>>> cli = client(api_key="sk_live_...")
|
113
|
+
>>> df = cli.eod.stock.ohlcv("HPG", start="2024-01-01", end="2024-06-30")
|
114
|
+
>>> df.head()
|
115
|
+
symbol Date Open High Low Close Volume
|
116
|
+
0 HPG 2024-01-02 27200 27500 26900 27400 15300000
|
117
|
+
|
118
|
+
Notes
|
119
|
+
-----
|
120
|
+
- Tự động validate khoảng thời gian (`start <= end`).
|
121
|
+
- Có thể truy vấn nhiều mã cùng lúc để so sánh hiệu suất.
|
122
|
+
- Hàm này là wrapper của `private_core.eod_core.fetch_ohlcv`.
|
123
|
+
"""
|
124
|
+
start_date, end_date = self._normalize_range(start, end)
|
125
|
+
interval_value = self._validate_interval(interval)
|
126
|
+
payload = eod_core.fetch_ohlcv(
|
127
|
+
self.session,
|
128
|
+
symbol,
|
129
|
+
start=start_date,
|
130
|
+
end=end_date,
|
131
|
+
adjusted=adjusted,
|
132
|
+
interval=interval_value,
|
133
|
+
)
|
134
|
+
return self._to_dataframe(payload)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""
|
4
|
+
fin68.clients.eod.stock (stub)
|
5
|
+
==============================
|
6
|
+
|
7
|
+
Type stub cho lớp `StockEod` — phục vụ autocomplete/typing trong IDE (VSCode/PyCharm)
|
8
|
+
và static type checker (mypy/pyright). Không thực thi logic.
|
9
|
+
|
10
|
+
Ghi chú
|
11
|
+
-------
|
12
|
+
- Docstring giữ chuẩn NumPy style như bản .py.
|
13
|
+
- Các thân hàm được thay bằng `...` (ellipsis) theo PEP 484.
|
14
|
+
"""
|
15
|
+
|
16
|
+
from typing import TYPE_CHECKING, Sequence, Union
|
17
|
+
from ..base import BaseClient
|
18
|
+
from .helper import DateStr, Interval, _EodMixin
|
19
|
+
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from pandas import DataFrame
|
22
|
+
from private_core.http_core import HttpSession
|
23
|
+
|
24
|
+
SymbolArg = Union[str, Sequence[str]]
|
25
|
+
|
26
|
+
class StockEod(BaseClient, _EodMixin):
|
27
|
+
"""
|
28
|
+
Cung cấp các endpoint EOD cho từng mã cổ phiếu (equity symbol).
|
29
|
+
|
30
|
+
Parameters
|
31
|
+
----------
|
32
|
+
session : HttpSession
|
33
|
+
Phiên HTTP đã xác thực, được truyền từ `EodClient`.
|
34
|
+
|
35
|
+
Notes
|
36
|
+
-----
|
37
|
+
- Hỗ trợ truy vấn nhiều mã cùng lúc (`list[str]`).
|
38
|
+
- Có `adjusted=True` để lấy giá đã điều chỉnh cổ tức/chia tách.
|
39
|
+
- Kế thừa `_EodMixin` để chuẩn hóa khoảng thời gian và validate interval.
|
40
|
+
"""
|
41
|
+
|
42
|
+
def __init__(self, session: "HttpSession") -> None: ...
|
43
|
+
"""
|
44
|
+
Khởi tạo `StockEod` với session đã xác thực.
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
session : HttpSession
|
49
|
+
Phiên HTTP dùng chung từ `EodClient`.
|
50
|
+
"""
|
51
|
+
|
52
|
+
def ohlcv(
|
53
|
+
self,
|
54
|
+
symbol: SymbolArg,
|
55
|
+
start: DateStr = None,
|
56
|
+
end: DateStr = None,
|
57
|
+
interval: Interval = "1D",
|
58
|
+
) -> "DataFrame": ...
|
59
|
+
"""
|
60
|
+
Lấy dữ liệu OHLCV (Open, High, Low, Close, Volume) cho một hoặc nhiều mã cổ phiếu.
|
61
|
+
|
62
|
+
Parameters
|
63
|
+
----------
|
64
|
+
symbol : str or Sequence[str]
|
65
|
+
Mã cổ phiếu hoặc danh sách mã (VD: "HPG", ["HPG", "VCB"]).
|
66
|
+
start : str, optional
|
67
|
+
Ngày bắt đầu, định dạng "YYYY-MM-DD". Nếu `None`, dùng mặc định hệ thống.
|
68
|
+
end : str, optional
|
69
|
+
Ngày kết thúc, định dạng "YYYY-MM-DD". Nếu `None`, mặc định là hôm nay.
|
70
|
+
interval : {"1D", "1W", "1M", "3M", "6M", "1Y"}, default "1D"
|
71
|
+
Khoảng thời gian lấy dữ liệu.
|
72
|
+
|
73
|
+
Returns
|
74
|
+
-------
|
75
|
+
DataFrame
|
76
|
+
Gồm các cột cơ bản: `symbol`, `Date`, `Open`, `High`, `Low`, `Close`, `Volume`.
|
77
|
+
|
78
|
+
|
79
|
+
Raises
|
80
|
+
------
|
81
|
+
ValueError
|
82
|
+
Nếu `start > end` hoặc `interval` không hợp lệ.
|
83
|
+
ApiRequestError
|
84
|
+
Nếu backend trả lỗi khi gọi API.
|
85
|
+
|
86
|
+
Examples
|
87
|
+
--------
|
88
|
+
>>> from fin68 import client
|
89
|
+
>>> cli = client(api_key="sk_live_...")
|
90
|
+
>>> df = cli.eod.stock.ohlcv("HPG", start="2024-01-01", end="2024-06-30")
|
91
|
+
>>> df.head()
|
92
|
+
"""
|
@@ -0,0 +1,65 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from datetime import date, datetime, timedelta
|
4
|
+
from typing import ClassVar, Iterable, Literal, Optional, TypeAlias,Union,Sequence
|
5
|
+
|
6
|
+
import pandas as pd
|
7
|
+
|
8
|
+
from ...utils import _validate_date_eod
|
9
|
+
|
10
|
+
DateStr: TypeAlias = Optional[str]
|
11
|
+
Interval = Literal["1D", "1W", "1M", "3M", "6M", "1Y"]
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
class _EodMixin:
|
16
|
+
"""Common helpers shared across EOD namespaces."""
|
17
|
+
|
18
|
+
_DEFAULT_LOOKBACK_DAYS: ClassVar[int] = 30
|
19
|
+
_ALLOWED_INTERVALS: ClassVar[set[str]] = {"1D", "1W", "1M", "3M", "6M", "1Y"}
|
20
|
+
|
21
|
+
def _normalize_range(self, start: DateStr, end: DateStr) -> tuple[str, str]:
|
22
|
+
"""Return a validated (start, end) date pair in ISO format."""
|
23
|
+
today = date.today()
|
24
|
+
|
25
|
+
if end is None:
|
26
|
+
end_date = today
|
27
|
+
else:
|
28
|
+
_validate_date_eod(end, "end")
|
29
|
+
end_date = datetime.strptime(end, "%Y-%m-%d").date()
|
30
|
+
|
31
|
+
if start is None:
|
32
|
+
start_date = end_date - timedelta(days=self._DEFAULT_LOOKBACK_DAYS)
|
33
|
+
else:
|
34
|
+
_validate_date_eod(start, "start")
|
35
|
+
start_date = datetime.strptime(start, "%Y-%m-%d").date()
|
36
|
+
|
37
|
+
if start_date > end_date:
|
38
|
+
raise ValueError(
|
39
|
+
f"start date ({start_date:%Y-%m-%d}) cannot be after end date ({end_date:%Y-%m-%d})."
|
40
|
+
)
|
41
|
+
|
42
|
+
return start_date.strftime("%Y-%m-%d"), end_date.strftime("%Y-%m-%d")
|
43
|
+
|
44
|
+
def _validate_interval(self, interval: Interval) -> Interval:
|
45
|
+
"""Ensure the provided interval value is supported by the backend."""
|
46
|
+
if interval not in self._ALLOWED_INTERVALS:
|
47
|
+
allowed = ", ".join(sorted(self._ALLOWED_INTERVALS))
|
48
|
+
raise ValueError(f"interval must be one of {{{allowed}}}, received {interval!r}.")
|
49
|
+
return interval
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
def _to_dataframe(rows: Iterable[dict]) -> pd.DataFrame:
|
53
|
+
"""Materialise raw rows into a sorted pandas.DataFrame."""
|
54
|
+
frame = pd.DataFrame(rows)
|
55
|
+
if frame.empty:
|
56
|
+
return frame
|
57
|
+
|
58
|
+
if "date" in frame.columns:
|
59
|
+
frame["date"] = pd.to_datetime(frame["date"], errors="coerce")
|
60
|
+
|
61
|
+
sort_cols = [column for column in ("symbol", "date") if column in frame.columns]
|
62
|
+
if sort_cols:
|
63
|
+
frame = frame.sort_values(sort_cols).reset_index(drop=True)
|
64
|
+
|
65
|
+
return frame
|
@@ -0,0 +1,111 @@
|
|
1
|
+
from typing import ClassVar, Iterable, Literal, Optional, TypeAlias,Union,Sequence
|
2
|
+
icbMap={'xây dựng': '2357',
|
3
|
+
'vật liệu xây dựng & nội thất': '2353',
|
4
|
+
'đường sắt': '2775',
|
5
|
+
'kho bãi, hậu cần và bảo dưỡng': '2777',
|
6
|
+
'dịch vụ vận tải': '2779',
|
7
|
+
'tư vấn & hỗ trợ kd': '2791',
|
8
|
+
'đào tạo & việc làm': '2793',
|
9
|
+
'công nghiệp hàng không': '2713',
|
10
|
+
'quốc phòng': '2717',
|
11
|
+
'containers & đóng gói': '2723',
|
12
|
+
'công nghiệp phức hợp': '2727',
|
13
|
+
'hàng điện & điện tử': '2733',
|
14
|
+
'thiết bị điện': '2737',
|
15
|
+
'xe tải & đóng tàu': '2753',
|
16
|
+
'máy công nghiệp': '2757',
|
17
|
+
'chuyển phát nhanh': '2771',
|
18
|
+
'vận tải thủy': '2773',
|
19
|
+
'nhựa, cao su & sợi': '1353',
|
20
|
+
'sản phẩm hóa dầu, nông dược & hóa chất khác': '1357',
|
21
|
+
'lâm sản và chế biến gỗ': '1733',
|
22
|
+
'sản xuất giấy': '1737',
|
23
|
+
'nhôm': '1753',
|
24
|
+
'kim loại màu': '1755',
|
25
|
+
'thép và sản phẩm thép': '1757',
|
26
|
+
'khai thác than': '1771',
|
27
|
+
'đá quý và kim cương': '1773',
|
28
|
+
'khai khoáng': '1775',
|
29
|
+
'khai thác vàng': '1777',
|
30
|
+
'bạch kim & kim loại quý khác': '1779',
|
31
|
+
'quản lý tài chính': '2795',
|
32
|
+
'chất thải & môi trường': '2799',
|
33
|
+
'sản xuất ô tô': '3353',
|
34
|
+
'phụ tùng ô tô': '3355',
|
35
|
+
'lốp xe': '3357',
|
36
|
+
'sản xuất và khai thác dầu khí': '0533',
|
37
|
+
'tổ hợp dầu khí': '0537',
|
38
|
+
'thiết bị và dịch vụ dầu khí': '0573',
|
39
|
+
'ống dẫn dầu': '0577',
|
40
|
+
'thiết bị năng lượng tái chế': '0583',
|
41
|
+
'nhiên liệu thay thế': '0587',
|
42
|
+
'nhà cung cấp thiết bị': '2797',
|
43
|
+
'sản xuất bia': '3533',
|
44
|
+
'vang & rượu mạnh': '3535',
|
45
|
+
'đồ uống & giải khát': '3537',
|
46
|
+
'nuôi trồng nông & hải sản': '3573',
|
47
|
+
'thực phẩm': '3577',
|
48
|
+
'đồ gia dụng lâu bền': '3722',
|
49
|
+
'đồ gia dụng một lần': '3724',
|
50
|
+
'thiết bị gia dụng': '3726',
|
51
|
+
'xây nhà': '3728',
|
52
|
+
'điện tử tiêu dùng': '3743',
|
53
|
+
'sản phẩm nghệ thuật': '3745',
|
54
|
+
'đồ chơi': '3747',
|
55
|
+
'hàng may mặc': '3763',
|
56
|
+
'giầy dép': '3765',
|
57
|
+
'hàng cá nhân': '3767',
|
58
|
+
'thuốc lá': '3785',
|
59
|
+
'chăm sóc y tế': '4533',
|
60
|
+
'thiết bị y tế': '4535',
|
61
|
+
'dụng cụ y tế': '4537',
|
62
|
+
'công nghệ sinh học': '4573',
|
63
|
+
'dược phẩm': '4577',
|
64
|
+
'phân phối dược phẩm': '5333',
|
65
|
+
'phân phối thực phẩm': '5337',
|
66
|
+
'giải trí & truyền thông': '5553',
|
67
|
+
'dịch vụ truyền thông': '5555',
|
68
|
+
'sách, ấn bản & sản phẩm văn hóa': '5557',
|
69
|
+
'nhà hàng và quán bar': '5757',
|
70
|
+
'vận tải hành khách & du lịch': '5759',
|
71
|
+
'viễn thông cố định': '6535',
|
72
|
+
'môi giới bảo hiểm': '8534',
|
73
|
+
'bảo hiểm phi nhân thọ': '8536',
|
74
|
+
'tái bảo hiểm': '8538',
|
75
|
+
'bảo hiểm nhân thọ': '8575',
|
76
|
+
'bán lẻ hàng may mặc': '5371',
|
77
|
+
'bán lẻ phức hợp': '5373',
|
78
|
+
'phân phối nội thất': '5375',
|
79
|
+
'dịch vụ tiêu dùng chuyên ngành': '5377',
|
80
|
+
'phân phối hàng chuyên dụng': '5379',
|
81
|
+
'dịch vụ hàng không': '5751',
|
82
|
+
'gambling': '5752',
|
83
|
+
'khách sạn': '5753',
|
84
|
+
'dịch vụ giải trí': '5755',
|
85
|
+
'viễn thông di động': '6575',
|
86
|
+
'ngân hàng': '8355',
|
87
|
+
'bảo hiểm phức hợp': '8532',
|
88
|
+
'sản xuất & phân phối điện': '7535',
|
89
|
+
'phân phối xăng dầu & khí đốt': '7573',
|
90
|
+
'tiện ích khác': '7575',
|
91
|
+
'nước': '7577',
|
92
|
+
'bất động sản': '8633',
|
93
|
+
'phần mềm': '9537',
|
94
|
+
'phần cứng': '9572',
|
95
|
+
'thiết bị văn phòng': '9574',
|
96
|
+
'bán dẫn': '9576',
|
97
|
+
'thiết bị viễn thông': '9578',
|
98
|
+
'tư vấn, định giá, môi giới bất động sản': '8637',
|
99
|
+
'quỹ ủy thác bđs': '8677',
|
100
|
+
'quản lý tài sản': '8771',
|
101
|
+
'tài chính cá nhân': '8773',
|
102
|
+
'tài chính đặc biệt': '8775',
|
103
|
+
'môi giới chứng khoán': '8777',
|
104
|
+
'cầm cố': '8779',
|
105
|
+
'quỹ đầu tư': '8985',
|
106
|
+
'dịch vụ máy tính': '9533',
|
107
|
+
'internet': '9535'}
|
108
|
+
SectorArg = Union[
|
109
|
+
Literal['Xây dựng', 'Vật liệu xây dựng & Nội thất', 'Đường sắt', 'Kho bãi, hậu cần và bảo dưỡng', 'Dịch vụ vận tải', 'Tư vấn & Hỗ trợ KD', 'Đào tạo & Việc làm', 'Công nghiệp hàng không', 'Quốc phòng', 'Containers & Đóng gói', 'Công nghiệp phức hợp', 'Hàng điện & điện tử', 'Thiết bị điện', 'Xe tải & Đóng tàu', 'Máy công nghiệp', 'Chuyển phát nhanh', 'Vận tải Thủy', 'Nhựa, cao su & sợi', 'Sản phẩm hóa dầu, Nông dược & Hóa chất khác', 'Lâm sản và Chế biến gỗ', 'Sản xuất giấy', 'Nhôm', 'Kim Loại màu', 'Thép và sản phẩm thép', 'Khai thác Than', 'Đá quý và Kim cương', 'Khai khoáng', 'Khai thác vàng', 'Bạch kim & Kim loại quý khác', 'Quản lý Tài chính', 'Chất thải & Môi trường', 'Sản xuất ô tô', 'Phụ tùng ô tô', 'Lốp xe', 'Sản xuất và Khai thác dầu khí', 'Tổ hợp Dầu khí', 'Thiết bị và Dịch vụ Dầu khí', 'Ống dẫn Dầu', 'Thiết bị năng lượng tái chế', 'Nhiên liệu thay thế', 'Nhà cung cấp thiết bị', 'Sản xuất bia', 'Vang & Rượu mạnh', 'Đồ uống & giải khát', 'Nuôi trồng nông & hải sản', 'Thực phẩm', 'Đồ gia dụng lâu bền', 'Đồ gia dụng một lần', 'Thiết bị gia dụng', 'Xây nhà', 'Điện tử tiêu dùng', 'Sản phẩm nghệ thuật', 'Đồ chơi', 'Hàng May mặc', 'Giầy dép', 'Hàng cá nhân', 'Thuốc lá', 'Chăm sóc y tế', 'Thiết bị y tế', 'Dụng cụ y tế', 'Công nghệ sinh học', 'Dược phẩm', 'Phân phối dược phẩm', 'Phân phối thực phẩm', 'Giải trí & Truyền thông', 'Dịch vụ truyền thông', 'Sách, ấn bản & sản phẩm văn hóa', 'Nhà hàng và quán bar', 'Vận tải hành khách & Du lịch', 'Viễn thông cố định', 'Môi giới bảo hiểm', 'Bảo hiểm phi nhân thọ', 'Tái bảo hiểm', 'Bảo hiểm nhân thọ', 'Bán lẻ hàng may mặc', 'Bán lẻ phức hợp', 'Phân phối nội thất', 'Dịch vụ tiêu dùng chuyên ngành', 'Phân phối hàng chuyên dụng', 'Dịch vụ hàng không', 'Gambling', 'Khách sạn', 'Dịch vụ giải trí', 'Viễn thông di động', 'Ngân hàng', 'Bảo hiểm phức hợp', 'Sản xuất & Phân phối Điện', 'Phân phối xăng dầu & khí đốt', 'Tiện ích khác', 'Nước', 'Bất động sản', 'Phần mềm', 'Phần cứng', 'Thiết bị văn phòng', 'Bán dẫn', 'Thiết bị viễn thông', 'Tư Vấn, Định giá, Môi giới Bất động sản', 'Quỹ ủy thác BĐS', 'Quản lý tài sản', 'Tài chính cá nhân', 'Tài chính đặc biệt', 'Môi giới chứng khoán', 'Cầm cố', 'Quỹ đầu tư', 'Dịch vụ Máy tính', 'Internet'],
|
110
|
+
Sequence[
|
111
|
+
Literal['Xây dựng', 'Vật liệu xây dựng & Nội thất', 'Đường sắt', 'Kho bãi, hậu cần và bảo dưỡng', 'Dịch vụ vận tải', 'Tư vấn & Hỗ trợ KD', 'Đào tạo & Việc làm', 'Công nghiệp hàng không', 'Quốc phòng', 'Containers & Đóng gói', 'Công nghiệp phức hợp', 'Hàng điện & điện tử', 'Thiết bị điện', 'Xe tải & Đóng tàu', 'Máy công nghiệp', 'Chuyển phát nhanh', 'Vận tải Thủy', 'Nhựa, cao su & sợi', 'Sản phẩm hóa dầu, Nông dược & Hóa chất khác', 'Lâm sản và Chế biến gỗ', 'Sản xuất giấy', 'Nhôm', 'Kim Loại màu', 'Thép và sản phẩm thép', 'Khai thác Than', 'Đá quý và Kim cương', 'Khai khoáng', 'Khai thác vàng', 'Bạch kim & Kim loại quý khác', 'Quản lý Tài chính', 'Chất thải & Môi trường', 'Sản xuất ô tô', 'Phụ tùng ô tô', 'Lốp xe', 'Sản xuất và Khai thác dầu khí', 'Tổ hợp Dầu khí', 'Thiết bị và Dịch vụ Dầu khí', 'Ống dẫn Dầu', 'Thiết bị năng lượng tái chế', 'Nhiên liệu thay thế', 'Nhà cung cấp thiết bị', 'Sản xuất bia', 'Vang & Rượu mạnh', 'Đồ uống & giải khát', 'Nuôi trồng nông & hải sản', 'Thực phẩm', 'Đồ gia dụng lâu bền', 'Đồ gia dụng một lần', 'Thiết bị gia dụng', 'Xây nhà', 'Điện tử tiêu dùng', 'Sản phẩm nghệ thuật', 'Đồ chơi', 'Hàng May mặc', 'Giầy dép', 'Hàng cá nhân', 'Thuốc lá', 'Chăm sóc y tế', 'Thiết bị y tế', 'Dụng cụ y tế', 'Công nghệ sinh học', 'Dược phẩm', 'Phân phối dược phẩm', 'Phân phối thực phẩm', 'Giải trí & Truyền thông', 'Dịch vụ truyền thông', 'Sách, ấn bản & sản phẩm văn hóa', 'Nhà hàng và quán bar', 'Vận tải hành khách & Du lịch', 'Viễn thông cố định', 'Môi giới bảo hiểm', 'Bảo hiểm phi nhân thọ', 'Tái bảo hiểm', 'Bảo hiểm nhân thọ', 'Bán lẻ hàng may mặc', 'Bán lẻ phức hợp', 'Phân phối nội thất', 'Dịch vụ tiêu dùng chuyên ngành', 'Phân phối hàng chuyên dụng', 'Dịch vụ hàng không', 'Gambling', 'Khách sạn', 'Dịch vụ giải trí', 'Viễn thông di động', 'Ngân hàng', 'Bảo hiểm phức hợp', 'Sản xuất & Phân phối Điện', 'Phân phối xăng dầu & khí đốt', 'Tiện ích khác', 'Nước', 'Bất động sản', 'Phần mềm', 'Phần cứng', 'Thiết bị văn phòng', 'Bán dẫn', 'Thiết bị viễn thông', 'Tư Vấn, Định giá, Môi giới Bất động sản', 'Quỹ ủy thác BĐS', 'Quản lý tài sản', 'Tài chính cá nhân', 'Tài chính đặc biệt', 'Môi giới chứng khoán', 'Cầm cố', 'Quỹ đầu tư', 'Dịch vụ Máy tính', 'Internet']]]
|