aishare-txt 1.0.2__py3-none-any.whl → 1.0.3__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.
- AIShareTxt/core/data_fetcher.py +146 -19
- {aishare_txt-1.0.2.dist-info → aishare_txt-1.0.3.dist-info}/METADATA +5 -4
- {aishare_txt-1.0.2.dist-info → aishare_txt-1.0.3.dist-info}/RECORD +7 -7
- {aishare_txt-1.0.2.dist-info → aishare_txt-1.0.3.dist-info}/WHEEL +0 -0
- {aishare_txt-1.0.2.dist-info → aishare_txt-1.0.3.dist-info}/entry_points.txt +0 -0
- {aishare_txt-1.0.2.dist-info → aishare_txt-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {aishare_txt-1.0.2.dist-info → aishare_txt-1.0.3.dist-info}/top_level.txt +0 -0
AIShareTxt/core/data_fetcher.py
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
import akshare as ak
|
|
9
9
|
import pandas as pd
|
|
10
10
|
import numpy as np
|
|
11
|
+
import pandas_market_calendars as mcal
|
|
11
12
|
from typing import Optional, Union, cast
|
|
12
|
-
from datetime import datetime, timedelta
|
|
13
|
+
from datetime import datetime, timedelta, time
|
|
13
14
|
from .config import IndicatorConfig as Config
|
|
14
15
|
from ..utils.utils import LoggerManager
|
|
15
16
|
import warnings
|
|
@@ -22,14 +23,122 @@ class StockDataFetcher:
|
|
|
22
23
|
def __init__(self):
|
|
23
24
|
self.config = Config()
|
|
24
25
|
self.logger = LoggerManager.get_logger('data_fetcher')
|
|
26
|
+
# 获取上交所(SSE)日历
|
|
27
|
+
self.sse_calendar = mcal.get_calendar('SSE')
|
|
25
28
|
|
|
29
|
+
def _is_trading_day_and_not_closed(self) -> bool:
|
|
30
|
+
"""
|
|
31
|
+
判断今天是否是交易日且未收盘
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
bool: True表示今天是交易日且未收盘,False表示非交易日或已收盘
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
now = datetime.now()
|
|
38
|
+
today = now.date()
|
|
39
|
+
current_time = now.time()
|
|
40
|
+
|
|
41
|
+
# 首先使用 pandas_market_calendars 判断是否为交易日
|
|
42
|
+
is_trading_day = self._is_trading_day(today)
|
|
43
|
+
|
|
44
|
+
if not is_trading_day:
|
|
45
|
+
self.logger.debug(f"今天 {today} 不是交易日")
|
|
46
|
+
return False
|
|
47
|
+
|
|
48
|
+
# 收盘时间:15:00
|
|
49
|
+
market_close = time(15, 0)
|
|
50
|
+
|
|
51
|
+
# 判断今天是否已收盘
|
|
52
|
+
is_market_closed = current_time >= market_close
|
|
53
|
+
|
|
54
|
+
self.logger.debug(f"当前时间: {now}")
|
|
55
|
+
self.logger.debug(f"是否为交易日: {is_trading_day}")
|
|
56
|
+
self.logger.debug(f"市场是否已收盘: {is_market_closed}")
|
|
57
|
+
|
|
58
|
+
# 如果是交易日且未收盘,返回True
|
|
59
|
+
return not is_market_closed
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
self.logger.warning(f"判断交易时间时出错:{str(e)}")
|
|
63
|
+
# 如果无法判断,返回False(保守处理)
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
def _is_trading_day(self, date_to_check) -> bool:
|
|
67
|
+
"""
|
|
68
|
+
判断指定日期是否为交易日
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
date_to_check: 要检查的日期
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
bool: True表示是交易日,False表示非交易日
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
# 获取日期前后的交易日历(扩大范围以确保能获取到数据)
|
|
78
|
+
start_date = date_to_check - timedelta(days=7) # 往前7天
|
|
79
|
+
end_date = date_to_check + timedelta(days=7) # 往后7天
|
|
80
|
+
|
|
81
|
+
schedule = self.sse_calendar.schedule(start_date=start_date, end_date=end_date)
|
|
82
|
+
|
|
83
|
+
if schedule.empty:
|
|
84
|
+
self.logger.debug(f"无法获取 {start_date} 到 {end_date} 的交易日历")
|
|
85
|
+
return self._fallback_trading_day_check(date_to_check)
|
|
86
|
+
|
|
87
|
+
# 检查指定日期是否在交易日历中
|
|
88
|
+
trading_days = schedule.index.date
|
|
89
|
+
is_trading = date_to_check in trading_days
|
|
90
|
+
|
|
91
|
+
self.logger.debug(f"使用日历检查 {date_to_check}: {'是交易日' if is_trading else '非交易日'}")
|
|
92
|
+
return is_trading
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
self.logger.warning(f"使用pandas_market_calendars判断交易日失败:{str(e)}")
|
|
96
|
+
# 如果 pandas_market_calendars 失败,回退到简单的周一到周五判断
|
|
97
|
+
return self._fallback_trading_day_check(date_to_check)
|
|
98
|
+
|
|
99
|
+
def _fallback_trading_day_check(self, date_to_check) -> bool:
|
|
100
|
+
"""
|
|
101
|
+
备用的交易日判断方法(简单的周一到周五判断)
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
date_to_check: 要检查的日期
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
bool: True表示是交易日,False表示非交易日
|
|
108
|
+
"""
|
|
109
|
+
weekday = date_to_check.weekday()
|
|
110
|
+
is_trading = weekday < 5 # 0-4 是周一到周五
|
|
111
|
+
self.logger.debug(f"使用备用方法检查 {date_to_check}: {'是交易日' if is_trading else '非交易日'}")
|
|
112
|
+
return is_trading
|
|
113
|
+
|
|
114
|
+
def _remove_incomplete_trading_data(self, data: pd.DataFrame) -> pd.DataFrame:
|
|
115
|
+
"""
|
|
116
|
+
如果今天是交易日且未收盘,移除最后一个不完整的交易日数据
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
data: 股票数据DataFrame
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
处理后的DataFrame
|
|
123
|
+
"""
|
|
124
|
+
if self._is_trading_day_and_not_closed():
|
|
125
|
+
self.logger.info("检测到当前为交易日且未收盘,移除最新不完整数据")
|
|
126
|
+
# 移除最后一行数据
|
|
127
|
+
if len(data) > 0:
|
|
128
|
+
data = data.iloc[:-1].copy()
|
|
129
|
+
self.logger.info(f"已移除最新数据,剩余 {len(data)} 条记录")
|
|
130
|
+
else:
|
|
131
|
+
self.logger.debug("当前为非交易日或已收盘,保留所有数据")
|
|
132
|
+
|
|
133
|
+
return data
|
|
134
|
+
|
|
26
135
|
def _add_stock_prefix(self, stock_code: str) -> str:
|
|
27
136
|
"""
|
|
28
137
|
为股票代码添加市场前缀
|
|
29
|
-
|
|
138
|
+
|
|
30
139
|
Args:
|
|
31
140
|
stock_code (str): 6位股票代码,如 '000001'
|
|
32
|
-
|
|
141
|
+
|
|
33
142
|
Returns:
|
|
34
143
|
str: 带前缀的股票代码,如 'sz000001' 或 'sh600000'
|
|
35
144
|
"""
|
|
@@ -110,11 +219,22 @@ class StockDataFetcher:
|
|
|
110
219
|
self.logger.error("数据类型错误:期望DataFrame类型")
|
|
111
220
|
return None
|
|
112
221
|
data = data.sort_values('date').reset_index(drop=True)
|
|
113
|
-
|
|
222
|
+
|
|
223
|
+
# 检查并处理未收盘的不完整数据
|
|
224
|
+
original_length = len(data)
|
|
225
|
+
data = self._remove_incomplete_trading_data(data)
|
|
226
|
+
|
|
227
|
+
if len(data) == 0:
|
|
228
|
+
self.logger.error("处理后数据为空")
|
|
229
|
+
return None
|
|
230
|
+
|
|
114
231
|
self.logger.info(f"✓ 成功处理股票 {stock_code} 的数据,共 {len(data)} 条记录")
|
|
232
|
+
if original_length != len(data):
|
|
233
|
+
self.logger.info(f"已移除 {original_length - len(data)} 条不完整交易数据")
|
|
234
|
+
|
|
115
235
|
self.logger.debug(f"数据日期范围: {data['date'].iloc[0]} 到 {data['date'].iloc[-1]}")
|
|
116
236
|
self.logger.info(f"最新收盘价: {data['close'].iloc[-1]:.{self.config.DISPLAY_PRECISION['price']}f}")
|
|
117
|
-
|
|
237
|
+
|
|
118
238
|
return data
|
|
119
239
|
|
|
120
240
|
except Exception as e:
|
|
@@ -211,10 +331,10 @@ class StockDataFetcher:
|
|
|
211
331
|
stock_code (str): 股票代码,如 '000001'
|
|
212
332
|
period (str): 周期,默认为 'daily'
|
|
213
333
|
adjust (str): 复权类型,默认为 'qfq'
|
|
214
|
-
start_date (str): 开始日期,如 '20230101'
|
|
334
|
+
start_date (str): 开始日期,如 '20230101',默认为当前时间往前推4个月
|
|
215
335
|
|
|
216
336
|
Returns:
|
|
217
|
-
pd.DataFrame or None: 股票数据DataFrame
|
|
337
|
+
pd.DataFrame or None: 股票数据DataFrame,失败返回None
|
|
218
338
|
"""
|
|
219
339
|
if period is None:
|
|
220
340
|
period = self.config.DATA_CONFIG['default_period']
|
|
@@ -253,51 +373,58 @@ class StockDataFetcher:
|
|
|
253
373
|
self.logger.info("4. 股票代码不存在或已退市")
|
|
254
374
|
return None
|
|
255
375
|
|
|
376
|
+
|
|
377
|
+
|
|
256
378
|
# 处理数据
|
|
257
379
|
return self._process_stock_data(raw_data, stock_code)
|
|
258
380
|
|
|
259
381
|
def get_fund_flow_data(self, stock_code):
|
|
260
382
|
"""
|
|
261
383
|
获取主力资金流数据
|
|
262
|
-
|
|
384
|
+
|
|
263
385
|
Args:
|
|
264
386
|
stock_code (str): 股票代码
|
|
265
|
-
|
|
387
|
+
|
|
266
388
|
Returns:
|
|
267
389
|
dict: 资金流数据字典
|
|
390
|
+
|
|
391
|
+
Note:
|
|
392
|
+
akshare 的 stock_individual_fund_flow API 已经处理了交易日不完整数据的问题,
|
|
393
|
+
不需要额外移除当日数据
|
|
268
394
|
"""
|
|
269
395
|
try:
|
|
270
396
|
self.logger.info("正在获取主力资金流数据...")
|
|
271
|
-
|
|
397
|
+
|
|
272
398
|
fund_flow_data = {}
|
|
273
|
-
|
|
399
|
+
|
|
274
400
|
# 获取个股资金流数据
|
|
275
401
|
try:
|
|
276
402
|
# 根据股票代码判断市场
|
|
277
403
|
market = self._determine_market(stock_code)
|
|
278
404
|
fund_df = ak.stock_individual_fund_flow(stock=stock_code, market=market)
|
|
279
|
-
|
|
405
|
+
|
|
280
406
|
if fund_df is not None and len(fund_df) > 0:
|
|
281
407
|
# 获取最新一天的数据
|
|
282
408
|
latest_row = fund_df.iloc[-1]
|
|
283
|
-
|
|
409
|
+
|
|
284
410
|
# 解析资金流数据
|
|
285
411
|
fund_flow_data = self._parse_fund_flow_data(latest_row)
|
|
286
|
-
|
|
412
|
+
|
|
287
413
|
# 计算5日累计数据
|
|
288
414
|
if len(fund_df) >= 5:
|
|
289
415
|
fund_flow_data.update(self._calculate_5day_fund_flow(fund_df))
|
|
290
|
-
|
|
416
|
+
|
|
291
417
|
self.logger.info("✓ 主力资金流数据获取成功")
|
|
292
|
-
|
|
418
|
+
self.logger.debug(f"资金流数据范围: {fund_df.iloc[0].get('日期', 'N/A')} 到 {fund_df.iloc[-1].get('日期', 'N/A')}")
|
|
419
|
+
|
|
293
420
|
except Exception as e:
|
|
294
421
|
self.logger.warning(f"获取个股资金流数据失败:{str(e)}")
|
|
295
|
-
|
|
422
|
+
|
|
296
423
|
# 数据清洗和格式化
|
|
297
424
|
fund_flow_data = self._clean_fund_flow_data(fund_flow_data)
|
|
298
|
-
|
|
425
|
+
|
|
299
426
|
return fund_flow_data
|
|
300
|
-
|
|
427
|
+
|
|
301
428
|
except Exception as e:
|
|
302
429
|
self.logger.error(f"获取主力资金流数据失败:{str(e)}")
|
|
303
430
|
return {}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aishare-txt
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: 中国股票技术指标文本生成工具包,用于为金融分析相关领域的AI智能体提供上下文服务。
|
|
5
5
|
Home-page: https://gitee.com/chaofanat/aishare-txt
|
|
6
6
|
Author: AIShareTxt Team
|
|
@@ -23,6 +23,7 @@ Requires-Dist: TA-Lib>=0.4.26
|
|
|
23
23
|
Requires-Dist: pandas>=1.5.0
|
|
24
24
|
Requires-Dist: numpy>=1.21.0
|
|
25
25
|
Requires-Dist: requests>=2.28.0
|
|
26
|
+
Requires-Dist: pandas_market_calendars>=1.1.0
|
|
26
27
|
Requires-Dist: openai>=1.0.0
|
|
27
28
|
Requires-Dist: zhipuai>=2.0.0
|
|
28
29
|
Provides-Extra: dev
|
|
@@ -461,9 +462,9 @@ batch_analysis()
|
|
|
461
462
|
|
|
462
463
|
## 📞 联系方式
|
|
463
464
|
|
|
464
|
-
- 项目主页: https://github.com/
|
|
465
|
-
- 问题反馈: https://github.com/
|
|
466
|
-
- 邮箱:
|
|
465
|
+
- 项目主页: https://github.com/chaofanat/aishare-txt
|
|
466
|
+
- 问题反馈: https://github.com/chaofanat/aishare-txt/issues
|
|
467
|
+
- 邮箱: chaofanat@gmail.com
|
|
467
468
|
|
|
468
469
|
## 🙏 致谢
|
|
469
470
|
|
|
@@ -5,7 +5,7 @@ AIShareTxt/ai/providers/__init__.py,sha256=xTHQjb3D8uhzORRK0PLIAtMGQjBiOAB1Ku_W5
|
|
|
5
5
|
AIShareTxt/core/__init__.py,sha256=amDOtASzAiPWcFF7bpqUDcbrnMB9cvbi3yb4XGUVkzs,390
|
|
6
6
|
AIShareTxt/core/analyzer.py,sha256=wSRKzILTI-bZSflcXv9l_nBXsfk3-K2f8OBjMQpHf6M,18277
|
|
7
7
|
AIShareTxt/core/config.py,sha256=Y0MEqxkrRU0KYkEoP7maQPNyOYr8Klf7xBhpJnCxP8Q,10133
|
|
8
|
-
AIShareTxt/core/data_fetcher.py,sha256=
|
|
8
|
+
AIShareTxt/core/data_fetcher.py,sha256=jUKvgOIEyfqHlX60Q55U_EMjW8YwlrCVFqmly-72vhY,24650
|
|
9
9
|
AIShareTxt/core/report_generator.py,sha256=0oLwwplpnnll_QLwUq8lB3lFFtmp2sh2fE1PXENo2pg,34914
|
|
10
10
|
AIShareTxt/docs/AI_INTEGRATION.md,sha256=o_LShuGc2yflg-GtB9jZfoSI7FoLE7azLJ2KaZbdzwE,4826
|
|
11
11
|
AIShareTxt/docs/README_重构说明.md,sha256=ESqc8LrIjzdkvYT6EwPsovsy1D7erFg0Wrw7SlhWeeE,6067
|
|
@@ -17,9 +17,9 @@ AIShareTxt/tests/__init__.py,sha256=MlKVJxxbjvhfSuwtqxm3oTCvuR4vKKAF2RnteIreMzI,
|
|
|
17
17
|
AIShareTxt/utils/__init__.py,sha256=0uIK4zwLAskgc6gWaJb1jLLYBuymHS4cB_bTezRz0W0,324
|
|
18
18
|
AIShareTxt/utils/stock_list.py,sha256=XNB5L38zcrGAfrfbKCkr6rX0LgvC-ARZEZ6Yw_7AmUg,12643
|
|
19
19
|
AIShareTxt/utils/utils.py,sha256=moitjd_pInNX0AuLMDH68zHoJExn_LRIWXSHAedqR3A,18626
|
|
20
|
-
aishare_txt-1.0.
|
|
21
|
-
aishare_txt-1.0.
|
|
22
|
-
aishare_txt-1.0.
|
|
23
|
-
aishare_txt-1.0.
|
|
24
|
-
aishare_txt-1.0.
|
|
25
|
-
aishare_txt-1.0.
|
|
20
|
+
aishare_txt-1.0.3.dist-info/licenses/LICENSE,sha256=XSvtcrKVUiuUFjMkfG9j3uejIjZQY17bVdkVb4Ztxlk,9441
|
|
21
|
+
aishare_txt-1.0.3.dist-info/METADATA,sha256=A2CXNVfrIxYHOmPkv1cloacZRfq3QNE0n-27lIrZAzA,13330
|
|
22
|
+
aishare_txt-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
23
|
+
aishare_txt-1.0.3.dist-info/entry_points.txt,sha256=oBpEICBCJsNJi8FpvmswG1rqeRMNz6c2KmVhZyEqXts,58
|
|
24
|
+
aishare_txt-1.0.3.dist-info/top_level.txt,sha256=xblaN5YdcyV6_Y9T9yFQgC7uettcJc8gKYCmw4UW9I4,11
|
|
25
|
+
aishare_txt-1.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|