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
fin68/__init__.py
ADDED
@@ -0,0 +1,323 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""
|
4
|
+
Fin68 Python Client
|
5
|
+
===================
|
6
|
+
|
7
|
+
Client cấp cao giúp xác thực API Key, hiển thị thông báo từ backend và cung cấp
|
8
|
+
các domain client như :class:`EodClient`. Thiết kế theo pattern "facade" để người
|
9
|
+
dùng khởi tạo một lần và làm việc qua thuộc tính `client.eod`, v.v.
|
10
|
+
|
11
|
+
Notes
|
12
|
+
-----
|
13
|
+
- Sử dụng `private_core.HttpSession` để gắn `base_url`, `timeout` và `user-agent`.
|
14
|
+
- Tự động gọi `validate_api_key` khi khởi tạo để nhận thông tin phiên bản, cảnh báo
|
15
|
+
và meta của API key.
|
16
|
+
- Tất cả cảnh báo/nhắc nâng cấp được hiển thị qua `warnings.warn` hoặc `logging`.
|
17
|
+
|
18
|
+
See Also
|
19
|
+
--------
|
20
|
+
EodClient
|
21
|
+
Domain client cho dữ liệu EOD.
|
22
|
+
"""
|
23
|
+
|
24
|
+
import logging
|
25
|
+
import warnings
|
26
|
+
from typing import Optional
|
27
|
+
|
28
|
+
from private_core.auth_core import validate_api_key
|
29
|
+
from private_core.http_core import BASE_URL, DEFAULT_TIMEOUT, HttpSession
|
30
|
+
|
31
|
+
from ._version import __version__
|
32
|
+
from .clients import EodClient
|
33
|
+
from .exceptions import ApiKeyValidationError, ConfigurationError
|
34
|
+
from .types import ApiKeyMeta, ApiKeyValidationResponse, BackendMessage, MessageType
|
35
|
+
|
36
|
+
logger = logging.getLogger(__name__)
|
37
|
+
|
38
|
+
|
39
|
+
class Fin68Client:
|
40
|
+
"""
|
41
|
+
Điểm truy cập chính tổng hợp các domain client của Fin68.
|
42
|
+
|
43
|
+
Khi khởi tạo, client sẽ:
|
44
|
+
1) Tạo `HttpSession` với API key.
|
45
|
+
2) Gọi `validate_api_key` để xác thực và lấy thông tin phiên bản.
|
46
|
+
3) Hiển thị các thông báo (nâng cấp/báo lỗi/cảnh báo) từ backend tới người dùng.
|
47
|
+
4) Khởi tạo các domain client (hiện tại: `eod`).
|
48
|
+
|
49
|
+
Parameters
|
50
|
+
----------
|
51
|
+
api_key : str
|
52
|
+
API key hợp lệ do Fin68 phát hành.
|
53
|
+
extra_context : dict, optional
|
54
|
+
Ngữ cảnh bổ sung gửi lên backend khi validate (VD: môi trường chạy, app_name).
|
55
|
+
|
56
|
+
Attributes
|
57
|
+
----------
|
58
|
+
eod : EodClient
|
59
|
+
Domain client cho dữ liệu EOD (OHLCV, technical, ...).
|
60
|
+
_session : HttpSession
|
61
|
+
Phiên HTTP dùng chung cho toàn bộ domain client.
|
62
|
+
_api_key_meta : ApiKeyMeta or None
|
63
|
+
Thông tin meta của API key sau khi xác thực.
|
64
|
+
_messages : list[BackendMessage]
|
65
|
+
Danh sách thông điệp backend trả về sau validate.
|
66
|
+
|
67
|
+
Raises
|
68
|
+
------
|
69
|
+
ConfigurationError
|
70
|
+
Khi `api_key` rỗng/không được cung cấp.
|
71
|
+
ApiKeyValidationError
|
72
|
+
Khi xác thực API key thất bại (khóa sai/hết hạn/hệ thống từ chối).
|
73
|
+
|
74
|
+
Examples
|
75
|
+
--------
|
76
|
+
Khởi tạo và dùng như context manager:
|
77
|
+
|
78
|
+
>>> from fin68 import client
|
79
|
+
>>> with client(api_key="sk_live_...") as cli:
|
80
|
+
... df = cli.eod.ohlcv("HPG", start="2024-01-01", end="2024-12-31")
|
81
|
+
|
82
|
+
Hoặc quản lý vòng đời thủ công:
|
83
|
+
|
84
|
+
>>> cli = client(api_key="sk_live_...")
|
85
|
+
>>> try:
|
86
|
+
... meta = cli.api_key_metadata
|
87
|
+
... data = cli.eod.ohlcv("VCB")
|
88
|
+
... finally:
|
89
|
+
... cli.close()
|
90
|
+
"""
|
91
|
+
|
92
|
+
def __init__(
|
93
|
+
self,
|
94
|
+
api_key: str,
|
95
|
+
*,
|
96
|
+
extra_context: Optional[dict] = None,
|
97
|
+
) -> None:
|
98
|
+
"""
|
99
|
+
Khởi tạo client và xác thực API key ngay lập tức.
|
100
|
+
|
101
|
+
Parameters
|
102
|
+
----------
|
103
|
+
api_key : str
|
104
|
+
API key hợp lệ.
|
105
|
+
extra_context : dict, optional
|
106
|
+
Context bổ sung gửi kèm khi validate.
|
107
|
+
|
108
|
+
Raises
|
109
|
+
------
|
110
|
+
ConfigurationError
|
111
|
+
Khi không cung cấp API key.
|
112
|
+
ApiKeyValidationError
|
113
|
+
Khi backend từ chối API key (hết hạn, không hợp lệ, ...).
|
114
|
+
"""
|
115
|
+
if not api_key:
|
116
|
+
raise ConfigurationError("Cần cung cấp API key để khởi tạo fin68.client()")
|
117
|
+
|
118
|
+
self._session = HttpSession(
|
119
|
+
api_key,
|
120
|
+
base_url=BASE_URL,
|
121
|
+
timeout=DEFAULT_TIMEOUT,
|
122
|
+
version=__version__,
|
123
|
+
)
|
124
|
+
self._api_key_meta: Optional[ApiKeyMeta] = None
|
125
|
+
self._messages: list[BackendMessage] = []
|
126
|
+
|
127
|
+
try:
|
128
|
+
validation = validate_api_key(
|
129
|
+
self._session,
|
130
|
+
client_version=__version__,
|
131
|
+
include_messages=True,
|
132
|
+
extra_context=extra_context,
|
133
|
+
)
|
134
|
+
except ApiKeyValidationError:
|
135
|
+
# Đảm bảo đóng session nếu validate thất bại
|
136
|
+
self._session.close()
|
137
|
+
raise
|
138
|
+
self._api_key_meta = validation.meta
|
139
|
+
self._messages = list(validation.iter_messages())
|
140
|
+
|
141
|
+
self._surface_messages(validation)
|
142
|
+
|
143
|
+
# Domain clients
|
144
|
+
self.eod = EodClient(self._session)
|
145
|
+
|
146
|
+
def _surface_messages(self, validation: ApiKeyValidationResponse) -> None:
|
147
|
+
"""
|
148
|
+
Hiển thị các thông điệp từ backend (cảnh báo, lỗi, nhắc nâng cấp).
|
149
|
+
|
150
|
+
Parameters
|
151
|
+
----------
|
152
|
+
validation : ApiKeyValidationResponse
|
153
|
+
Kết quả validate từ backend, bao gồm thông tin version và danh sách messages.
|
154
|
+
|
155
|
+
Notes
|
156
|
+
-----
|
157
|
+
- Các thông điệp loại `WARNING`, `ERROR`, `NOTIFY_API_KEY_EXPIRING/EXPIRED`
|
158
|
+
sẽ được hiển thị qua `warnings.warn(...)`.
|
159
|
+
- Thông báo nâng cấp (khi client cũ hơn phiên bản tối thiểu/khuyến nghị)
|
160
|
+
cũng được cảnh báo bằng `warnings.warn(...)` để người dùng sớm cập nhật.
|
161
|
+
"""
|
162
|
+
version_info = validation.version
|
163
|
+
for message in self._messages:
|
164
|
+
if message.is_upgrade_notice() and version_info.requires_upgrade():
|
165
|
+
_emit_upgrade_notice(message, version_info.backend_version)
|
166
|
+
elif message.type in {
|
167
|
+
MessageType.NOTIFY_API_KEY_EXPIRING,
|
168
|
+
MessageType.NOTIFY_API_KEY_EXPIRED,
|
169
|
+
MessageType.WARNING,
|
170
|
+
MessageType.ERROR,
|
171
|
+
}:
|
172
|
+
warnings.warn(message.message, stacklevel=2)
|
173
|
+
else:
|
174
|
+
logger.info("Thông báo từ Fin68: %s", message.message)
|
175
|
+
|
176
|
+
if version_info.requires_upgrade() and not any(m.is_upgrade_notice() for m in self._messages):
|
177
|
+
warnings.warn(
|
178
|
+
(
|
179
|
+
f"Đã có phiên bản fin68 mới hơn ({version_info.backend_version}). "
|
180
|
+
"Hãy cập nhật để sử dụng các tính năng và bản sửa lỗi mới nhất."
|
181
|
+
),
|
182
|
+
stacklevel=2,
|
183
|
+
)
|
184
|
+
|
185
|
+
if version_info.is_out_of_support():
|
186
|
+
warnings.warn(
|
187
|
+
(
|
188
|
+
f"Phiên bản fin68 hiện tại ({version_info.client_version}) "
|
189
|
+
f"đã thấp hơn mức tối thiểu được hỗ trợ ({version_info.minimum_supported_version}). "
|
190
|
+
"Một số tính năng có thể không còn hoạt động ổn định."
|
191
|
+
),
|
192
|
+
stacklevel=2,
|
193
|
+
)
|
194
|
+
|
195
|
+
@property
|
196
|
+
def api_key_metadata(self) -> Optional[ApiKeyMeta]:
|
197
|
+
"""
|
198
|
+
Trả về meta của API key sau khi xác thực.
|
199
|
+
|
200
|
+
Returns
|
201
|
+
-------
|
202
|
+
ApiKeyMeta or None
|
203
|
+
Meta của API key (ngày hết hạn, trạng thái, ...), hoặc `None` nếu không sẵn có.
|
204
|
+
"""
|
205
|
+
return self._api_key_meta
|
206
|
+
|
207
|
+
@property
|
208
|
+
def messages(self) -> list[BackendMessage]:
|
209
|
+
"""
|
210
|
+
Danh sách thông điệp backend trả về khi validate.
|
211
|
+
|
212
|
+
Returns
|
213
|
+
-------
|
214
|
+
list[BackendMessage]
|
215
|
+
Bản sao danh sách thông điệp để tránh chỉnh sửa ngoài ý muốn.
|
216
|
+
"""
|
217
|
+
return list(self._messages)
|
218
|
+
|
219
|
+
def close(self) -> None:
|
220
|
+
"""
|
221
|
+
Đóng phiên HTTP bên dưới và giải phóng tài nguyên.
|
222
|
+
|
223
|
+
Notes
|
224
|
+
-----
|
225
|
+
- Nên gọi trong `finally` hoặc dùng context manager để đảm bảo đóng phiên.
|
226
|
+
"""
|
227
|
+
self._session.close()
|
228
|
+
|
229
|
+
def __enter__(self) -> "Fin68Client":
|
230
|
+
"""
|
231
|
+
Vào ngữ cảnh `with`.
|
232
|
+
|
233
|
+
Returns
|
234
|
+
-------
|
235
|
+
Fin68Client
|
236
|
+
Chính instance hiện tại.
|
237
|
+
"""
|
238
|
+
return self
|
239
|
+
|
240
|
+
def __exit__(self, exc_type, exc, tb) -> None:
|
241
|
+
"""
|
242
|
+
Thoát ngữ cảnh `with` và tự động đóng phiên HTTP.
|
243
|
+
|
244
|
+
Parameters
|
245
|
+
----------
|
246
|
+
exc_type, exc, tb
|
247
|
+
Thông tin ngoại lệ (nếu có). Không can thiệp vào flow ngoại lệ.
|
248
|
+
"""
|
249
|
+
self.close()
|
250
|
+
|
251
|
+
|
252
|
+
def _emit_upgrade_notice(message: BackendMessage, latest_version: Optional[str]) -> None:
|
253
|
+
"""
|
254
|
+
Hiển thị cảnh báo nâng cấp phiên bản client.
|
255
|
+
|
256
|
+
Parameters
|
257
|
+
----------
|
258
|
+
message : BackendMessage
|
259
|
+
Thông điệp nâng cấp từ backend.
|
260
|
+
latest_version : str or None
|
261
|
+
Phiên bản mới nhất phía backend biết đến (nếu có).
|
262
|
+
|
263
|
+
Notes
|
264
|
+
-----
|
265
|
+
- Sử dụng `warnings.warn(..., stacklevel=3)` để đẩy cảnh báo lên callsite người dùng.
|
266
|
+
"""
|
267
|
+
suffix = f" Phiên bản mới nhất: {latest_version}" if latest_version else ""
|
268
|
+
warnings.warn(f"{message.message}{suffix}", stacklevel=3)
|
269
|
+
|
270
|
+
|
271
|
+
def client(
|
272
|
+
apiKey: Optional[str] = None,
|
273
|
+
*,
|
274
|
+
api_key: Optional[str] = None,
|
275
|
+
extra_context: Optional[dict] = None,
|
276
|
+
) -> Fin68Client:
|
277
|
+
"""
|
278
|
+
Factory tạo ra một :class:`Fin68Client` đã sẵn sàng sử dụng.
|
279
|
+
|
280
|
+
Hỗ trợ cả hai tên tham số `api_key` (chuẩn Python) và `apiKey` (thân thiện
|
281
|
+
với người dùng/JS). Nếu truyền cả hai, `api_key` sẽ được ưu tiên.
|
282
|
+
|
283
|
+
Parameters
|
284
|
+
----------
|
285
|
+
apiKey : str, optional
|
286
|
+
API key. Chỉ dùng khi không truyền `api_key`.
|
287
|
+
api_key : str, optional
|
288
|
+
API key chuẩn (ưu tiên nếu được truyền).
|
289
|
+
extra_context : dict, optional
|
290
|
+
Ngữ cảnh bổ sung gửi lên backend khi validate.
|
291
|
+
|
292
|
+
Returns
|
293
|
+
-------
|
294
|
+
Fin68Client
|
295
|
+
Client đã xác thực xong, kèm các domain client như `eod`.
|
296
|
+
|
297
|
+
Raises
|
298
|
+
------
|
299
|
+
ConfigurationError
|
300
|
+
Khi không cung cấp `api_key`/`apiKey`.
|
301
|
+
ApiKeyValidationError
|
302
|
+
Khi backend từ chối API key trong quá trình khởi tạo.
|
303
|
+
|
304
|
+
Examples
|
305
|
+
--------
|
306
|
+
>>> from fin68 import client
|
307
|
+
>>> cli = client(api_key="sk_live_...")
|
308
|
+
>>> try:
|
309
|
+
... df = cli.eod.ohlcv("HPG", start="2024-01-01", end="2024-06-30")
|
310
|
+
... finally:
|
311
|
+
... cli.close()
|
312
|
+
"""
|
313
|
+
key = api_key or apiKey
|
314
|
+
if not key:
|
315
|
+
raise ConfigurationError("Cần truyền tham số apiKey hoặc api_key khi khởi tạo fin68.client()")
|
316
|
+
|
317
|
+
return Fin68Client(
|
318
|
+
key,
|
319
|
+
extra_context=extra_context,
|
320
|
+
)
|
321
|
+
|
322
|
+
|
323
|
+
__all__ = ["Fin68Client", "client", "__version__"]
|
fin68/__init__.pyi
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
from typing import Optional, List, Dict
|
2
|
+
from .clients import EodClient
|
3
|
+
from .types import ApiKeyMeta, BackendMessage
|
4
|
+
|
5
|
+
__version__: str
|
6
|
+
|
7
|
+
class Fin68Client:
|
8
|
+
"""
|
9
|
+
Client chính của thư viện Fin68 — chịu trách nhiệm xác thực API key,
|
10
|
+
khởi tạo phiên làm việc HTTP, và cung cấp các domain client như `eod`.
|
11
|
+
"""
|
12
|
+
|
13
|
+
eod: EodClient
|
14
|
+
"""
|
15
|
+
Domain client cho dữ liệu EOD (End-of-Day).
|
16
|
+
|
17
|
+
Cung cấp ba namespace chính:
|
18
|
+
- **stock** (`StockEod`) → Dữ liệu EOD cấp cổ phiếu, bao gồm:
|
19
|
+
- OHLCV (Open, High, Low, Close, Volume)
|
20
|
+
- Các chỉ báo kỹ thuật (MA, RSI, MACD, Bollinger, v.v.)
|
21
|
+
- Thống kê biến động và hiệu suất theo mã
|
22
|
+
|
23
|
+
- **market** (`MarketEod`) → Dữ liệu chỉ số thị trường (VNINDEX, HNXINDEX, UPCOMINDEX):
|
24
|
+
- Giá đóng cửa, khối lượng, vốn hóa, PE/PB toàn thị trường
|
25
|
+
- So sánh diễn biến giữa các sàn
|
26
|
+
|
27
|
+
- **sector** (`SectorEod`) → Dữ liệu EOD cấp ngành (ICB, VNSector...):
|
28
|
+
- Hiệu suất ngành, thay đổi vốn hóa, tỷ trọng thanh khoản
|
29
|
+
- So sánh ngành/tiểu ngành theo thời gian
|
30
|
+
|
31
|
+
Examples
|
32
|
+
--------
|
33
|
+
>>> from fin68 import client
|
34
|
+
>>> cli = client(api_key="sk_live_...")
|
35
|
+
>>> cli.eod.stock.ohlcv("HPG", start="2024-01-01", end="2024-06-30")
|
36
|
+
>>> cli.eod.market.ohlcv("VNINDEX", start="2024-01-01", end="2024-06-30")
|
37
|
+
>>> cli.eod.sector.ohlcv("Ngân hàng", start="2024-01-01", end="2024-06-30")
|
38
|
+
|
39
|
+
|
40
|
+
"""
|
41
|
+
|
42
|
+
_api_key_meta: Optional[ApiKeyMeta]
|
43
|
+
"""Thông tin meta của API key sau khi xác thực."""
|
44
|
+
|
45
|
+
_messages: List[BackendMessage]
|
46
|
+
"""Danh sách thông điệp backend trả về sau validate."""
|
47
|
+
|
48
|
+
def __init__(
|
49
|
+
self,
|
50
|
+
api_key: str,
|
51
|
+
*,
|
52
|
+
extra_context: Optional[Dict] = None,
|
53
|
+
) -> None: ...
|
54
|
+
"""Khởi tạo Fin68Client và tự động xác thực API key."""
|
55
|
+
|
56
|
+
@property
|
57
|
+
def api_key_metadata(self) -> Optional[ApiKeyMeta]: ...
|
58
|
+
"""Trả về meta của API key sau khi xác thực."""
|
59
|
+
|
60
|
+
@property
|
61
|
+
def messages(self) -> List[BackendMessage]: ...
|
62
|
+
"""Danh sách thông điệp backend trả về khi validate."""
|
63
|
+
|
64
|
+
def close(self) -> None: ...
|
65
|
+
"""Đóng session HTTP và giải phóng tài nguyên."""
|
66
|
+
|
67
|
+
def __enter__(self) -> "Fin68Client": ...
|
68
|
+
"""Vào context manager (`with`)."""
|
69
|
+
|
70
|
+
def __exit__(self, exc_type, exc, tb) -> None: ...
|
71
|
+
"""Thoát context manager (`with`) và tự động đóng session."""
|
72
|
+
|
73
|
+
def client(
|
74
|
+
apiKey: Optional[str] = None,
|
75
|
+
*,
|
76
|
+
api_key: Optional[str] = None,
|
77
|
+
extra_context: Optional[Dict] = None,
|
78
|
+
) -> Fin68Client: ...
|
79
|
+
"""Factory khởi tạo và trả về một Fin68Client đã sẵn sàng sử dụng."""
|
80
|
+
|
81
|
+
__all__: list[str]
|
fin68/_core/__init__.py
ADDED
File without changes
|
fin68/_version.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
try:
|
4
|
+
from importlib.metadata import PackageNotFoundError, version
|
5
|
+
except ImportError: # pragma: no cover - Python <3.8
|
6
|
+
from importlib_metadata import PackageNotFoundError, version # type: ignore
|
7
|
+
|
8
|
+
|
9
|
+
def _resolve_version() -> str:
|
10
|
+
try:
|
11
|
+
return version("fin68")
|
12
|
+
except PackageNotFoundError:
|
13
|
+
return "0.0.0"
|
14
|
+
|
15
|
+
|
16
|
+
__all__ = ["__version__"]
|
17
|
+
__version__ = _resolve_version()
|
fin68/auth/__init__.py
ADDED
File without changes
|
fin68/auth/api_key.py
ADDED
File without changes
|
fin68/clients/base.py
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from private_core.http_core import HttpSession
|
7
|
+
|
8
|
+
|
9
|
+
class BaseClient:
|
10
|
+
"""Base class for all Fin68 client modules providing shared HTTP session access."""
|
11
|
+
|
12
|
+
def __init__(self, session: "HttpSession") -> None:
|
13
|
+
self._session = session
|
14
|
+
|
15
|
+
@property
|
16
|
+
def session(self) -> "HttpSession":
|
17
|
+
return self._session
|
@@ -0,0 +1,142 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""
|
4
|
+
fin68.clients.eod.market
|
5
|
+
========================
|
6
|
+
|
7
|
+
Cung cấp lớp `MarketEod` — tập hợp các endpoint EOD (End-of-Day)
|
8
|
+
dành cho **chỉ số thị trường** như VNINDEX, VN30, HNX, HNX30 và UPCOM.
|
9
|
+
|
10
|
+
Notes
|
11
|
+
-----
|
12
|
+
- Dữ liệu được lấy từ `private_core.eod_core.fetch_ohlcv()`.
|
13
|
+
- Tất cả kết quả được trả về dưới dạng `pandas.DataFrame`.
|
14
|
+
- Có thể truy vấn nhiều chỉ số cùng lúc.
|
15
|
+
|
16
|
+
"""
|
17
|
+
|
18
|
+
from typing import TYPE_CHECKING, Sequence, Union, Literal
|
19
|
+
from private_core import eod_core
|
20
|
+
from ..base import BaseClient
|
21
|
+
from .helper import DateStr, Interval, _EodMixin
|
22
|
+
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
from pandas import DataFrame
|
25
|
+
from private_core.http_core import HttpSession
|
26
|
+
|
27
|
+
IndexArg = Union[
|
28
|
+
Literal["VNINDEX", "VN30", "HNX", "HNX30", "UPCOM"],
|
29
|
+
Sequence[Literal["VNINDEX", "VN30", "HNX", "HNX30", "UPCOM"]],
|
30
|
+
]
|
31
|
+
INDEXS = {"VNINDEX", "VN30", "HNX", "HNX30", "UPCOM"}
|
32
|
+
|
33
|
+
class MarketEod(BaseClient, _EodMixin):
|
34
|
+
"""
|
35
|
+
Cung cấp các endpoint EOD cho các chỉ số thị trường (Market Indices).
|
36
|
+
|
37
|
+
Bao gồm các chỉ số phổ biến:
|
38
|
+
- **VNINDEX**: chỉ số toàn thị trường HOSE
|
39
|
+
- **VN30**: nhóm 30 cổ phiếu vốn hóa lớn trên HOSE
|
40
|
+
- **HNX**, **HNX30**: chỉ số sàn Hà Nội
|
41
|
+
- **UPCOM**: thị trường UPCOM
|
42
|
+
|
43
|
+
Parameters
|
44
|
+
----------
|
45
|
+
session : HttpSession
|
46
|
+
Phiên HTTP đã xác thực, được truyền từ `EodClient`.
|
47
|
+
|
48
|
+
Notes
|
49
|
+
-----
|
50
|
+
- Hỗ trợ nhiều chỉ số trong cùng một lệnh truy vấn (`list[str]`).
|
51
|
+
- Tự động kiểm tra tính hợp lệ của khoảng thời gian (`start <= end`).
|
52
|
+
- Có thể lấy dữ liệu theo nhiều `interval` (ngày, tuần, tháng, v.v.).
|
53
|
+
- Trả về dữ liệu OHLCV (Open, High, Low, Close, Volume) của chỉ số.
|
54
|
+
"""
|
55
|
+
|
56
|
+
def __init__(self, session: "HttpSession") -> None:
|
57
|
+
"""
|
58
|
+
Khởi tạo `MarketEod` với session HTTP đã xác thực.
|
59
|
+
|
60
|
+
Parameters
|
61
|
+
----------
|
62
|
+
session : HttpSession
|
63
|
+
Phiên HTTP được chia sẻ từ `EodClient`.
|
64
|
+
"""
|
65
|
+
super().__init__(session=session)
|
66
|
+
|
67
|
+
def ohlcv(
|
68
|
+
self,
|
69
|
+
symbol: IndexArg,
|
70
|
+
start: DateStr = None,
|
71
|
+
end: DateStr = None,
|
72
|
+
interval: Interval = "1D",
|
73
|
+
) -> "DataFrame":
|
74
|
+
"""
|
75
|
+
Lấy dữ liệu OHLCV (Open, High, Low, Close, Volume) cho một hoặc nhiều chỉ số thị trường.
|
76
|
+
|
77
|
+
Parameters
|
78
|
+
----------
|
79
|
+
symbol : {"VNINDEX", "VN30", "HNX", "HNX30", "UPCOM"} or Sequence
|
80
|
+
Chỉ số hoặc danh sách chỉ số cần lấy dữ liệu.
|
81
|
+
start : str, optional
|
82
|
+
Ngày bắt đầu, định dạng `"YYYY-MM-DD"`.
|
83
|
+
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).
|
84
|
+
end : str, optional
|
85
|
+
Ngày kết thúc, định dạng `"YYYY-MM-DD"`.
|
86
|
+
Nếu không truyền, mặc định là ngày hiện tại.
|
87
|
+
interval : {"1D", "1W", "1M", "3M", "6M", "1Y"}, default "1D"
|
88
|
+
Khoảng thời gian dữ liệu:
|
89
|
+
- `"1D"`: theo ngày
|
90
|
+
- `"1W"`: theo tuần
|
91
|
+
- `"1M"`: theo tháng
|
92
|
+
- `"3M"`, `"6M"`, `"1Y"`: theo quý, nửa năm hoặc năm
|
93
|
+
|
94
|
+
Returns
|
95
|
+
-------
|
96
|
+
DataFrame
|
97
|
+
Bảng dữ liệu OHLCV, gồm các cột:
|
98
|
+
- `symbol`: tên chỉ số
|
99
|
+
- `Date`: ngày giao dịch
|
100
|
+
- `Open`, `High`, `Low`, `Close`, `Volume`
|
101
|
+
|
102
|
+
Raises
|
103
|
+
------
|
104
|
+
ValueError
|
105
|
+
Nếu ngày bắt đầu > ngày kết thúc hoặc interval không hợp lệ.
|
106
|
+
ApiRequestError
|
107
|
+
Nếu 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.market.ohlcv("VNINDEX", start="2024-01-01", end="2024-06-30")
|
114
|
+
>>> df.head()
|
115
|
+
symbol Date Open High Low Close Volume
|
116
|
+
0 VNINDEX 2024-01-02 1120.0 1125.6 1110.2 1119.4 612000000
|
117
|
+
|
118
|
+
>>> # Lấy đồng thời nhiều chỉ số
|
119
|
+
>>> cli.eod.market.ohlcv(["VNINDEX", "VN30", "HNX"])
|
120
|
+
|
121
|
+
Notes
|
122
|
+
-----
|
123
|
+
- Có thể dùng để so sánh biến động giữa các chỉ số thị trường.
|
124
|
+
- Hàm này là wrapper của `private_core.eod_core.fetch_ohlcv`.
|
125
|
+
"""
|
126
|
+
if not isinstance(symbol, list):
|
127
|
+
if symbol.upper() not in INDEXS:
|
128
|
+
raise ValueError(f"Chỉ số '{symbol}' không hợp lệ. Vui lòng chọn trong {INDEXS}")
|
129
|
+
else:
|
130
|
+
for idx in symbol:
|
131
|
+
if idx.upper() not in INDEXS:
|
132
|
+
raise ValueError(f"Chỉ số '{idx}' không hợp lệ. Vui lòng chọn trong {INDEXS}")
|
133
|
+
start_date, end_date = self._normalize_range(start, end)
|
134
|
+
interval_value = self._validate_interval(interval)
|
135
|
+
payload = eod_core.fetch_ohlcv(
|
136
|
+
self.session,
|
137
|
+
symbol,
|
138
|
+
start=start_date,
|
139
|
+
end=end_date,
|
140
|
+
interval=interval_value,
|
141
|
+
)
|
142
|
+
return self._to_dataframe(payload)
|
@@ -0,0 +1,95 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
"""
|
4
|
+
fin68.clients.eod.market (stub)
|
5
|
+
===============================
|
6
|
+
|
7
|
+
Type stub cho lớp `MarketEod` — phục vụ autocomplete/typing trong IDE và
|
8
|
+
static type checker. Không thực thi logic, chỉ khai báo kiểu.
|
9
|
+
|
10
|
+
Ghi chú
|
11
|
+
-------
|
12
|
+
- Docstring giữ phong cách NumPy, giúp IDE hiển thị tooltip rõ ràng.
|
13
|
+
- Thân hàm dùng `...` theo chuẩn stub.
|
14
|
+
"""
|
15
|
+
|
16
|
+
from typing import TYPE_CHECKING, Sequence, Union, Literal
|
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
|
+
IndexArg = Union[
|
25
|
+
Literal["VNINDEX", "VN30", "HNX", "HNX30", "UPCOM"],
|
26
|
+
Sequence[Literal["VNINDEX", "VN30", "HNX", "HNX30", "UPCOM"]],
|
27
|
+
]
|
28
|
+
|
29
|
+
|
30
|
+
class MarketEod(BaseClient, _EodMixin):
|
31
|
+
"""
|
32
|
+
Endpoint EOD cho các chỉ số thị trường (VNINDEX, VN30, HNX, HNX30, UPCOM).
|
33
|
+
|
34
|
+
Parameters
|
35
|
+
----------
|
36
|
+
session : HttpSession
|
37
|
+
Phiên HTTP đã xác thực, được truyền từ `EodClient`.
|
38
|
+
|
39
|
+
Notes
|
40
|
+
-----
|
41
|
+
- Hỗ trợ truy vấn nhiều chỉ số cùng lúc (list).
|
42
|
+
- Tự động chuẩn hóa khoảng thời gian và validate `interval`.
|
43
|
+
- Trả về dữ liệu OHLCV (Open, High, Low, Close, Volume) của chỉ số.
|
44
|
+
"""
|
45
|
+
|
46
|
+
def __init__(self, session: "HttpSession") -> None: ...
|
47
|
+
"""
|
48
|
+
Khởi tạo `MarketEod` với session HTTP đã xác thực.
|
49
|
+
|
50
|
+
Parameters
|
51
|
+
----------
|
52
|
+
session : HttpSession
|
53
|
+
Phiên HTTP chia sẻ từ `EodClient`.
|
54
|
+
"""
|
55
|
+
|
56
|
+
def ohlcv(
|
57
|
+
self,
|
58
|
+
symbol: IndexArg,
|
59
|
+
start: DateStr = None,
|
60
|
+
end: DateStr = None,
|
61
|
+
interval: Interval = "1D",
|
62
|
+
) -> "DataFrame": ...
|
63
|
+
"""
|
64
|
+
Lấy dữ liệu OHLCV cho một hoặc nhiều chỉ số thị trường.
|
65
|
+
|
66
|
+
Parameters
|
67
|
+
----------
|
68
|
+
symbol : {"VNINDEX", "VN30", "HNX", "HNX30", "UPCOM"} or Sequence
|
69
|
+
Chỉ số hoặc danh sách chỉ số cần lấy dữ liệu.
|
70
|
+
start : str, optional
|
71
|
+
Ngày bắt đầu, định dạng "YYYY-MM-DD". Nếu `None`, dùng mặc định hệ thống.
|
72
|
+
end : str, optional
|
73
|
+
Ngày kết thúc, định dạng "YYYY-MM-DD". Nếu `None`, mặc định là hôm nay.
|
74
|
+
interval : {"1D", "1W", "1M", "3M", "6M", "1Y"}, default "1D"
|
75
|
+
Khoảng thời gian lấy dữ liệu.
|
76
|
+
|
77
|
+
Returns
|
78
|
+
-------
|
79
|
+
DataFrame
|
80
|
+
Bảng dữ liệu gồm: `symbol`, `Date`, `Open`, `High`, `Low`, `Close`, `Volume`.
|
81
|
+
|
82
|
+
Raises
|
83
|
+
------
|
84
|
+
ValueError
|
85
|
+
Nếu `start > end` hoặc `interval` không hợp lệ.
|
86
|
+
ApiRequestError
|
87
|
+
Nếu backend trả lỗi khi gọi API.
|
88
|
+
|
89
|
+
Examples
|
90
|
+
--------
|
91
|
+
>>> from fin68 import client
|
92
|
+
>>> cli = client(api_key="sk_live_...")
|
93
|
+
>>> cli.eod.market.ohlcv("VNINDEX", start="2024-01-01", end="2024-06-30")
|
94
|
+
>>> cli.eod.market.ohlcv(["VNINDEX", "VN30", "HNX"])
|
95
|
+
"""
|