rquote 0.4.4__py3-none-any.whl → 0.4.6__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.
- rquote/__init__.py +3 -1
- rquote/api/__init__.py +4 -2
- rquote/api/lists.py +65 -1
- rquote/markets/base.py +16 -6
- rquote/markets/cn_stock.py +9 -2
- {rquote-0.4.4.dist-info → rquote-0.4.6.dist-info}/METADATA +24 -2
- {rquote-0.4.4.dist-info → rquote-0.4.6.dist-info}/RECORD +9 -9
- {rquote-0.4.4.dist-info → rquote-0.4.6.dist-info}/WHEEL +0 -0
- {rquote-0.4.4.dist-info → rquote-0.4.6.dist-info}/top_level.txt +0 -0
rquote/__init__.py
CHANGED
|
@@ -23,7 +23,8 @@ from .api import (
|
|
|
23
23
|
get_us_stocks,
|
|
24
24
|
get_cn_fund_list,
|
|
25
25
|
get_tick,
|
|
26
|
-
get_industry_stocks
|
|
26
|
+
get_industry_stocks,
|
|
27
|
+
get_cnindex_stocks
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
# 工具类
|
|
@@ -89,6 +90,7 @@ __all__ = [
|
|
|
89
90
|
'get_cn_fund_list',
|
|
90
91
|
'get_tick',
|
|
91
92
|
'get_industry_stocks',
|
|
93
|
+
'get_cnindex_stocks',
|
|
92
94
|
# 工具类
|
|
93
95
|
'WebUtils',
|
|
94
96
|
'BasicFactors',
|
rquote/api/__init__.py
CHANGED
|
@@ -10,7 +10,8 @@ from .lists import (
|
|
|
10
10
|
get_cn_fund_list,
|
|
11
11
|
get_cn_future_list,
|
|
12
12
|
get_all_industries,
|
|
13
|
-
get_industry_stocks
|
|
13
|
+
get_industry_stocks,
|
|
14
|
+
get_cnindex_stocks
|
|
14
15
|
)
|
|
15
16
|
from .tick import get_tick
|
|
16
17
|
from .stock_info import get_stock_concepts, get_stock_industry
|
|
@@ -27,6 +28,7 @@ __all__ = [
|
|
|
27
28
|
'get_stock_concepts',
|
|
28
29
|
'get_stock_industry',
|
|
29
30
|
'get_all_industries',
|
|
30
|
-
'get_industry_stocks'
|
|
31
|
+
'get_industry_stocks',
|
|
32
|
+
'get_cnindex_stocks'
|
|
31
33
|
]
|
|
32
34
|
|
rquote/api/lists.py
CHANGED
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import re
|
|
7
7
|
import base64
|
|
8
8
|
import time
|
|
9
|
+
from urllib.parse import urlencode
|
|
9
10
|
from ..utils import hget, logger
|
|
10
11
|
from ..exceptions import HTTPError
|
|
11
12
|
|
|
@@ -78,7 +79,7 @@ def get_us_stocks(k=100):
|
|
|
78
79
|
|
|
79
80
|
def get_cn_fund_list():
|
|
80
81
|
"""
|
|
81
|
-
获取A股ETF
|
|
82
|
+
获取A股ETF基金列表(sina)
|
|
82
83
|
|
|
83
84
|
Returns:
|
|
84
85
|
基金列表,格式: [code, name, change, amount, price]
|
|
@@ -173,3 +174,66 @@ def get_industry_stocks(node):
|
|
|
173
174
|
data = json.loads(a.text)
|
|
174
175
|
return data
|
|
175
176
|
|
|
177
|
+
|
|
178
|
+
def get_cnindex_stocks(index_type='hs300'):
|
|
179
|
+
"""
|
|
180
|
+
获取中国指数成分股列表
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
index_type: 指数类型,可选值: 'hs300', 'zz500', 'zz1000'
|
|
184
|
+
hs300: 沪深300 (TYPE=1)
|
|
185
|
+
zz500: 中证500 (TYPE=3)
|
|
186
|
+
zz1000: 中证1000 (TYPE=7)
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
股票列表,包含 SECUCODE, SECURITY_CODE, SECURITY_NAME_ABBR, CLOSE_PRICE 等字段
|
|
190
|
+
"""
|
|
191
|
+
# 指数类型到 TYPE 值的映射
|
|
192
|
+
index_type_map = {
|
|
193
|
+
'hs300': '1',
|
|
194
|
+
'zz500': '3',
|
|
195
|
+
'zz1000': '7'
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if index_type not in index_type_map:
|
|
199
|
+
raise ValueError(f"不支持的指数类型: {index_type},支持的类型: {list(index_type_map.keys())}")
|
|
200
|
+
|
|
201
|
+
type_value = index_type_map[index_type]
|
|
202
|
+
|
|
203
|
+
# 构建 URL
|
|
204
|
+
base_url = 'https://datacenter-web.eastmoney.com/api/data/v1/get'
|
|
205
|
+
params = {
|
|
206
|
+
'callback': 'jQuery112308471143523381743_1763517709888',
|
|
207
|
+
'sortColumns': 'SECURITY_CODE',
|
|
208
|
+
'sortTypes': '-1',
|
|
209
|
+
'pageSize': '500',
|
|
210
|
+
'pageNumber': '1',
|
|
211
|
+
'reportName': 'RPT_INDEX_TS_COMPONENT',
|
|
212
|
+
'columns': 'SECURITY_CODE,SECURITY_NAME_ABBR,INDUSTRY,WEIGHT,EPS,BPS,ROE,FREE_CAP',
|
|
213
|
+
'quoteColumns': '',
|
|
214
|
+
'quoteType': '0',
|
|
215
|
+
'source': 'WEB',
|
|
216
|
+
'client': 'WEB',
|
|
217
|
+
'filter': f'(TYPE="{type_value}")'
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# 构建完整 URL
|
|
221
|
+
url = f'{base_url}?{urlencode(params)}'
|
|
222
|
+
|
|
223
|
+
# 发送请求
|
|
224
|
+
a = hget(url)
|
|
225
|
+
if not a:
|
|
226
|
+
raise HTTPError(f'Failed to fetch {index_type} stocks from EastMoney')
|
|
227
|
+
|
|
228
|
+
# 解析 JSONP 格式的返回数据
|
|
229
|
+
# 格式: jQuery112308471143523381743_1763517709888({...})
|
|
230
|
+
json_str = a.text.split('(', 1)[1].rstrip(');')
|
|
231
|
+
data = json.loads(json_str)
|
|
232
|
+
|
|
233
|
+
# 返回 result.data 中的数据列表
|
|
234
|
+
if data.get('result') and data['result'].get('data'):
|
|
235
|
+
return data['result']['data']
|
|
236
|
+
else:
|
|
237
|
+
logger.warning(f'No data found in response for {index_type}')
|
|
238
|
+
return []
|
|
239
|
+
|
rquote/markets/base.py
CHANGED
|
@@ -98,10 +98,15 @@ class Market(ABC):
|
|
|
98
98
|
if PersistentCache and isinstance(self.cache, PersistentCache):
|
|
99
99
|
# 从完整 key 中提取 base_key
|
|
100
100
|
parts = key.split(':')
|
|
101
|
-
if len(parts)
|
|
101
|
+
if len(parts) == 3:
|
|
102
|
+
# 已经是 base_key 格式:symbol:freq:fq
|
|
103
|
+
base_key = key
|
|
104
|
+
cached = self.cache.get(base_key, sdate=sdate, edate=edate)
|
|
105
|
+
elif len(parts) >= 6:
|
|
106
|
+
# 完整 key 格式:symbol:sdate:edate:freq:days:fq
|
|
102
107
|
symbol = parts[0]
|
|
103
|
-
freq = parts[3]
|
|
104
|
-
fq = parts[5]
|
|
108
|
+
freq = parts[3]
|
|
109
|
+
fq = parts[5]
|
|
105
110
|
base_key = f"{symbol}:{freq}:{fq}"
|
|
106
111
|
cached = self.cache.get(base_key, sdate=sdate, edate=edate)
|
|
107
112
|
else:
|
|
@@ -119,10 +124,15 @@ class Market(ABC):
|
|
|
119
124
|
if PersistentCache and isinstance(self.cache, PersistentCache):
|
|
120
125
|
# 从完整 key 中提取 base_key
|
|
121
126
|
parts = key.split(':')
|
|
122
|
-
if len(parts)
|
|
127
|
+
if len(parts) == 3:
|
|
128
|
+
# 已经是 base_key 格式:symbol:freq:fq
|
|
129
|
+
base_key = key
|
|
130
|
+
self.cache.put(base_key, value)
|
|
131
|
+
elif len(parts) >= 6:
|
|
132
|
+
# 完整 key 格式:symbol:sdate:edate:freq:days:fq
|
|
123
133
|
symbol = parts[0]
|
|
124
|
-
freq = parts[3]
|
|
125
|
-
fq = parts[5]
|
|
134
|
+
freq = parts[3]
|
|
135
|
+
fq = parts[5]
|
|
126
136
|
base_key = f"{symbol}:{freq}:{fq}"
|
|
127
137
|
self.cache.put(base_key, value)
|
|
128
138
|
else:
|
rquote/markets/cn_stock.py
CHANGED
|
@@ -94,6 +94,13 @@ class CNStockMarket(Market):
|
|
|
94
94
|
def _get_pt_price(self, symbol: str, sdate: str, edate: str,
|
|
95
95
|
freq: str, days: int, fq: str) -> Tuple[str, str, pd.DataFrame]:
|
|
96
96
|
"""获取PT代码价格"""
|
|
97
|
+
# 先检查缓存(使用base_key格式,日期通过参数传递)
|
|
98
|
+
base_key = f"{symbol}:{freq}:{fq}"
|
|
99
|
+
cached = self._get_cached(base_key, sdate=sdate, edate=edate)
|
|
100
|
+
if cached:
|
|
101
|
+
logger.info(f"[PT CACHE HIT] symbol={symbol}, 从缓存返回数据")
|
|
102
|
+
return cached
|
|
103
|
+
|
|
97
104
|
try:
|
|
98
105
|
url = f'https://proxy.finance.qq.com/ifzqgtimg/appstock/app/newfqkline/get?_var=kline_dayqfq¶m={symbol},{freq},{sdate},{edate},{days},{fq}'
|
|
99
106
|
response = hget(url)
|
|
@@ -117,7 +124,7 @@ class CNStockMarket(Market):
|
|
|
117
124
|
parser = KlineParser()
|
|
118
125
|
name, df = parser.parse_tencent_kline(data, symbol)
|
|
119
126
|
result = (symbol, name, df)
|
|
120
|
-
self._put_cache(
|
|
127
|
+
self._put_cache(base_key, result)
|
|
121
128
|
return result
|
|
122
129
|
except Exception as e:
|
|
123
130
|
logger.warning(f'Failed to parse {symbol}, using fallback: {e}')
|
|
@@ -147,7 +154,7 @@ class CNStockMarket(Market):
|
|
|
147
154
|
df[col] = pd.to_numeric(df[col], errors='coerce')
|
|
148
155
|
|
|
149
156
|
result = (symbol, name, df)
|
|
150
|
-
self._put_cache(
|
|
157
|
+
self._put_cache(base_key, result)
|
|
151
158
|
return result
|
|
152
159
|
except Exception as e:
|
|
153
160
|
logger.warning(f'error fetching {symbol}, err: {e}')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rquote
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.6
|
|
4
4
|
Summary: Mostly day quotes of cn/hk/us/fund/future markets, side with quote list fetch
|
|
5
5
|
Requires-Python: >=3.9.0
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -18,7 +18,7 @@ Requires-Dist: duckdb>=0.9.0; extra == "persistent"
|
|
|
18
18
|
|
|
19
19
|
## 版本信息
|
|
20
20
|
|
|
21
|
-
当前版本:**0.4.
|
|
21
|
+
当前版本:**0.4.6**
|
|
22
22
|
|
|
23
23
|
## 主要特性
|
|
24
24
|
|
|
@@ -219,6 +219,28 @@ us_stocks = get_us_stocks(k=100) # 获取前100只
|
|
|
219
219
|
# 返回格式: [{name, symbol, market, mktcap, pe, ...}, ...]
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
+
#### `get_cnindex_stocks(index_type='hs300')`
|
|
223
|
+
|
|
224
|
+
获取中国指数成分股列表
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
from rquote import get_cnindex_stocks
|
|
228
|
+
|
|
229
|
+
# 获取沪深300成分股
|
|
230
|
+
hs300_stocks = get_cnindex_stocks('hs300')
|
|
231
|
+
# 获取中证500成分股
|
|
232
|
+
zz500_stocks = get_cnindex_stocks('zz500')
|
|
233
|
+
# 获取中证1000成分股
|
|
234
|
+
zz1000_stocks = get_cnindex_stocks('zz1000')
|
|
235
|
+
|
|
236
|
+
# 返回格式: [{SECURITY_CODE, SECURITY_NAME_ABBR, INDUSTRY, WEIGHT, EPS, BPS, ROE, FREE_CAP, ...}, ...]
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
支持的指数类型:
|
|
240
|
+
- `'hs300'`: 沪深300
|
|
241
|
+
- `'zz500'`: 中证500
|
|
242
|
+
- `'zz1000'`: 中证1000
|
|
243
|
+
|
|
222
244
|
### 基金和期货
|
|
223
245
|
|
|
224
246
|
#### `get_cn_fund_list()`
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
rquote/__init__.py,sha256=
|
|
1
|
+
rquote/__init__.py,sha256=VEZOnqv7Erc34qqllI17bG9cezoaf9uNoOtX9fmhtyw,2420
|
|
2
2
|
rquote/config.py,sha256=noep_VzY_nJehnkPQb4mkwzpeYLwkU1riqofQJ6Hhw0,1108
|
|
3
3
|
rquote/exceptions.py,sha256=lJH2GC5dDhMoW_OtlBc03wlUn684-7jNPyF1NjmfVIE,569
|
|
4
4
|
rquote/plots.py,sha256=UQn4sjhIzVwagfhUDM738b2HHjKo4tRdU2UCs_1-FbY,2341
|
|
5
5
|
rquote/utils.py,sha256=bH0ZFIo-ZelNztzPS6BXFShXE3yGA9USI_P9INN0Y-s,310
|
|
6
|
-
rquote/api/__init__.py,sha256=
|
|
7
|
-
rquote/api/lists.py,sha256=
|
|
6
|
+
rquote/api/__init__.py,sha256=17VgPOKsbQX8kKcuU8fKqn4qOHxMamV9GBTXTCcbdvw,706
|
|
7
|
+
rquote/api/lists.py,sha256=WGyliA9pkpPrN019xkhKL-5nzrmTGrsb8bnG8Zz4cRs,7395
|
|
8
8
|
rquote/api/price.py,sha256=I5lZl6cUQRlE4AtzNbR-uGZt1ho9vgP1cgNFDjaigMA,3575
|
|
9
9
|
rquote/api/stock_info.py,sha256=h_AbgsR8CLWz5zA2PtGsS3ROQ3qcw_hnRAtG3USeMos,2988
|
|
10
10
|
rquote/api/tick.py,sha256=nEcjuAjtBHUaD8KPRLg643piVa21PhKDQvkVWNwvvME,1431
|
|
@@ -19,8 +19,8 @@ rquote/data_sources/tencent.py,sha256=ayt1O85pheLwzX3z5c6Qij1NrmUywcsz6YcSVzdDoM
|
|
|
19
19
|
rquote/factors/__init__.py,sha256=_ZbH2XxYtXwCJpvRVdNvGncoPSpMqrtlYmf1_fMGIjM,116
|
|
20
20
|
rquote/factors/technical.py,sha256=dPDs3pDEDRV9iQJBrSoKpGFLQMjOqyoBdN2rUntpOUU,4235
|
|
21
21
|
rquote/markets/__init__.py,sha256=k4F8cZgb-phqemMqhZXFPdOKsR4P--DD3d5i21vKhbg,365
|
|
22
|
-
rquote/markets/base.py,sha256=
|
|
23
|
-
rquote/markets/cn_stock.py,sha256=
|
|
22
|
+
rquote/markets/base.py,sha256=SyzghAMvSoQNL8DbHCXbWQ0bhu0pDlJrYmO16L_blOI,11646
|
|
23
|
+
rquote/markets/cn_stock.py,sha256=lATGlunbR5cV-D84S3iD-VKc3L71hz4dyYx8YBYiuQg,8596
|
|
24
24
|
rquote/markets/factory.py,sha256=4Txpuok0LBOLT_vAiIU-NslwVnYF7sKHCdlacAboxpo,2875
|
|
25
25
|
rquote/markets/future.py,sha256=yGMyUu9Fv75jbzPbvW6_36otEeebSij7vnzow_zyEn8,7358
|
|
26
26
|
rquote/markets/hk_stock.py,sha256=AhRJpWp027ACew9ogxkVCJXbqbYQ1AkbFwDJccXbvAs,1183
|
|
@@ -33,7 +33,7 @@ rquote/utils/helpers.py,sha256=V07n9BtRS8bEJH023Kca78-unk7iD3B9hn2UjELetYs,354
|
|
|
33
33
|
rquote/utils/http.py,sha256=X0Alhnu0CNqyQeOt6ivUWmh2XwrWxXd2lSpQOKDdnzw,3249
|
|
34
34
|
rquote/utils/logging.py,sha256=fs2YF1Srux4LLTdk_Grjm5g1f4mzewI38VVSAI82goA,1471
|
|
35
35
|
rquote/utils/web.py,sha256=I8_pcThW6VUvahuRHdtp32iZwr85hEt1hB6TgznMy_U,3854
|
|
36
|
-
rquote-0.4.
|
|
37
|
-
rquote-0.4.
|
|
38
|
-
rquote-0.4.
|
|
39
|
-
rquote-0.4.
|
|
36
|
+
rquote-0.4.6.dist-info/METADATA,sha256=LRPiTig-kVH_yqrJpXu2h_8wlq4q9pjghHs3Fmv687w,14898
|
|
37
|
+
rquote-0.4.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
38
|
+
rquote-0.4.6.dist-info/top_level.txt,sha256=CehAiaZx7Fo8HGoV2zd5GhILUW1jQEN8YS-cWMlrK9Y,7
|
|
39
|
+
rquote-0.4.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|