aishare-txt 1.0.1__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/config.py CHANGED
@@ -191,7 +191,7 @@ class IndicatorConfig:
191
191
 
192
192
  # DeepSeek AI配置
193
193
  'deepseek': {
194
- 'api_key': os.environ.get('DEEPSEEK_API_KEY', 'sk-76e7edcd5796492a907d1f11c121f602'), # DeepSeek API密钥(从环境变量获取)
194
+ 'api_key': os.environ.get('DEEPSEEK_API_KEY'), # DeepSeek API密钥(从环境变量获取)
195
195
  'base_url': 'https://api.deepseek.com', # DeepSeek API地址
196
196
  'model': 'deepseek-chat', # 使用的模型
197
197
  'max_tokens': 100, # 最大输出token数
@@ -200,7 +200,7 @@ class IndicatorConfig:
200
200
 
201
201
  # 智谱AI配置
202
202
  'zhipuai': {
203
- 'api_key': '554b0440621a402ba40c9ee3de113f29.wGxg4Y9PHYJ0D8BE', # 智谱AI API密钥
203
+ 'api_key': os.environ.get('ZHIPUAI_API_KEY'), # 智谱AI API密钥
204
204
  'model': 'glm-4.5-flash', # 使用的模型
205
205
  'max_tokens': 100, # 最大输出token数
206
206
  'temperature': 0.6 # 控制随机性,降低以提高一致性
@@ -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',默认为当前时间往前推4个月
334
+ start_date (str): 开始日期,如 '20230101',默认为当前时间往前推4个月
215
335
 
216
336
  Returns:
217
- pd.DataFrame or None: 股票数据DataFrame,失败返回None
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.1
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
@@ -77,26 +78,38 @@ AIShareTxt是一个功能强大的Python股票技术指标分析工具包,提
77
78
  ### 安装方法
78
79
 
79
80
  ```bash
80
- # 从源码安装(推荐)
81
- git clone https://github.com/your-repo/aishare-txt.git
81
+ # 从源码安装
82
+ git clone https://gitee.com/chaofanat/aishare-txt
82
83
  cd aishare-txt
83
84
  pip install -e .
84
85
 
85
- # 或者直接安装依赖后使用项目
86
- pip install -r requirements.txt
86
+ # 或者直接安装使用项目(推荐)
87
+ pip install aishare-txt
87
88
  ```
88
89
 
90
+
89
91
  ### 依赖说明
90
92
 
91
93
  项目会自动安装以下核心依赖:
92
94
  - `akshare>=1.9.0` - 股票数据获取
93
- - `TA-Lib>=0.4.26` - 技术指标计算
95
+ - `TA-Lib>=0.4.26` - 技术指标计算(**需先系统级安装 TA-Lib 二进制库,详见下方提示**)
94
96
  - `pandas>=1.5.0` - 数据处理
95
97
  - `numpy>=1.21.0` - 数值计算
96
98
  - `requests>=2.28.0` - HTTP请求
97
99
  - `openai>=1.0.0` - AI分析(可选)
98
100
  - `zhipuai>=2.0.0` - AI分析(可选)
99
101
 
102
+ > ⚠️ **(https://ta-lib.org/install/)[TA-Lib] 系统级安装提示**
103
+ > 在 Linux/macOS 上请先执行:
104
+ > ```bash
105
+ > # Ubuntu/Debian
106
+ > sudo apt-get install -y build-essential python3-dev ta-lib
107
+ > # macOS (Homebrew)
108
+ > brew install ta-lib
109
+ > ```
110
+ > Windows 用户请下载对应 Python 版本的 [TA-Lib 预编译 whl](https://github.com/cgohlke/talib-build) 后手动安装。
111
+ > 否则 `pip install TA-Lib` 会因缺少底层 C 库而失败。
112
+
100
113
  ## 📖 快速开始
101
114
 
102
115
  ### 基本使用
@@ -232,19 +245,86 @@ AIShareTxt/
232
245
 
233
246
  ### AI配置
234
247
 
235
- 在使用AI功能前,需要配置API密钥:
248
+ 在使用AI功能前,需要配置API密钥。项目支持DeepSeek和智谱AI两种AI服务:
236
249
 
237
250
  1. **DeepSeek配置**
238
- ```python
239
- # 设置环境变量
240
- export DEEPSEEK_API_KEY="your_deepseek_api_key"
241
- ```
251
+ - 需要设置环境变量 `DEEPSEEK_API_KEY`
252
+ - 获取API密钥:访问 [DeepSeek官网](https://platform.deepseek.com/) 注册并获取API密钥
253
+
254
+ **Windows系统设置方法:**
255
+ ```powershell
256
+ # 临时设置(当前会话有效)
257
+ $env:DEEPSEEK_API_KEY="your_deepseek_api_key"
258
+
259
+ # 永久设置(需要管理员权限)
260
+ setx DEEPSEEK_API_KEY "your_deepseek_api_key"
261
+ ```
262
+
263
+ **Linux/macOS系统设置方法:**
264
+ ```bash
265
+ # 临时设置(当前会话有效)
266
+ export DEEPSEEK_API_KEY="your_deepseek_api_key"
267
+
268
+ # 永久设置(添加到~/.bashrc或~/.zshrc)
269
+ echo 'export DEEPSEEK_API_KEY="your_deepseek_api_key"' >> ~/.bashrc
270
+ source ~/.bashrc
271
+ ```
242
272
 
243
273
  2. **智谱AI配置**
244
- ```python
245
- # 在代码中配置
246
- ai_client = AIClient(api_key="your_zhipuai_api_key", provider="zhipuai")
247
- ```
274
+ - 需要设置环境变量 `ZHIPUAI_API_KEY`
275
+ - 获取API密钥:访问 [智谱AI官网](https://open.bigmodel.cn/) 注册并获取API密钥
276
+
277
+ **Windows系统设置方法:**
278
+ ```powershell
279
+ # 临时设置(当前会话有效)
280
+ $env:ZHIPUAI_API_KEY="your_zhipuai_api_key"
281
+
282
+ # 永久设置(需要管理员权限)
283
+ setx ZHIPUAI_API_KEY "your_zhipuai_api_key"
284
+ ```
285
+
286
+ **Linux/macOS系统设置方法:**
287
+ ```bash
288
+ # 临时设置(当前会话有效)
289
+ export ZHIPUAI_API_KEY="your_zhipuai_api_key"
290
+
291
+ # 永久设置(添加到~/.bashrc或~/.zshrc)
292
+ echo 'export ZHIPUAI_API_KEY="your_zhipuai_api_key"' >> ~/.bashrc
293
+ source ~/.bashrc
294
+ ```
295
+
296
+ 3. **验证环境变量设置**
297
+ ```python
298
+ import os
299
+
300
+ # 检查环境变量是否设置成功
301
+ deepseek_key = os.environ.get('DEEPSEEK_API_KEY')
302
+ zhipuai_key = os.environ.get('ZHIPUAI_API_KEY')
303
+
304
+ print(f"DeepSeek API Key: {'已设置' if deepseek_key else '未设置'}")
305
+ print(f"智谱AI API Key: {'已设置' if zhipuai_key else '未设置'}")
306
+ ```
307
+
308
+ 4. **在代码中使用**
309
+ ```python
310
+ from AIShareTxt.ai.client import AIClient
311
+
312
+ # 使用DeepSeek(默认)
313
+ ai_client = AIClient(provider="deepseek")
314
+
315
+ # 使用智谱AI
316
+ ai_client = AIClient(provider="zhipuai")
317
+
318
+ # 进行AI分析
319
+ if ai_client.is_available():
320
+ advice = ai_client.analyze_investment_recommendation(
321
+ technical_report="技术分析报告内容",
322
+ stock_code="000001"
323
+ )
324
+ print(f"AI投资建议: {ai_client.get_recommendation_text(advice)}")
325
+ else:
326
+ print("AI功能不可用,请检查API配置")
327
+ ```
248
328
 
249
329
  ### 分析配置
250
330
 
@@ -382,9 +462,9 @@ batch_analysis()
382
462
 
383
463
  ## 📞 联系方式
384
464
 
385
- - 项目主页: https://github.com/your-repo/aishare-txt
386
- - 问题反馈: https://github.com/your-repo/aishare-txt/issues
387
- - 邮箱: aishare@example.com
465
+ - 项目主页: https://github.com/chaofanat/aishare-txt
466
+ - 问题反馈: https://github.com/chaofanat/aishare-txt/issues
467
+ - 邮箱: chaofanat@gmail.com
388
468
 
389
469
  ## 🙏 致谢
390
470
 
@@ -4,8 +4,8 @@ AIShareTxt/ai/client.py,sha256=eYwiTZMCHBoKzbO60l0EXXB56q4MYxVAJSMwRo9jbYw,11620
4
4
  AIShareTxt/ai/providers/__init__.py,sha256=xTHQjb3D8uhzORRK0PLIAtMGQjBiOAB1Ku_W56KWopU,140
5
5
  AIShareTxt/core/__init__.py,sha256=amDOtASzAiPWcFF7bpqUDcbrnMB9cvbi3yb4XGUVkzs,390
6
6
  AIShareTxt/core/analyzer.py,sha256=wSRKzILTI-bZSflcXv9l_nBXsfk3-K2f8OBjMQpHf6M,18277
7
- AIShareTxt/core/config.py,sha256=tSsnBijqtJN573I8n0tTqpGeXqea16-22U6WWGJ6tSY,10190
8
- AIShareTxt/core/data_fetcher.py,sha256=ziPs0jgd4D3BS5D5Kdttt3AR2EZOWdI1d9s6-sJYWB8,19786
7
+ AIShareTxt/core/config.py,sha256=Y0MEqxkrRU0KYkEoP7maQPNyOYr8Klf7xBhpJnCxP8Q,10133
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.1.dist-info/licenses/LICENSE,sha256=XSvtcrKVUiuUFjMkfG9j3uejIjZQY17bVdkVb4Ztxlk,9441
21
- aishare_txt-1.0.1.dist-info/METADATA,sha256=kMmNw0dafL7Wz1ItquOcK_SoeZXuHfR6GH8UYKlXYZg,10467
22
- aishare_txt-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- aishare_txt-1.0.1.dist-info/entry_points.txt,sha256=oBpEICBCJsNJi8FpvmswG1rqeRMNz6c2KmVhZyEqXts,58
24
- aishare_txt-1.0.1.dist-info/top_level.txt,sha256=xblaN5YdcyV6_Y9T9yFQgC7uettcJc8gKYCmw4UW9I4,11
25
- aishare_txt-1.0.1.dist-info/RECORD,,
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,,