litequant 3.0.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.
- litequant/LiteQuantClient.py +884 -0
- litequant/ParquetManager.py +1379 -0
- litequant/__init__.py +54 -0
- litequant/exceptions.py +168 -0
- litequant/log.py +24 -0
- litequant-3.0.0.dist-info/LICENSE +21 -0
- litequant-3.0.0.dist-info/METADATA +139 -0
- litequant-3.0.0.dist-info/RECORD +10 -0
- litequant-3.0.0.dist-info/WHEEL +5 -0
- litequant-3.0.0.dist-info/top_level.txt +1 -0
litequant/__init__.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
# LiteQuant Client SDK
|
|
4
|
+
from .LiteQuantClient import DataTicket, LiteQuantClient
|
|
5
|
+
|
|
6
|
+
from .ParquetManager import ParquetDataManager
|
|
7
|
+
from .exceptions import (
|
|
8
|
+
LiteQuantError,
|
|
9
|
+
AuthError,
|
|
10
|
+
HostSelectionError,
|
|
11
|
+
RemoteDataError,
|
|
12
|
+
SerializationError,
|
|
13
|
+
LocalStorageError,
|
|
14
|
+
CategoryError,
|
|
15
|
+
CategoryNotFoundError,
|
|
16
|
+
InvalidCategoryError,
|
|
17
|
+
MetaError,
|
|
18
|
+
ProtocolError,
|
|
19
|
+
TicketExpiredError,
|
|
20
|
+
QuotaExceededError,
|
|
21
|
+
PermissionDeniedError,
|
|
22
|
+
NetworkError,
|
|
23
|
+
DataConnectionError,
|
|
24
|
+
ServiceUnavailableError,
|
|
25
|
+
SyncError,
|
|
26
|
+
APIError,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__version__ = "3.0.0"
|
|
30
|
+
__all__ = [
|
|
31
|
+
# Client
|
|
32
|
+
"LiteQuantClient",
|
|
33
|
+
"DataTicket",
|
|
34
|
+
"ParquetDataManager",
|
|
35
|
+
"LiteQuantError",
|
|
36
|
+
"AuthError",
|
|
37
|
+
"HostSelectionError",
|
|
38
|
+
"RemoteDataError",
|
|
39
|
+
"SerializationError",
|
|
40
|
+
"LocalStorageError",
|
|
41
|
+
"CategoryError",
|
|
42
|
+
"CategoryNotFoundError",
|
|
43
|
+
"InvalidCategoryError",
|
|
44
|
+
"MetaError",
|
|
45
|
+
"ProtocolError",
|
|
46
|
+
"TicketExpiredError",
|
|
47
|
+
"QuotaExceededError",
|
|
48
|
+
"PermissionDeniedError",
|
|
49
|
+
"NetworkError",
|
|
50
|
+
"DataConnectionError",
|
|
51
|
+
"ServiceUnavailableError",
|
|
52
|
+
"SyncError",
|
|
53
|
+
"APIError",
|
|
54
|
+
]
|
litequant/exceptions.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class LiteQuantError(Exception):
|
|
5
|
+
"""Base SDK error with a public, privacy-safe message."""
|
|
6
|
+
|
|
7
|
+
code = "SERVICE_UNAVAILABLE"
|
|
8
|
+
user_message = "服务暂时不可用,请稍后重试"
|
|
9
|
+
retryable = False
|
|
10
|
+
|
|
11
|
+
def __init__(self, message=None, *, code=None, user_message=None, retryable=None, detail=None):
|
|
12
|
+
# Keep the public exception object privacy-safe. Internal implementation
|
|
13
|
+
# errors must not be attached here because users may print or inspect it.
|
|
14
|
+
self.detail = None
|
|
15
|
+
self.code = code or self.code
|
|
16
|
+
self.user_message = user_message or self.user_message
|
|
17
|
+
self.retryable = self.retryable if retryable is None else retryable
|
|
18
|
+
super().__init__(self.user_message)
|
|
19
|
+
|
|
20
|
+
def __str__(self):
|
|
21
|
+
return self.user_message
|
|
22
|
+
|
|
23
|
+
def __repr__(self):
|
|
24
|
+
return f"{self.__class__.__name__}(code={self.code!r}, user_message={self.user_message!r})"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AuthError(LiteQuantError):
|
|
28
|
+
"""Authentication failed."""
|
|
29
|
+
|
|
30
|
+
code = "AUTH_INVALID"
|
|
31
|
+
user_message = "API 凭证无效或已过期,请检查后重试"
|
|
32
|
+
retryable = False
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class HostSelectionError(LiteQuantError):
|
|
36
|
+
"""Host selection failed."""
|
|
37
|
+
|
|
38
|
+
code = "SERVICE_UNAVAILABLE"
|
|
39
|
+
user_message = "服务暂时不可用,请稍后重试"
|
|
40
|
+
retryable = True
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class NetworkError(LiteQuantError):
|
|
44
|
+
"""Network request failed."""
|
|
45
|
+
|
|
46
|
+
code = "CONNECTION_INTERRUPTED"
|
|
47
|
+
user_message = "连接中断,请检查网络后重试"
|
|
48
|
+
retryable = True
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DataConnectionError(LiteQuantError):
|
|
52
|
+
"""Data connection failed."""
|
|
53
|
+
|
|
54
|
+
code = "CONNECTION_INTERRUPTED"
|
|
55
|
+
user_message = "连接中断,请重新初始化客户端"
|
|
56
|
+
retryable = True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class RemoteDataError(LiteQuantError):
|
|
60
|
+
"""Remote data read failed."""
|
|
61
|
+
|
|
62
|
+
code = "DATA_UNAVAILABLE"
|
|
63
|
+
user_message = "数据暂时不可用,请稍后重试"
|
|
64
|
+
retryable = True
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class SerializationError(LiteQuantError):
|
|
68
|
+
"""Serialization or validation failed."""
|
|
69
|
+
|
|
70
|
+
code = "DATA_VERIFY_FAILED"
|
|
71
|
+
user_message = "数据校验失败,请重新同步"
|
|
72
|
+
retryable = True
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class LocalStorageError(LiteQuantError):
|
|
76
|
+
"""Local storage failed."""
|
|
77
|
+
|
|
78
|
+
code = "DATA_UNAVAILABLE"
|
|
79
|
+
user_message = "本地数据保存失败,请检查存储路径"
|
|
80
|
+
retryable = False
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CategoryError(LiteQuantError):
|
|
84
|
+
"""Category related error."""
|
|
85
|
+
|
|
86
|
+
code = "DATA_UNAVAILABLE"
|
|
87
|
+
user_message = "数据类别不可用"
|
|
88
|
+
retryable = False
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class CategoryNotFoundError(CategoryError):
|
|
92
|
+
"""Category does not exist."""
|
|
93
|
+
|
|
94
|
+
code = "DATA_UNAVAILABLE"
|
|
95
|
+
user_message = "数据类别不存在或当前账号无权访问"
|
|
96
|
+
retryable = False
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class InvalidCategoryError(CategoryError):
|
|
100
|
+
"""Category name is invalid."""
|
|
101
|
+
|
|
102
|
+
code = "REQUEST_INVALID"
|
|
103
|
+
user_message = "数据类别名称无效"
|
|
104
|
+
retryable = False
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class MetaError(LiteQuantError):
|
|
108
|
+
"""Metadata error."""
|
|
109
|
+
|
|
110
|
+
code = "DATA_UNAVAILABLE"
|
|
111
|
+
user_message = "数据暂时不可用,请稍后重试"
|
|
112
|
+
retryable = True
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class ProtocolError(LiteQuantError):
|
|
116
|
+
"""Protocol mismatch."""
|
|
117
|
+
|
|
118
|
+
code = "SERVICE_UNAVAILABLE"
|
|
119
|
+
user_message = "服务暂时不可用,请升级客户端或稍后重试"
|
|
120
|
+
retryable = False
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class TicketExpiredError(LiteQuantError):
|
|
124
|
+
"""Connection credential expired."""
|
|
125
|
+
|
|
126
|
+
code = "CONNECTION_INTERRUPTED"
|
|
127
|
+
user_message = "连接已中断,请重新初始化客户端"
|
|
128
|
+
retryable = True
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class QuotaExceededError(LiteQuantError):
|
|
132
|
+
"""Quota or connection limit exceeded."""
|
|
133
|
+
|
|
134
|
+
code = "CONNECTION_LIMIT"
|
|
135
|
+
user_message = "连接数已达上限,请关闭其他连接后重试"
|
|
136
|
+
retryable = True
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class PermissionDeniedError(LiteQuantError):
|
|
140
|
+
"""Permission denied."""
|
|
141
|
+
|
|
142
|
+
code = "PERMISSION_DENIED"
|
|
143
|
+
user_message = "权限不足:当前账号没有该数据权限"
|
|
144
|
+
retryable = False
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class ServiceUnavailableError(LiteQuantError):
|
|
148
|
+
"""Service unavailable."""
|
|
149
|
+
|
|
150
|
+
code = "SERVICE_UNAVAILABLE"
|
|
151
|
+
user_message = "服务暂时不可用,请稍后重试"
|
|
152
|
+
retryable = True
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class SyncError(LiteQuantError):
|
|
156
|
+
"""One or more data sync tasks failed."""
|
|
157
|
+
|
|
158
|
+
code = "DATA_UNAVAILABLE"
|
|
159
|
+
user_message = "部分数据更新失败,请稍后重试"
|
|
160
|
+
retryable = True
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class APIError(LiteQuantError):
|
|
164
|
+
"""Generic API call failed."""
|
|
165
|
+
|
|
166
|
+
code = "SERVICE_UNAVAILABLE"
|
|
167
|
+
user_message = "服务暂时不可用,请稍后重试"
|
|
168
|
+
retryable = True
|
litequant/log.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def GetLogger(name: str = "litequant", level: int = logging.INFO) -> logging.Logger:
|
|
8
|
+
logger = logging.getLogger(name)
|
|
9
|
+
|
|
10
|
+
if logger.handlers:
|
|
11
|
+
logger.setLevel(level)
|
|
12
|
+
return logger
|
|
13
|
+
|
|
14
|
+
logger.setLevel(level)
|
|
15
|
+
logger.propagate = False
|
|
16
|
+
|
|
17
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
18
|
+
formatter = logging.Formatter(
|
|
19
|
+
fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s",
|
|
20
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
21
|
+
)
|
|
22
|
+
handler.setFormatter(formatter)
|
|
23
|
+
logger.addHandler(handler)
|
|
24
|
+
return logger
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LiteQuant
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: litequant
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: LiteQuant Python client SDK
|
|
5
|
+
Author: LiteQuant Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://litequant.com
|
|
8
|
+
Project-URL: Documentation, https://litequant.com/docs
|
|
9
|
+
Project-URL: Issues, https://litequant.com/support
|
|
10
|
+
Keywords: quant,finance,data,parquet,pandas
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: pandas>=1.3.0
|
|
28
|
+
Requires-Dist: pyarrow>=10.0.0
|
|
29
|
+
Requires-Dist: redis>=4.0.0
|
|
30
|
+
Requires-Dist: requests>=2.25.0
|
|
31
|
+
Requires-Dist: tqdm>=4.60.0
|
|
32
|
+
Provides-Extra: test
|
|
33
|
+
Requires-Dist: pytest<8.4,>=7.0; extra == "test"
|
|
34
|
+
Provides-Extra: release
|
|
35
|
+
Requires-Dist: build>=1.0; extra == "release"
|
|
36
|
+
Requires-Dist: twine>=5.0; extra == "release"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest<8.4,>=7.0; extra == "dev"
|
|
39
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
40
|
+
Requires-Dist: twine>=5.0; extra == "dev"
|
|
41
|
+
|
|
42
|
+
# LiteQuant Python SDK
|
|
43
|
+
|
|
44
|
+
LiteQuant Python SDK 用于在本地下载和读取 LiteQuant 数据。SDK 会自动处理连接、同步、本地 Parquet 缓存和常见错误提示。
|
|
45
|
+
|
|
46
|
+
支持 Python 3.8 及以上版本。
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install litequant
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from litequant import LiteQuantClient
|
|
58
|
+
|
|
59
|
+
client = LiteQuantClient(
|
|
60
|
+
api_token="your_api_token",
|
|
61
|
+
save_path="./litequant_data",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
client.UpdateAllCategory(update_method="incremental")
|
|
65
|
+
df = client.GetCategory("cn_stock_pivot#open")
|
|
66
|
+
print(df.tail())
|
|
67
|
+
|
|
68
|
+
client.close()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
推荐使用上下文管理器自动关闭连接:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from litequant import LiteQuantClient
|
|
75
|
+
|
|
76
|
+
with LiteQuantClient(api_token="your_api_token", save_path="./litequant_data") as client:
|
|
77
|
+
categories = client.ListCategories()
|
|
78
|
+
df = client.GetCategory(categories[0])
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
默认 API 地址为 `https://www.litequant.pro`。如需连接测试环境,可以传入 `api_url` 或设置环境变量 `LITEQUANT_API_URL`。
|
|
82
|
+
|
|
83
|
+
## Error Handling
|
|
84
|
+
|
|
85
|
+
SDK 默认会在 Python 终端输出功能面错误提示,同时抛出类型化异常。错误信息不会展示后端细节。
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from litequant import LiteQuantClient, LiteQuantError
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
with LiteQuantClient(api_token="your_api_token", save_path="./litequant_data") as client:
|
|
92
|
+
df = client.GetCategory("cn_stock_pivot#open")
|
|
93
|
+
except LiteQuantError as exc:
|
|
94
|
+
print(exc.code)
|
|
95
|
+
print(exc.user_message)
|
|
96
|
+
print(exc.retryable)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
如果你希望完全自己处理错误提示,可以关闭终端输出:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
client = LiteQuantClient(
|
|
103
|
+
api_token="your_api_token",
|
|
104
|
+
save_path="./litequant_data",
|
|
105
|
+
display_errors=False,
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
常见公开错误码:
|
|
110
|
+
|
|
111
|
+
- `AUTH_INVALID`:API 凭证无效或已过期
|
|
112
|
+
- `ACCOUNT_UNAVAILABLE`:账号不可用
|
|
113
|
+
- `SUBSCRIPTION_UNAVAILABLE`:套餐不可用或已过期
|
|
114
|
+
- `PERMISSION_DENIED`:权限不足
|
|
115
|
+
- `CONNECTION_LIMIT`:连接数已达上限
|
|
116
|
+
- `CONNECTION_INTERRUPTED`:连接已中断,请重新连接
|
|
117
|
+
- `REQUEST_INVALID`:请求参数无效
|
|
118
|
+
- `SERVICE_UNAVAILABLE`:服务暂时不可用
|
|
119
|
+
- `DATA_UNAVAILABLE`:数据暂时不可用
|
|
120
|
+
- `DATA_VERIFY_FAILED`:数据校验失败
|
|
121
|
+
|
|
122
|
+
## Data Cache
|
|
123
|
+
|
|
124
|
+
SDK 会把数据保存为本地 Parquet 缓存。再次读取同一数据类别时,会优先使用本地缓存;如果本地没有该数据类别,SDK 会自动同步。
|
|
125
|
+
|
|
126
|
+
当前 pivot 数据支持 `monthly` 和 `daily` 两种分区模式。后续可以在服务端元信息和客户端缓存层一起扩展更多分区模式。
|
|
127
|
+
|
|
128
|
+
## Metadata Contract
|
|
129
|
+
|
|
130
|
+
数据服务会提供客户端可识别的类别列表、类别元信息和分区元信息。客户端会自动读取这些元信息并完成数据同步。
|
|
131
|
+
|
|
132
|
+
公开稳定字段包括:
|
|
133
|
+
|
|
134
|
+
- `partition_mode`:pivot 分区模式,目前支持 `monthly` 和 `daily`
|
|
135
|
+
- `partition_keys`:数据分区标识列表
|
|
136
|
+
- `hash`:数据校验值
|
|
137
|
+
- `key_columns` / `dedupe_keys`:unstack 数据去重键
|
|
138
|
+
|
|
139
|
+
普通用户通常不需要直接使用这些字段。
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
litequant/LiteQuantClient.py,sha256=6rob4I0a8sohfoeG68xwoQnXbUECwmaA-eoE10KtZRc,33397
|
|
2
|
+
litequant/ParquetManager.py,sha256=eYoq-veV8DH7RjZHaTBxHWhAJ-B4UV6dnrGIoSIfRuM,55694
|
|
3
|
+
litequant/__init__.py,sha256=7TkrmgWx97ndsIdz8cvy_rcpoiBmgONzBgRfxY64Ca8,1208
|
|
4
|
+
litequant/exceptions.py,sha256=p93kxzaPvP_vQ_p9XsITxzOwANcLUbRbgyJayPa9T84,4428
|
|
5
|
+
litequant/log.py,sha256=dRE9oXTyyNoGBpU67Suaz4mITzZXtuRpkp188qEeUWQ,612
|
|
6
|
+
litequant-3.0.0.dist-info/LICENSE,sha256=v4ndfeoLXVjQaijygqdq6QPTg9iS3RKofG1EKNRhUZg,1066
|
|
7
|
+
litequant-3.0.0.dist-info/METADATA,sha256=MDirJTDKALBKEol1co6Pqz4teX3BOn7kBEYRwQ-QvHg,4687
|
|
8
|
+
litequant-3.0.0.dist-info/WHEEL,sha256=beeZ86-EfXScwlR_HKu4SllMC9wUEj_8Z_4FJ3egI2w,91
|
|
9
|
+
litequant-3.0.0.dist-info/top_level.txt,sha256=ktSowtaruI9jxc1qPpgwBQjR0UvDgDEf15s92HqCzLw,10
|
|
10
|
+
litequant-3.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
litequant
|