aishare-txt 1.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.
@@ -0,0 +1,507 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 技术指标计算模块
5
+ 负责计算各种技术指标
6
+ """
7
+
8
+ import talib
9
+ import pandas as pd
10
+ import numpy as np
11
+ from ..core.config import IndicatorConfig as Config
12
+ from ..utils.utils import LoggerManager
13
+ import warnings
14
+ warnings.filterwarnings('ignore')
15
+
16
+
17
+ class TechnicalIndicators:
18
+ """技术指标计算器"""
19
+
20
+ def __init__(self):
21
+ self.config = Config()
22
+ self.logger = LoggerManager.get_logger('technical_indicators')
23
+
24
+ def calculate_all_indicators(self, data):
25
+ """
26
+ 计算所有技术指标
27
+
28
+ Args:
29
+ data (pd.DataFrame): 股票数据
30
+
31
+ Returns:
32
+ dict: 包含所有指标的字典
33
+ """
34
+ if data is None or len(data) == 0:
35
+ return None
36
+
37
+ # 获取价格数据
38
+ close = data['close'].values
39
+ high = data['high'].values
40
+ low = data['low'].values
41
+ volume = data['volume'].values
42
+ open_price = data['open'].values
43
+
44
+ indicators = {}
45
+
46
+ # 计算各类指标
47
+ indicators.update(self._calculate_moving_averages(close))
48
+ indicators.update(self._calculate_ma_derived_indicators(close))
49
+ indicators.update(self._calculate_volume_price_indicators(high, low, close, volume))
50
+ indicators.update(self._calculate_trend_strength_indicators(high, low, close))
51
+ indicators.update(self._calculate_momentum_oscillators(high, low, close))
52
+ indicators.update(self._calculate_volatility_indicators(high, low, close))
53
+ indicators.update(self._calculate_volume_indicators(volume))
54
+
55
+ # 添加基础数据
56
+ indicators['current_price'] = close[-1]
57
+ # 将日期转换为字符串格式以便JSON序列化
58
+ date_value = data['date'].iloc[-1]
59
+ if hasattr(date_value, 'strftime'):
60
+ indicators['date'] = date_value.strftime('%Y-%m-%d')
61
+ else:
62
+ indicators['date'] = str(date_value)
63
+
64
+ return indicators
65
+
66
+ def _calculate_moving_averages(self, close):
67
+ """计算移动平均线指标"""
68
+ indicators = {}
69
+
70
+ try:
71
+ # MA(移动平均线)
72
+ all_periods = (self.config.MA_PERIODS['short'] +
73
+ self.config.MA_PERIODS['medium'] +
74
+ self.config.MA_PERIODS['long'])
75
+
76
+ for period in all_periods:
77
+ ma = talib.SMA(close, timeperiod=period)
78
+ indicators[f'MA_{period}'] = ma[-1]
79
+ indicators[f'SMA_{period}'] = ma[-1] # SMA与MA相同,保留用于兼容
80
+
81
+ # EMA(指数移动平均线)
82
+ for period in self.config.EMA_PERIODS:
83
+ ema = talib.EMA(close, timeperiod=period)
84
+ indicators[f'EMA_{period}'] = ema[-1]
85
+
86
+ # WMA(加权移动平均线)
87
+ for period in self.config.WMA_PERIODS:
88
+ wma = talib.WMA(close, timeperiod=period)
89
+ indicators[f'WMA_{period}'] = wma[-1]
90
+
91
+ # 均线形态分析
92
+ ma_patterns = self.analyze_ma_patterns(close)
93
+ indicators.update(ma_patterns)
94
+
95
+ except Exception as e:
96
+ self.logger.warning(f"均线指标计算失败:{str(e)}")
97
+
98
+ return indicators
99
+
100
+ def _calculate_ma_derived_indicators(self, close):
101
+ """计算均线衍生指标"""
102
+ indicators = {}
103
+
104
+ try:
105
+ # BIAS 乖离率
106
+ for period in self.config.BIAS_PERIODS:
107
+ bias = self.calculate_bias(close, timeperiod=period)
108
+ if bias is not None:
109
+ indicators[f'BIAS_{period}'] = bias[-1]
110
+
111
+ # MACD
112
+ macd_config = self.config.MACD_CONFIG
113
+ macd, macdsignal, macdhist = talib.MACD(
114
+ close,
115
+ fastperiod=macd_config['fastperiod'],
116
+ slowperiod=macd_config['slowperiod'],
117
+ signalperiod=macd_config['signalperiod']
118
+ )
119
+ indicators['MACD_DIF'] = macd[-1]
120
+ indicators['MACD_DEA'] = macdsignal[-1]
121
+ indicators['MACD_HIST'] = macdhist[-1]
122
+
123
+ # 布林带
124
+ bb_config = self.config.BOLLINGER_BANDS_CONFIG
125
+ upperband, middleband, lowerband = talib.BBANDS(
126
+ close,
127
+ timeperiod=bb_config['timeperiod'],
128
+ nbdevup=bb_config['nbdevup'],
129
+ nbdevdn=bb_config['nbdevdn'],
130
+ matype=int(bb_config['matype']) # type: ignore
131
+ )
132
+ indicators['BB_UPPER'] = upperband[-1]
133
+ indicators['BB_MIDDLE'] = middleband[-1]
134
+ indicators['BB_LOWER'] = lowerband[-1]
135
+ indicators['BB_WIDTH'] = (upperband[-1] - lowerband[-1]) / middleband[-1] * 100
136
+
137
+ except Exception as e:
138
+ self.logger.warning(f"均线衍生指标计算失败:{str(e)}")
139
+
140
+ return indicators
141
+
142
+ def _calculate_volume_price_indicators(self, high, low, close, volume):
143
+ """计算量价指标"""
144
+ indicators = {}
145
+
146
+ try:
147
+ # VWAP (Volume Weighted Average Price)
148
+ typical_price = (high + low + close) / 3
149
+ vwap_period = self.config.VWAP_PERIOD
150
+ if len(typical_price) >= vwap_period:
151
+ price_volume = typical_price[-vwap_period:] * volume[-vwap_period:]
152
+ indicators['VWAP_14'] = np.sum(price_volume) / np.sum(volume[-vwap_period:])
153
+
154
+ # OBV (On Balance Volume)
155
+ close_float = close.astype(np.float64)
156
+ volume_float = volume.astype(np.float64)
157
+ obv = talib.OBV(close_float, volume_float)
158
+
159
+ indicators.update(self._analyze_obv(obv, close))
160
+
161
+ except Exception as e:
162
+ self.logger.warning(f"量价指标计算失败:{str(e)}")
163
+
164
+ return indicators
165
+
166
+ def _calculate_trend_strength_indicators(self, high, low, close):
167
+ """计算趋势强度指标"""
168
+ indicators = {}
169
+
170
+ try:
171
+ # ADX (Average Directional Index) 和 DMI 指标
172
+ adx_period = self.config.ADX_PERIOD
173
+ adx = talib.ADX(high, low, close, timeperiod=adx_period)
174
+ plus_di = talib.PLUS_DI(high, low, close, timeperiod=adx_period)
175
+ minus_di = talib.MINUS_DI(high, low, close, timeperiod=adx_period)
176
+
177
+ indicators['ADX'] = adx[-1]
178
+ indicators['DI_PLUS'] = plus_di[-1]
179
+ indicators['DI_MINUS'] = minus_di[-1]
180
+ indicators['DI_DIFF'] = plus_di[-1] - minus_di[-1]
181
+
182
+ # ADX趋势强度跟踪
183
+ if len(adx) >= 5:
184
+ adx_recent = adx[-5:]
185
+ adx_slope = np.polyfit(range(len(adx_recent)), adx_recent, 1)[0]
186
+ indicators['ADX_TREND'] = adx_slope
187
+ indicators['ADX_5D_CHANGE'] = ((adx[-1] - adx[-5]) / adx[-5] * 100) if adx[-5] != 0 else 0
188
+
189
+ except Exception as e:
190
+ self.logger.warning(f"趋势强度指标计算失败:{str(e)}")
191
+
192
+ return indicators
193
+
194
+ def _calculate_momentum_oscillators(self, high, low, close):
195
+ """计算动量振荡指标"""
196
+ indicators = {}
197
+
198
+ try:
199
+ # RSI (Relative Strength Index)
200
+ for period in self.config.RSI_PERIODS:
201
+ rsi = talib.RSI(close, timeperiod=period)
202
+ indicators[f'RSI_{period}'] = rsi[-1]
203
+
204
+ # Stochastic Oscillator (KD指标)
205
+ stoch_config = self.config.STOCH_CONFIG
206
+ slowk, slowd = talib.STOCH(
207
+ high, low, close,
208
+ fastk_period=stoch_config['fastk_period'],
209
+ slowk_period=stoch_config['slowk_period'],
210
+ slowk_matype=int(stoch_config['slowk_matype']), # type: ignore
211
+ slowd_period=stoch_config['slowd_period'],
212
+ slowd_matype=int(stoch_config['slowd_matype']) # type: ignore
213
+ )
214
+ indicators['STOCH_K'] = slowk[-1]
215
+ indicators['STOCH_D'] = slowd[-1]
216
+ indicators['STOCH_KD_DIFF'] = slowk[-1] - slowd[-1]
217
+
218
+ except Exception as e:
219
+ self.logger.warning(f"动量振荡指标计算失败:{str(e)}")
220
+
221
+ return indicators
222
+
223
+ def _calculate_volatility_indicators(self, high, low, close):
224
+ """计算波动率指标"""
225
+ indicators = {}
226
+
227
+ try:
228
+ # ATR (Average True Range)
229
+ atr_period = self.config.ATR_PERIOD
230
+ atr = talib.ATR(high, low, close, timeperiod=atr_period)
231
+ indicators['ATR_14'] = atr[-1]
232
+ indicators['ATR_RATIO'] = (atr[-1] / close[-1] * 100) if close[-1] != 0 else 0
233
+
234
+ except Exception as e:
235
+ self.logger.warning(f"波动率指标计算失败:{str(e)}")
236
+
237
+ return indicators
238
+
239
+ def _calculate_volume_indicators(self, volume):
240
+ """计算成交量指标"""
241
+ indicators = {}
242
+
243
+ try:
244
+ # 量比指标
245
+ if len(volume) >= 6:
246
+ current_volume = volume[-1]
247
+ avg_volume_5d = np.mean(volume[-6:-1]) # 过去5日平均
248
+ indicators['VOLUME_RATIO'] = current_volume / avg_volume_5d if avg_volume_5d != 0 else 0
249
+
250
+ # 成交量趋势
251
+ if len(volume) >= 5:
252
+ volume_recent = volume[-5:]
253
+ volume_slope = np.polyfit(range(len(volume_recent)), volume_recent, 1)[0]
254
+ indicators['VOLUME_TREND'] = volume_slope
255
+
256
+ except Exception as e:
257
+ self.logger.warning(f"成交量指标计算失败:{str(e)}")
258
+
259
+ return indicators
260
+
261
+ def calculate_bias(self, close, timeperiod=20):
262
+ """
263
+ 计算乖离率 (BIAS)
264
+ BIAS = (收盘价 - N日SMA) / N日SMA * 100
265
+ """
266
+ sma = talib.SMA(close, timeperiod=timeperiod)
267
+ bias = ((close - sma) / sma) * 100
268
+ return bias
269
+
270
+ def analyze_ma_patterns(self, close):
271
+ """分析均线形态"""
272
+ patterns = {
273
+ 'trend_pattern': '',
274
+ 'cross_pattern': '',
275
+ 'position_pattern': '',
276
+ 'arrangement_pattern': '',
277
+ 'support_resistance': ''
278
+ }
279
+
280
+ # 计算均线
281
+ ma5 = talib.SMA(close, timeperiod=5)
282
+ ma10 = talib.SMA(close, timeperiod=10)
283
+ ma20 = talib.SMA(close, timeperiod=20)
284
+ ma60 = talib.SMA(close, timeperiod=60)
285
+
286
+ if len(ma5) < 2 or len(ma10) < 2 or len(ma20) < 2:
287
+ return patterns
288
+
289
+ current_price = close[-1]
290
+ current_ma5 = ma5[-1]
291
+ current_ma10 = ma10[-1]
292
+ current_ma20 = ma20[-1]
293
+ current_ma60 = ma60[-1] if len(ma60) > 0 else 0
294
+
295
+ prev_ma5 = ma5[-2]
296
+ prev_ma10 = ma10[-2]
297
+ prev_ma20 = ma20[-2]
298
+
299
+ # 使用配置中的阈值
300
+ thresholds = self.config.MA_THRESHOLDS
301
+
302
+ # 1. 趋势形态分析
303
+ patterns['trend_pattern'] = self._analyze_trend_pattern(
304
+ current_ma5, current_ma10, current_ma20, thresholds
305
+ )
306
+
307
+ # 2. 交叉形态分析
308
+ patterns['cross_pattern'] = self._analyze_cross_pattern(
309
+ current_ma5, current_ma10, current_ma20,
310
+ prev_ma5, prev_ma10, prev_ma20
311
+ )
312
+
313
+ # 3. 位置形态分析
314
+ patterns['position_pattern'] = self._analyze_position_pattern(
315
+ current_price, current_ma5, current_ma10, current_ma20, current_ma60, thresholds
316
+ )
317
+
318
+ # 4. 排列形态分析
319
+ patterns['arrangement_pattern'] = self._analyze_arrangement_pattern(
320
+ current_ma5, current_ma10, current_ma20, current_ma60
321
+ )
322
+
323
+ # 5. 支撑阻力分析
324
+ patterns['support_resistance'] = self._analyze_support_resistance(
325
+ current_price, current_ma5, current_ma10, current_ma20, thresholds
326
+ )
327
+
328
+ return patterns
329
+
330
+ def _analyze_trend_pattern(self, ma5, ma10, ma20, thresholds):
331
+ """分析趋势形态"""
332
+ if ma5 > ma10 > ma20:
333
+ # 检查发散程度
334
+ ma_diff_5_10 = (ma5 - ma10) / ma10 * 100
335
+ ma_diff_10_20 = (ma10 - ma20) / ma20 * 100
336
+
337
+ if ma_diff_5_10 > thresholds['divergence_strong'] * 100 and ma_diff_10_20 > thresholds['divergence_strong'] * 100:
338
+ return "多头发散"
339
+ elif ma_diff_5_10 < thresholds['divergence_weak'] * 100 and ma_diff_10_20 < thresholds['divergence_weak'] * 100:
340
+ return "多头收敛"
341
+ else:
342
+ return "多头排列"
343
+
344
+ elif ma5 < ma10 < ma20:
345
+ # 检查发散程度
346
+ ma_diff_5_10 = abs(ma5 - ma10) / ma10 * 100
347
+ ma_diff_10_20 = abs(ma10 - ma20) / ma20 * 100
348
+
349
+ if ma_diff_5_10 > thresholds['divergence_strong'] * 100 and ma_diff_10_20 > thresholds['divergence_strong'] * 100:
350
+ return "空头发散"
351
+ elif ma_diff_5_10 < thresholds['divergence_weak'] * 100 and ma_diff_10_20 < thresholds['divergence_weak'] * 100:
352
+ return "空头收敛"
353
+ else:
354
+ return "空头排列"
355
+ else:
356
+ # 检查是否为盘整形态
357
+ ma_diff_5_10 = abs(ma5 - ma10) / ma10 * 100
358
+ ma_diff_10_20 = abs(ma10 - ma20) / ma20 * 100
359
+ ma_diff_5_20 = abs(ma5 - ma20) / ma20 * 100
360
+
361
+ if (ma_diff_5_10 < thresholds['adhesion_loose'] * 100 and
362
+ ma_diff_10_20 < thresholds['adhesion_loose'] * 100 and
363
+ ma_diff_5_20 < thresholds['adhesion_range'] * 100):
364
+ return "三均线粘合"
365
+ elif ma_diff_5_10 < thresholds['adhesion_tight'] * 100:
366
+ return "MA5与MA10粘合"
367
+ elif ma_diff_10_20 < thresholds['adhesion_tight'] * 100:
368
+ return "MA10与MA20粘合"
369
+ else:
370
+ return "均线缠绕"
371
+
372
+ def _analyze_cross_pattern(self, ma5, ma10, ma20, prev_ma5, prev_ma10, prev_ma20):
373
+ """分析交叉形态"""
374
+ cross_signals = []
375
+
376
+ # 均线交叉检测
377
+ if ma5 > ma10 and prev_ma5 <= prev_ma10:
378
+ cross_signals.append("MA5上穿MA10")
379
+ if ma10 > ma20 and prev_ma10 <= prev_ma20:
380
+ cross_signals.append("MA10上穿MA20")
381
+
382
+ if ma5 < ma10 and prev_ma5 >= prev_ma10:
383
+ cross_signals.append("MA5下穿MA10")
384
+ if ma10 < ma20 and prev_ma10 >= prev_ma20:
385
+ cross_signals.append("MA10下穿MA20")
386
+
387
+ return ",".join(cross_signals) if cross_signals else "无交叉信号"
388
+
389
+ def _analyze_position_pattern(self, price, ma5, ma10, ma20, ma60, thresholds):
390
+ """分析位置形态"""
391
+ position_signals = []
392
+ support_threshold = thresholds['support_resistance']
393
+
394
+ # 价格与均线关系
395
+ for ma_value, ma_name in [(ma5, "MA5"), (ma10, "MA10"), (ma20, "MA20")]:
396
+ if price > ma_value:
397
+ position_signals.append(f"站上{ma_name}")
398
+ elif abs(price - ma_value) / ma_value < support_threshold:
399
+ position_signals.append(f"接近{ma_name}")
400
+
401
+ if ma60 > 0:
402
+ if price > ma60:
403
+ position_signals.append("站上MA60")
404
+ elif abs(price - ma60) / ma60 < support_threshold:
405
+ position_signals.append("接近MA60")
406
+
407
+ return ",".join(position_signals) if position_signals else "均线下方运行"
408
+
409
+ def _analyze_arrangement_pattern(self, ma5, ma10, ma20, ma60):
410
+ """分析排列形态"""
411
+ if ma5 > ma10 > ma20:
412
+ if ma60 > 0 and ma20 > ma60:
413
+ return "MA5>MA10>MA20>MA60"
414
+ else:
415
+ return "MA5>MA10>MA20"
416
+ elif ma5 < ma10 < ma20:
417
+ if ma60 > 0 and ma20 < ma60:
418
+ return "MA5<MA10<MA20<MA60"
419
+ else:
420
+ return "MA5<MA10<MA20"
421
+ else:
422
+ return "均线无规律排列"
423
+
424
+ def _analyze_support_resistance(self, price, ma5, ma10, ma20, thresholds):
425
+ """分析支撑阻力"""
426
+ support_resistance = []
427
+ support_threshold = thresholds['support_resistance']
428
+
429
+ for ma_value, ma_name in [(ma5, "MA5"), (ma10, "MA10"), (ma20, "MA20")]:
430
+ if abs(price - ma_value) / ma_value < support_threshold:
431
+ support_resistance.append(f"接近{ma_name}")
432
+
433
+ return ",".join(support_resistance) if support_resistance else "无接近均线"
434
+
435
+ def _analyze_obv(self, obv, close):
436
+ """分析OBV指标"""
437
+ indicators = {}
438
+ obv_config = self.config.OBV_CONFIG
439
+
440
+ # 计算OBV基础指标
441
+ indicators['OBV_current'] = obv[-1]
442
+ indicators['OBV_5d_ago'] = obv[-6] if len(obv) >= 6 else obv[0]
443
+ indicators['OBV_20d_ago'] = obv[-21] if len(obv) >= 21 else obv[0]
444
+
445
+ # 计算OBV变化率
446
+ obv_5d_change = ((obv[-1] - obv[-6]) / abs(obv[-6]) * 100) if len(obv) >= 6 and obv[-6] != 0 else 0
447
+ obv_20d_change = ((obv[-1] - obv[-21]) / abs(obv[-21]) * 100) if len(obv) >= 21 and obv[-21] != 0 else 0
448
+
449
+ indicators['OBV_5d_change'] = obv_5d_change
450
+ indicators['OBV_20d_change'] = obv_20d_change
451
+
452
+ # 计算OBV趋势(最近5日斜率)
453
+ if len(obv) >= 5:
454
+ recent_obv = obv[-5:]
455
+ x = np.arange(len(recent_obv))
456
+ slope = np.polyfit(x, recent_obv, 1)[0]
457
+ indicators['OBV_trend'] = slope
458
+
459
+ # OBV背离验证
460
+ if len(obv) >= 20 and len(close) >= 20:
461
+ try:
462
+ from scipy.signal import argrelextrema
463
+ indicators['OBV_DIVERGENCE'] = self._detect_obv_divergence(obv, close, obv_config)
464
+ except ImportError:
465
+ indicators['OBV_DIVERGENCE'] = "需要scipy库进行背离分析"
466
+
467
+ return indicators
468
+
469
+ def _detect_obv_divergence(self, obv, close, config):
470
+ """检测OBV背离"""
471
+ try:
472
+ from scipy.signal import argrelextrema
473
+
474
+ recent_close = close[-20:]
475
+ recent_obv = obv[-20:]
476
+
477
+ # 找局部高点和低点
478
+ order = config['extrema_order']
479
+ price_highs = argrelextrema(recent_close, np.greater, order=order)[0]
480
+ price_lows = argrelextrema(recent_close, np.less, order=order)[0]
481
+ obv_highs = argrelextrema(recent_obv, np.greater, order=order)[0]
482
+ obv_lows = argrelextrema(recent_obv, np.less, order=order)[0]
483
+
484
+ # 顶背离:价格创新高,OBV未创新高
485
+ if len(price_highs) >= 2 and len(obv_highs) >= 2:
486
+ latest_price_high_idx = price_highs[-1]
487
+ prev_price_high_idx = price_highs[-2]
488
+
489
+ if latest_price_high_idx < len(obv_highs) and prev_price_high_idx < len(obv_highs):
490
+ if (recent_close[latest_price_high_idx] > recent_close[prev_price_high_idx] and
491
+ recent_obv[latest_price_high_idx] < recent_obv[prev_price_high_idx]):
492
+ return "顶背离"
493
+
494
+ # 底背离:价格创新低,OBV未创新低
495
+ if len(price_lows) >= 2 and len(obv_lows) >= 2:
496
+ latest_price_low_idx = price_lows[-1]
497
+ prev_price_low_idx = price_lows[-2]
498
+
499
+ if latest_price_low_idx < len(obv_lows) and prev_price_low_idx < len(obv_lows):
500
+ if (recent_close[latest_price_low_idx] < recent_close[prev_price_low_idx] and
501
+ recent_obv[latest_price_low_idx] > recent_obv[prev_price_low_idx]):
502
+ return "底背离"
503
+
504
+ return "无明显背离"
505
+
506
+ except Exception:
507
+ return "背离检测失败"
@@ -0,0 +1,11 @@
1
+ """
2
+ AIShareTxt测试模块
3
+
4
+ 包含单元测试和集成测试。
5
+ """
6
+
7
+ # 这里可以添加测试相关的导入
8
+ # from .test_analyzer import TestStockAnalyzer
9
+ # from .test_indicators import TestTechnicalIndicators
10
+
11
+ __all__ = []
@@ -0,0 +1,17 @@
1
+ """
2
+ AIShareTxt工具模块
3
+
4
+ 包含日志管理、股票列表获取等辅助功能。
5
+ """
6
+
7
+ from .utils import Logger
8
+ from .stock_list import get_stock_list
9
+
10
+ __all__ = [
11
+ "Logger",
12
+ "get_stock_list",
13
+ ]
14
+
15
+ # 这里可以添加更多工具函数
16
+ # from .data_validator import DataValidator
17
+ # __all__.append("DataValidator")