qdata-quote 0.2.2__tar.gz → 0.3.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.
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/PKG-INFO +23 -8
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/README.md +22 -7
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/pyproject.toml +1 -1
- qdata_quote-0.3.0/src/qdata_quote/__init__.py +4 -0
- qdata_quote-0.3.0/src/qdata_quote/stock_codes.py +74 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/uv.lock +1 -1
- qdata_quote-0.2.2/src/qdata_quote/__init__.py +0 -3
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/.gitignore +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/docs/superpowers/plans/2026-06-12-quote-service.md +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/docs/superpowers/specs/2026-06-12-quote-service-design.md +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/src/qdata_quote/bench.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/src/qdata_quote/service.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/src/qdata_quote/sources/__init__.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/src/qdata_quote/sources/base.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/src/qdata_quote/sources/sina.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/src/qdata_quote/sources/tencent.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/src/qdata_quote/types.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/tests/test_service.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/tests/test_sina.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/tests/test_tencent.py +0 -0
- {qdata_quote-0.2.2 → qdata_quote-0.3.0}/tests/test_types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qdata-quote
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: 实时行情采集服务,支持新浪和腾讯数据源
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -25,12 +25,13 @@ pip install qdata-quote
|
|
|
25
25
|
## 快速开始
|
|
26
26
|
|
|
27
27
|
```python
|
|
28
|
-
from qdata_quote import QuoteService
|
|
28
|
+
from qdata_quote import QuoteService, get_stock_codes
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
# 获取全市场股票代码(首次从网络获取约 4 秒,后续读缓存 <1ms)
|
|
31
|
+
codes = get_stock_codes()
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
service.set_stock_codes(
|
|
33
|
+
service = QuoteService()
|
|
34
|
+
service.set_stock_codes(codes)
|
|
34
35
|
|
|
35
36
|
# 同步获取指定股票行情
|
|
36
37
|
df = service.get_real_sync(["000001", "600000"])
|
|
@@ -45,6 +46,20 @@ df = asyncio.run(service.get_real(["000001", "600000"]))
|
|
|
45
46
|
df_all = asyncio.run(service.get_all())
|
|
46
47
|
```
|
|
47
48
|
|
|
49
|
+
## 获取股票代码
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from qdata_quote import get_stock_codes
|
|
53
|
+
|
|
54
|
+
# 读取缓存(如果没有缓存则自动获取)
|
|
55
|
+
codes = get_stock_codes()
|
|
56
|
+
|
|
57
|
+
# 强制从网络刷新
|
|
58
|
+
codes = get_stock_codes(refresh=True)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
缓存文件位置:`~/.qdata_quote/stock_codes.json`
|
|
62
|
+
|
|
48
63
|
## 数据源
|
|
49
64
|
|
|
50
65
|
支持两个数据源,通过 `source` 参数指定:
|
|
@@ -109,18 +124,18 @@ df = service.get_real_sync(["000001"], source="tencent")
|
|
|
109
124
|
```python
|
|
110
125
|
# 同步
|
|
111
126
|
with QuoteService() as service:
|
|
112
|
-
service.set_stock_codes(
|
|
127
|
+
service.set_stock_codes(get_stock_codes())
|
|
113
128
|
df = service.get_all_sync()
|
|
114
129
|
|
|
115
130
|
# 异步
|
|
116
131
|
async with QuoteService() as service:
|
|
117
|
-
service.set_stock_codes(
|
|
132
|
+
service.set_stock_codes(get_stock_codes())
|
|
118
133
|
df = await service.get_all()
|
|
119
134
|
```
|
|
120
135
|
|
|
121
136
|
## 性能对比
|
|
122
137
|
|
|
123
|
-
与 easyquotation 对比(
|
|
138
|
+
与 easyquotation 对比(5500+ 只股票):
|
|
124
139
|
|
|
125
140
|
| 数据源 | easyquotation | qdata_quote sync | qdata_quote async |
|
|
126
141
|
|--------|--------------|-------------------|-------------------|
|
|
@@ -11,12 +11,13 @@ pip install qdata-quote
|
|
|
11
11
|
## 快速开始
|
|
12
12
|
|
|
13
13
|
```python
|
|
14
|
-
from qdata_quote import QuoteService
|
|
14
|
+
from qdata_quote import QuoteService, get_stock_codes
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
# 获取全市场股票代码(首次从网络获取约 4 秒,后续读缓存 <1ms)
|
|
17
|
+
codes = get_stock_codes()
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
service.set_stock_codes(
|
|
19
|
+
service = QuoteService()
|
|
20
|
+
service.set_stock_codes(codes)
|
|
20
21
|
|
|
21
22
|
# 同步获取指定股票行情
|
|
22
23
|
df = service.get_real_sync(["000001", "600000"])
|
|
@@ -31,6 +32,20 @@ df = asyncio.run(service.get_real(["000001", "600000"]))
|
|
|
31
32
|
df_all = asyncio.run(service.get_all())
|
|
32
33
|
```
|
|
33
34
|
|
|
35
|
+
## 获取股票代码
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from qdata_quote import get_stock_codes
|
|
39
|
+
|
|
40
|
+
# 读取缓存(如果没有缓存则自动获取)
|
|
41
|
+
codes = get_stock_codes()
|
|
42
|
+
|
|
43
|
+
# 强制从网络刷新
|
|
44
|
+
codes = get_stock_codes(refresh=True)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
缓存文件位置:`~/.qdata_quote/stock_codes.json`
|
|
48
|
+
|
|
34
49
|
## 数据源
|
|
35
50
|
|
|
36
51
|
支持两个数据源,通过 `source` 参数指定:
|
|
@@ -95,18 +110,18 @@ df = service.get_real_sync(["000001"], source="tencent")
|
|
|
95
110
|
```python
|
|
96
111
|
# 同步
|
|
97
112
|
with QuoteService() as service:
|
|
98
|
-
service.set_stock_codes(
|
|
113
|
+
service.set_stock_codes(get_stock_codes())
|
|
99
114
|
df = service.get_all_sync()
|
|
100
115
|
|
|
101
116
|
# 异步
|
|
102
117
|
async with QuoteService() as service:
|
|
103
|
-
service.set_stock_codes(
|
|
118
|
+
service.set_stock_codes(get_stock_codes())
|
|
104
119
|
df = await service.get_all()
|
|
105
120
|
```
|
|
106
121
|
|
|
107
122
|
## 性能对比
|
|
108
123
|
|
|
109
|
-
与 easyquotation 对比(
|
|
124
|
+
与 easyquotation 对比(5500+ 只股票):
|
|
110
125
|
|
|
111
126
|
| 数据源 | easyquotation | qdata_quote sync | qdata_quote async |
|
|
112
127
|
|--------|--------------|-------------------|-------------------|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""全市场股票代码获取与缓存"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
_CACHE_PATH = Path.home() / ".qdata_quote" / "stock_codes.json"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_stock_codes(refresh: bool = False) -> list[str]:
|
|
16
|
+
"""获取全市场 A 股股票代码列表
|
|
17
|
+
|
|
18
|
+
首次调用从新浪 API 获取并缓存到本地,后续直接读取缓存。
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
refresh: 是否强制从网络刷新,默认 False 读取缓存
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
股票代码列表,如 ["000001", "000002", "600000", ...]
|
|
25
|
+
"""
|
|
26
|
+
if not refresh and _CACHE_PATH.exists():
|
|
27
|
+
with open(_CACHE_PATH) as f:
|
|
28
|
+
return json.load(f)
|
|
29
|
+
|
|
30
|
+
codes = _fetch_from_sina()
|
|
31
|
+
_CACHE_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
32
|
+
with open(_CACHE_PATH, "w") as f:
|
|
33
|
+
json.dump(codes, f)
|
|
34
|
+
return codes
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _fetch_from_sina() -> list[str]:
|
|
38
|
+
"""从新浪 API 并发分页获取全部 A 股代码"""
|
|
39
|
+
session = requests.Session()
|
|
40
|
+
session.headers.update(
|
|
41
|
+
{
|
|
42
|
+
"Referer": "https://finance.sina.com.cn/",
|
|
43
|
+
"User-Agent": (
|
|
44
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
|
45
|
+
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
46
|
+
"Chrome/120.0.0.0 Safari/537.36"
|
|
47
|
+
),
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
base_url = (
|
|
51
|
+
"https://vip.stock.finance.sina.com.cn/quotes_service/"
|
|
52
|
+
"api/json_v2.php/Market_Center.getHQNodeData?"
|
|
53
|
+
"page={}&num=80&sort=symbol&asc=1&node=hs_a&symbol="
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def fetch_page(page):
|
|
57
|
+
try:
|
|
58
|
+
r = session.get(base_url.format(page), timeout=10)
|
|
59
|
+
data = json.loads(r.text)
|
|
60
|
+
return [s["code"] for s in data] if data else []
|
|
61
|
+
except Exception:
|
|
62
|
+
return []
|
|
63
|
+
|
|
64
|
+
# 80 页覆盖约 6400 只,超过当前 A 股总数
|
|
65
|
+
with ThreadPoolExecutor(max_workers=20) as pool:
|
|
66
|
+
results = pool.map(fetch_page, range(1, 81))
|
|
67
|
+
|
|
68
|
+
all_codes = []
|
|
69
|
+
for codes in results:
|
|
70
|
+
all_codes.extend(codes)
|
|
71
|
+
|
|
72
|
+
session.close()
|
|
73
|
+
# 去重保序
|
|
74
|
+
return list(dict.fromkeys(all_codes))
|
|
File without changes
|
|
File without changes
|
{qdata_quote-0.2.2 → qdata_quote-0.3.0}/docs/superpowers/specs/2026-06-12-quote-service-design.md
RENAMED
|
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
|