aishare-txt 2025.11.27.36__tar.gz → 2026.2.6.38__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.
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/__init__.py +1 -1
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/core/config.py +18 -3
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/indicators/report_generator.py +145 -6
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/indicators/technical_indicators.py +275 -5
- {aishare_txt-2025.11.27.36/aishare_txt.egg-info → aishare_txt-2026.2.6.38}/PKG-INFO +1 -1
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38/aishare_txt.egg-info}/PKG-INFO +1 -1
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/setup.py +1 -1
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/ai/__init__.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/ai/client.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/ai/providers/__init__.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/core/__init__.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/core/data_processor.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/docs/AI_INTEGRATION.md +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/docs/README_/351/207/215/346/236/204/350/257/264/346/230/216.md" +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/docs//351/207/215/346/236/204/345/256/214/346/210/220/346/200/273/347/273/223.md" +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/examples/legacy_api.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/indicators/__init__.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/indicators/data_fetcher.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/tests/__init__.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/utils/__init__.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/utils/stock_list.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/utils/utils.py +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/LICENSE +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/MANIFEST.in +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/README.md +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/aishare_txt.egg-info/SOURCES.txt +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/aishare_txt.egg-info/dependency_links.txt +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/aishare_txt.egg-info/entry_points.txt +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/aishare_txt.egg-info/not-zip-safe +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/aishare_txt.egg-info/requires.txt +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/aishare_txt.egg-info/top_level.txt +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/requirements-dev.txt +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/requirements.txt +0 -0
- {aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/setup.cfg +0 -0
|
@@ -141,10 +141,25 @@ class IndicatorConfig:
|
|
|
141
141
|
'default_period': 'daily',
|
|
142
142
|
'default_adjust': 'qfq', # 前复权
|
|
143
143
|
'required_columns': ['date', 'open', 'close', 'high', 'low', 'volume'],
|
|
144
|
-
'min_data_length':
|
|
145
|
-
'default_months_back':
|
|
144
|
+
'min_data_length': 90, # 最少需要90个交易日数据(支持时空维度分析)
|
|
145
|
+
'default_months_back': 6 # 默认获取往前推6个月的数据(确保有足够数据)
|
|
146
146
|
}
|
|
147
|
-
|
|
147
|
+
|
|
148
|
+
# 时空维度分析配置
|
|
149
|
+
SPATIAL_TEMPORAL_CONFIG = {
|
|
150
|
+
'high_low_lookback_days': 60, # 前高前低回溯天数
|
|
151
|
+
'fibonacci_sequence': [3, 5, 8, 13, 21, 34, 55, 89, 144], # 斐波那契数列
|
|
152
|
+
'fibonacci_window_threshold': 2, # 临近窗口判定阈值(±N日)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# 量能环比配置
|
|
156
|
+
VOLUME_COMPARISON_CONFIG = {
|
|
157
|
+
'short_ma_period': 5, # 短期均量周期
|
|
158
|
+
'medium_ma_period': 20, # 中期均量周期
|
|
159
|
+
'high_volume_threshold': 1.2, # 高位量能阈值(倍数)
|
|
160
|
+
'low_volume_threshold': 0.8, # 低位量能阈值(倍数)
|
|
161
|
+
}
|
|
162
|
+
|
|
148
163
|
# 列名映射
|
|
149
164
|
COLUMN_MAPPING = {
|
|
150
165
|
'日期': 'date',
|
{aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/indicators/report_generator.py
RENAMED
|
@@ -47,6 +47,10 @@ class ReportGenerator:
|
|
|
47
47
|
report.extend(self._generate_volatility_section(indicators))
|
|
48
48
|
report.extend(self._generate_fund_flow_section(indicators))
|
|
49
49
|
report.extend(self._generate_trend_strength_section(indicators))
|
|
50
|
+
report.extend(self._generate_spatial_temporal_section(indicators))
|
|
51
|
+
report.extend(self._generate_volume_comparison_section(indicators))
|
|
52
|
+
report.extend(self._generate_consecutive_days_section(indicators))
|
|
53
|
+
report.extend(self._generate_fibonacci_section(indicators))
|
|
50
54
|
report.extend(self._generate_summary_section(indicators))
|
|
51
55
|
|
|
52
56
|
# 最近5天OLHCV数据
|
|
@@ -63,7 +67,7 @@ class ReportGenerator:
|
|
|
63
67
|
header = []
|
|
64
68
|
header.append(self.config.REPORT_CONFIG['title_separator'])
|
|
65
69
|
header.append(f"股票代码:{stock_code}")
|
|
66
|
-
header.append(f"
|
|
70
|
+
header.append(f"数据日期:{indicators.get('date', 'N/A')}")
|
|
67
71
|
header.append(f"当前价格:{indicators.get('current_price', 0):.{self.config.DISPLAY_PRECISION['price']}f} 元")
|
|
68
72
|
header.append(self.config.REPORT_CONFIG['title_separator'])
|
|
69
73
|
return header
|
|
@@ -71,7 +75,7 @@ class ReportGenerator:
|
|
|
71
75
|
def _generate_basic_info_section(self, stock_info):
|
|
72
76
|
"""生成基本信息部分"""
|
|
73
77
|
section = []
|
|
74
|
-
section.append("\n
|
|
78
|
+
section.append("\n【一、股票基本信息】")
|
|
75
79
|
section.append(self.config.REPORT_CONFIG['section_separator'])
|
|
76
80
|
section.append(f"股票简称:{stock_info.get('股票简称', '未知')}")
|
|
77
81
|
section.append(f"所属行业:{stock_info.get('行业', '未知')}")
|
|
@@ -279,7 +283,7 @@ class ReportGenerator:
|
|
|
279
283
|
def _generate_summary_section(self, indicators):
|
|
280
284
|
"""生成指标状态汇总部分"""
|
|
281
285
|
section = []
|
|
282
|
-
section.append("\n
|
|
286
|
+
section.append("\n【十三、指标状态汇总】")
|
|
283
287
|
section.append(self.config.REPORT_CONFIG['section_separator'])
|
|
284
288
|
|
|
285
289
|
summary = self._collect_summary_items(indicators)
|
|
@@ -317,7 +321,7 @@ class ReportGenerator:
|
|
|
317
321
|
def _generate_ma_pattern_analysis(self, indicators):
|
|
318
322
|
"""生成均线形态分析"""
|
|
319
323
|
section = []
|
|
320
|
-
section.append("\n
|
|
324
|
+
section.append("\n均线形态统计:")
|
|
321
325
|
|
|
322
326
|
pattern_keys = ['trend_pattern', 'cross_pattern', 'position_pattern',
|
|
323
327
|
'arrangement_pattern', 'support_resistance']
|
|
@@ -511,7 +515,7 @@ class ReportGenerator:
|
|
|
511
515
|
def _generate_fund_flow_analysis(self, indicators, has_fund_data, has_5day_data):
|
|
512
516
|
"""生成资金流向分析"""
|
|
513
517
|
section = []
|
|
514
|
-
section.append("\n
|
|
518
|
+
section.append("\n资金流向统计:")
|
|
515
519
|
|
|
516
520
|
if has_fund_data:
|
|
517
521
|
section.extend(self._analyze_current_fund_flow(indicators))
|
|
@@ -790,7 +794,7 @@ class ReportGenerator:
|
|
|
790
794
|
def _generate_recent_ohlcv_section(self, stock_data):
|
|
791
795
|
"""生成最近5天OLHCV数据部分"""
|
|
792
796
|
section = []
|
|
793
|
-
section.append("\n
|
|
797
|
+
section.append("\n【十四、最近5天交易数据】")
|
|
794
798
|
section.append(self.config.REPORT_CONFIG['section_separator'])
|
|
795
799
|
|
|
796
800
|
# 获取最近5天数据
|
|
@@ -843,3 +847,138 @@ class ReportGenerator:
|
|
|
843
847
|
section.append(line)
|
|
844
848
|
|
|
845
849
|
return section
|
|
850
|
+
|
|
851
|
+
def _generate_spatial_temporal_section(self, indicators):
|
|
852
|
+
"""生成时空维度分析部分"""
|
|
853
|
+
section = []
|
|
854
|
+
section.append("\n【九、时空维度数据】")
|
|
855
|
+
section.append(self.config.REPORT_CONFIG['section_separator'])
|
|
856
|
+
|
|
857
|
+
# 距前高前低分析
|
|
858
|
+
if 'recent_high_price' in indicators:
|
|
859
|
+
section.append("距前高前低统计:")
|
|
860
|
+
section.append(f" 近{self.config.SPATIAL_TEMPORAL_CONFIG['high_low_lookback_days']}日前高: {indicators.get('recent_high_price', 0):.2f} 元 ({indicators.get('recent_high_date', 'N/A')})")
|
|
861
|
+
|
|
862
|
+
space_high = indicators.get('space_to_high_yuan', 0)
|
|
863
|
+
space_high_pct = indicators.get('space_to_high_pct', 0)
|
|
864
|
+
if space_high > 0:
|
|
865
|
+
section.append(f" 距前高空间: {space_high:.2f} 元 (+{space_high_pct:.2f}%)")
|
|
866
|
+
elif space_high < 0:
|
|
867
|
+
section.append(f" 距前高空间: {-space_high:.2f} 元 ({space_high_pct:.2f}%)")
|
|
868
|
+
else:
|
|
869
|
+
section.append(f" 距前高空间: {space_high:.2f} 元 ({space_high_pct:.2f}%)")
|
|
870
|
+
|
|
871
|
+
section.append(f" 近{self.config.SPATIAL_TEMPORAL_CONFIG['high_low_lookback_days']}日前低: {indicators.get('recent_low_price', 0):.2f} 元 ({indicators.get('recent_low_date', 'N/A')})")
|
|
872
|
+
|
|
873
|
+
gain_low = indicators.get('gain_from_low_yuan', 0)
|
|
874
|
+
gain_low_pct = indicators.get('gain_from_low_pct', 0)
|
|
875
|
+
if gain_low > 0:
|
|
876
|
+
section.append(f" 距前低涨幅: {gain_low:.2f} 元 (+{gain_low_pct:.2f}%)")
|
|
877
|
+
elif gain_low < 0:
|
|
878
|
+
section.append(f" 距前低跌幅: {-gain_low:.2f} 元 ({gain_low_pct:.2f}%)")
|
|
879
|
+
else:
|
|
880
|
+
section.append(f" 距前低涨幅: {gain_low:.2f} 元 ({gain_low_pct:.2f}%)")
|
|
881
|
+
|
|
882
|
+
return section
|
|
883
|
+
|
|
884
|
+
def _generate_volume_comparison_section(self, indicators):
|
|
885
|
+
"""生成量能环比分析部分"""
|
|
886
|
+
section = []
|
|
887
|
+
section.append("\n【十、量能环比数据】")
|
|
888
|
+
section.append(self.config.REPORT_CONFIG['section_separator'])
|
|
889
|
+
|
|
890
|
+
if 'volume_current_wan' in indicators:
|
|
891
|
+
section.append("成交量环比:")
|
|
892
|
+
section.append(f" 当日成交量: {indicators.get('volume_current_wan', 0):.1f} 万手")
|
|
893
|
+
|
|
894
|
+
if 'volume_yesterday_wan' in indicators:
|
|
895
|
+
yoy = indicators.get('volume_change_yoy', 0)
|
|
896
|
+
sign = '+' if yoy > 0 else ''
|
|
897
|
+
section.append(f" 前一交易日: {indicators.get('volume_yesterday_wan', 0):.1f} 万手 (环比: {sign}{yoy:.2f}%)")
|
|
898
|
+
|
|
899
|
+
if 'volume_5d_avg_wan' in indicators:
|
|
900
|
+
vs_5d = indicators.get('volume_vs_5d_avg', 0)
|
|
901
|
+
sign = '+' if vs_5d > 0 else ''
|
|
902
|
+
section.append(f" 5日均量: {indicators.get('volume_5d_avg_wan', 0):.1f} 万手 (较5日均量: {sign}{vs_5d:.2f}%)")
|
|
903
|
+
|
|
904
|
+
if 'volume_20d_avg_wan' in indicators:
|
|
905
|
+
vs_20d = indicators.get('volume_vs_20d_avg', 0)
|
|
906
|
+
sign = '+' if vs_20d > 0 else ''
|
|
907
|
+
section.append(f" 20日均量: {indicators.get('volume_20d_avg_wan', 0):.1f} 万手 (较20日均量: {sign}{vs_20d:.2f}%)")
|
|
908
|
+
|
|
909
|
+
if 'volume_level' in indicators:
|
|
910
|
+
section.append(f" 量能位置: {indicators.get('volume_level', '正常')}")
|
|
911
|
+
|
|
912
|
+
return section
|
|
913
|
+
|
|
914
|
+
def _generate_consecutive_days_section(self, indicators):
|
|
915
|
+
"""生成连续涨跌日统计部分"""
|
|
916
|
+
section = []
|
|
917
|
+
section.append("\n【十一、时间连续性统计】")
|
|
918
|
+
section.append(self.config.REPORT_CONFIG['section_separator'])
|
|
919
|
+
|
|
920
|
+
# 获取连续上涨和下跌数据
|
|
921
|
+
up_days = indicators.get('consecutive_up_days', 0)
|
|
922
|
+
down_days = indicators.get('consecutive_down_days', 0)
|
|
923
|
+
|
|
924
|
+
section.append("连续涨跌统计:")
|
|
925
|
+
|
|
926
|
+
if up_days > 0:
|
|
927
|
+
up_start = indicators.get('consecutive_up_start_price', 0)
|
|
928
|
+
up_gain = indicators.get('consecutive_up_gain_pct', 0)
|
|
929
|
+
current_price = indicators.get('current_price', 0)
|
|
930
|
+
sign = '+' if up_gain >= 0 else ''
|
|
931
|
+
section.append(f" 连续上涨日数: {up_days} 日")
|
|
932
|
+
section.append(f" 连续上涨涨幅: {sign}{up_gain:.2f}% ({up_start:.2f} → {current_price:.2f})")
|
|
933
|
+
elif down_days > 0:
|
|
934
|
+
down_start = indicators.get('consecutive_down_start_price', 0)
|
|
935
|
+
down_loss = indicators.get('consecutive_down_loss_pct', 0)
|
|
936
|
+
current_price = indicators.get('current_price', 0)
|
|
937
|
+
sign = '+' if down_loss >= 0 else ''
|
|
938
|
+
section.append(f" 连续下跌日数: {down_days} 日")
|
|
939
|
+
section.append(f" 连续下跌跌幅: {sign}{down_loss:.2f}% ({down_start:.2f} → {current_price:.2f})")
|
|
940
|
+
else:
|
|
941
|
+
section.append(f" 当前状态: 无明显连续涨跌")
|
|
942
|
+
|
|
943
|
+
return section
|
|
944
|
+
|
|
945
|
+
def _generate_fibonacci_section(self, indicators):
|
|
946
|
+
"""生成斐波那契时间窗口部分"""
|
|
947
|
+
section = []
|
|
948
|
+
section.append("\n【十二、斐波那契时间窗口】")
|
|
949
|
+
section.append(self.config.REPORT_CONFIG['section_separator'])
|
|
950
|
+
|
|
951
|
+
if 'fib_start_date' not in indicators:
|
|
952
|
+
section.append("暂无斐波那契窗口数据")
|
|
953
|
+
return section
|
|
954
|
+
|
|
955
|
+
section.append("斐波那契时间窗口:")
|
|
956
|
+
section.append(f" 计算起点: {indicators.get('fib_start_date', 'N/A')} (低点 {indicators.get('fib_start_price', 0):.2f} 元)")
|
|
957
|
+
section.append(f" 起点至今日: {indicators.get('days_from_start', 0)} 日")
|
|
958
|
+
|
|
959
|
+
# 解析窗口状态
|
|
960
|
+
fib_windows = indicators.get('fib_windows', {})
|
|
961
|
+
if fib_windows:
|
|
962
|
+
section.append(" 变盘窗口状态:")
|
|
963
|
+
|
|
964
|
+
fib_sequence = self.config.SPATIAL_TEMPORAL_CONFIG['fibonacci_sequence']
|
|
965
|
+
|
|
966
|
+
for fib_num in fib_sequence:
|
|
967
|
+
key = f'F{fib_num}'
|
|
968
|
+
if key in fib_windows:
|
|
969
|
+
status = fib_windows[key]
|
|
970
|
+
days_from_start = indicators.get('days_from_start', 0)
|
|
971
|
+
|
|
972
|
+
if status == 'passed':
|
|
973
|
+
section.append(f" {key}: 已通过")
|
|
974
|
+
elif status == 'future':
|
|
975
|
+
days_to = fib_num - days_from_start
|
|
976
|
+
section.append(f" {key}: 未来{days_to}日")
|
|
977
|
+
|
|
978
|
+
# 临近窗口预警
|
|
979
|
+
if indicators.get('fib_near_window', False):
|
|
980
|
+
nearest = indicators.get('fib_nearest_window', '')
|
|
981
|
+
section.append(f" ⚠️ 变盘预警: 当前距离{nearest}窗口,需关注变盘可能")
|
|
982
|
+
|
|
983
|
+
return section
|
|
984
|
+
|
{aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/AIShareTxt/indicators/technical_indicators.py
RENAMED
|
@@ -33,16 +33,16 @@ class TechnicalIndicators:
|
|
|
33
33
|
"""
|
|
34
34
|
if data is None or len(data) == 0:
|
|
35
35
|
return None
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
# 获取价格数据
|
|
38
38
|
close = data['close'].values
|
|
39
39
|
high = data['high'].values
|
|
40
40
|
low = data['low'].values
|
|
41
41
|
volume = data['volume'].values
|
|
42
42
|
open_price = data['open'].values
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
indicators = {}
|
|
45
|
-
|
|
45
|
+
|
|
46
46
|
# 处理各类指标
|
|
47
47
|
indicators.update(self._process_moving_averages(close))
|
|
48
48
|
indicators.update(self._process_ma_derived_indicators(close))
|
|
@@ -51,7 +51,7 @@ class TechnicalIndicators:
|
|
|
51
51
|
indicators.update(self._process_momentum_oscillators(high, low, close))
|
|
52
52
|
indicators.update(self._process_volatility_indicators(high, low, close))
|
|
53
53
|
indicators.update(self._process_volume_indicators(volume))
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
# 添加基础数据
|
|
56
56
|
indicators['current_price'] = close[-1]
|
|
57
57
|
# 将日期转换为字符串格式以便JSON序列化
|
|
@@ -60,7 +60,22 @@ class TechnicalIndicators:
|
|
|
60
60
|
indicators['date'] = date_value.strftime('%Y-%m-%d')
|
|
61
61
|
else:
|
|
62
62
|
indicators['date'] = str(date_value)
|
|
63
|
-
|
|
63
|
+
|
|
64
|
+
# 添加时空维度分析数据
|
|
65
|
+
spatial_temporal = self.calculate_spatial_temporal(data, close[-1])
|
|
66
|
+
if spatial_temporal:
|
|
67
|
+
indicators.update(spatial_temporal)
|
|
68
|
+
|
|
69
|
+
# 添加量能环比数据
|
|
70
|
+
volume_comparison = self.calculate_volume_comparison(data, volume[-1])
|
|
71
|
+
if volume_comparison:
|
|
72
|
+
indicators.update(volume_comparison)
|
|
73
|
+
|
|
74
|
+
# 添加连续涨跌日统计数据
|
|
75
|
+
consecutive_days = self.calculate_consecutive_days(data)
|
|
76
|
+
if consecutive_days:
|
|
77
|
+
indicators.update(consecutive_days)
|
|
78
|
+
|
|
64
79
|
return indicators
|
|
65
80
|
|
|
66
81
|
def _process_moving_averages(self, close):
|
|
@@ -540,3 +555,258 @@ class TechnicalIndicators:
|
|
|
540
555
|
except Exception as e:
|
|
541
556
|
print(f"OBV背离检测错误: {e}")
|
|
542
557
|
return "背离检测失败"
|
|
558
|
+
|
|
559
|
+
def calculate_spatial_temporal(self, df: pd.DataFrame, current_price: float) -> dict:
|
|
560
|
+
"""
|
|
561
|
+
计算时空维度分析数据
|
|
562
|
+
|
|
563
|
+
Args:
|
|
564
|
+
df: 历史数据DataFrame
|
|
565
|
+
current_price: 当前价格
|
|
566
|
+
|
|
567
|
+
Returns:
|
|
568
|
+
dict: 包含前高前低、斐波那契窗口、连续涨跌日等数据
|
|
569
|
+
"""
|
|
570
|
+
result = {}
|
|
571
|
+
config = self.config.SPATIAL_TEMPORAL_CONFIG
|
|
572
|
+
|
|
573
|
+
try:
|
|
574
|
+
# 确保有足够的数据
|
|
575
|
+
lookback_days = min(config['high_low_lookback_days'], len(df) - 1)
|
|
576
|
+
if lookback_days < 5:
|
|
577
|
+
return result
|
|
578
|
+
|
|
579
|
+
# 获取回溯范围内的数据
|
|
580
|
+
recent_data = df.iloc[-lookback_days:]
|
|
581
|
+
|
|
582
|
+
# 1. 计算距前高前低的空间分析
|
|
583
|
+
high_idx = recent_data['high'].idxmax()
|
|
584
|
+
low_idx = recent_data['low'].idxmin()
|
|
585
|
+
|
|
586
|
+
# 前高分析
|
|
587
|
+
result['recent_high_price'] = float(recent_data['high'].max())
|
|
588
|
+
high_date = df.loc[high_idx, 'date'] if 'date' in df.columns else high_idx
|
|
589
|
+
result['recent_high_date'] = high_date.strftime('%Y-%m-%d') if hasattr(high_date, 'strftime') else str(high_date)
|
|
590
|
+
result['space_to_high_yuan'] = round(result['recent_high_price'] - current_price, 2)
|
|
591
|
+
result['space_to_high_pct'] = round((result['recent_high_price'] / current_price - 1) * 100, 2)
|
|
592
|
+
|
|
593
|
+
# 前低分析
|
|
594
|
+
result['recent_low_price'] = float(recent_data['low'].min())
|
|
595
|
+
low_date = df.loc[low_idx, 'date'] if 'date' in df.columns else low_idx
|
|
596
|
+
result['recent_low_date'] = low_date.strftime('%Y-%m-%d') if hasattr(low_date, 'strftime') else str(low_date)
|
|
597
|
+
result['gain_from_low_yuan'] = round(current_price - result['recent_low_price'], 2)
|
|
598
|
+
result['gain_from_low_pct'] = round((current_price / result['recent_low_price'] - 1) * 100, 2)
|
|
599
|
+
|
|
600
|
+
# 2. 斐波那契时间窗口分析
|
|
601
|
+
fib_result = self._calculate_fibonacci_windows(df, current_price)
|
|
602
|
+
result.update(fib_result)
|
|
603
|
+
|
|
604
|
+
except Exception as e:
|
|
605
|
+
self.logger.warning(f"时空维度分析计算失败:{str(e)}")
|
|
606
|
+
|
|
607
|
+
return result
|
|
608
|
+
|
|
609
|
+
def _calculate_fibonacci_windows(self, df: pd.DataFrame, current_price: float) -> dict:
|
|
610
|
+
"""
|
|
611
|
+
计算斐波那契时间窗口
|
|
612
|
+
|
|
613
|
+
Args:
|
|
614
|
+
df: 历史数据DataFrame
|
|
615
|
+
current_price: 当前价格
|
|
616
|
+
|
|
617
|
+
Returns:
|
|
618
|
+
dict: 包含斐波那契窗口分析数据
|
|
619
|
+
"""
|
|
620
|
+
result = {}
|
|
621
|
+
config = self.config.SPATIAL_TEMPORAL_CONFIG
|
|
622
|
+
|
|
623
|
+
try:
|
|
624
|
+
# 获取斐波那契数列
|
|
625
|
+
fib_sequence = config['fibonacci_sequence']
|
|
626
|
+
window_threshold = config['fibonacci_window_threshold']
|
|
627
|
+
|
|
628
|
+
# 寻找趋势起点(近期重要低点)
|
|
629
|
+
close_data = df['close'].values
|
|
630
|
+
n = len(close_data)
|
|
631
|
+
|
|
632
|
+
if n < 10:
|
|
633
|
+
return result
|
|
634
|
+
|
|
635
|
+
# 使用局部极值检测寻找近期低点(取最近30个交易日内)
|
|
636
|
+
search_range = min(30, n // 2)
|
|
637
|
+
recent_close = close_data[-search_range:]
|
|
638
|
+
|
|
639
|
+
# 找到最低点作为起点
|
|
640
|
+
min_idx_in_recent = np.argmin(recent_close)
|
|
641
|
+
fib_start_idx = n - search_range + min_idx_in_recent
|
|
642
|
+
|
|
643
|
+
fib_start_price = float(close_data[fib_start_idx])
|
|
644
|
+
fib_start_date = df.loc[fib_start_idx, 'date'] if 'date' in df.columns else fib_start_idx
|
|
645
|
+
fib_start_date_str = fib_start_date.strftime('%Y-%m-%d') if hasattr(fib_start_date, 'strftime') else str(fib_start_date)
|
|
646
|
+
|
|
647
|
+
# 计算起点至今的天数
|
|
648
|
+
days_from_start = n - 1 - fib_start_idx
|
|
649
|
+
|
|
650
|
+
result['fib_start_date'] = fib_start_date_str
|
|
651
|
+
result['fib_start_price'] = round(fib_start_price, 2)
|
|
652
|
+
result['days_from_start'] = int(days_from_start)
|
|
653
|
+
|
|
654
|
+
# 计算各斐波那契窗口状态
|
|
655
|
+
fib_windows = {}
|
|
656
|
+
nearest_window = None
|
|
657
|
+
nearest_distance = float('inf')
|
|
658
|
+
|
|
659
|
+
for fib_num in fib_sequence:
|
|
660
|
+
if days_from_start >= fib_num:
|
|
661
|
+
# 已通过的窗口
|
|
662
|
+
fib_windows[f'F{fib_num}'] = 'passed'
|
|
663
|
+
else:
|
|
664
|
+
# 未来的窗口
|
|
665
|
+
days_to_window = fib_num - days_from_start
|
|
666
|
+
if days_to_window <= nearest_distance:
|
|
667
|
+
nearest_distance = days_to_window
|
|
668
|
+
nearest_window = f'F{fib_num}'
|
|
669
|
+
fib_windows[f'F{fib_num}'] = 'future'
|
|
670
|
+
|
|
671
|
+
result['fib_windows'] = fib_windows
|
|
672
|
+
|
|
673
|
+
# 判断是否临近窗口
|
|
674
|
+
is_near_window = nearest_distance <= window_threshold
|
|
675
|
+
result['fib_near_window'] = bool(is_near_window)
|
|
676
|
+
|
|
677
|
+
if nearest_window:
|
|
678
|
+
result['fib_nearest_window'] = f'{nearest_window}(距{nearest_distance}日)'
|
|
679
|
+
|
|
680
|
+
except Exception as e:
|
|
681
|
+
self.logger.warning(f"斐波那契窗口计算失败:{str(e)}")
|
|
682
|
+
|
|
683
|
+
return result
|
|
684
|
+
|
|
685
|
+
def calculate_volume_comparison(self, df: pd.DataFrame, current_volume: float) -> dict:
|
|
686
|
+
"""
|
|
687
|
+
计算成交量环比数据
|
|
688
|
+
|
|
689
|
+
Args:
|
|
690
|
+
df: 历史数据DataFrame
|
|
691
|
+
current_volume: 当日成交量(手)
|
|
692
|
+
|
|
693
|
+
Returns:
|
|
694
|
+
dict: 包含较前日、5日均量、20日均量的环比数据
|
|
695
|
+
"""
|
|
696
|
+
result = {}
|
|
697
|
+
config = self.config.VOLUME_COMPARISON_CONFIG
|
|
698
|
+
|
|
699
|
+
try:
|
|
700
|
+
volume_data = df['volume'].values
|
|
701
|
+
n = len(volume_data)
|
|
702
|
+
|
|
703
|
+
if n < 2:
|
|
704
|
+
return result
|
|
705
|
+
|
|
706
|
+
# 当日成交量(转换为万手)
|
|
707
|
+
result['volume_current_wan'] = round(current_volume / 10000, 1)
|
|
708
|
+
|
|
709
|
+
# 前一日成交量
|
|
710
|
+
result['volume_yesterday_wan'] = round(volume_data[-2] / 10000, 1)
|
|
711
|
+
result['volume_change_yoy'] = round((current_volume / volume_data[-2] - 1) * 100, 2)
|
|
712
|
+
|
|
713
|
+
# 5日均量
|
|
714
|
+
short_period = config['short_ma_period']
|
|
715
|
+
if n >= short_period + 1:
|
|
716
|
+
volume_5d_avg = np.mean(volume_data[-short_period-1:-1])
|
|
717
|
+
result['volume_5d_avg_wan'] = round(volume_5d_avg / 10000, 1)
|
|
718
|
+
result['volume_vs_5d_avg'] = round((current_volume / volume_5d_avg - 1) * 100, 2)
|
|
719
|
+
|
|
720
|
+
# 20日均量
|
|
721
|
+
medium_period = config['medium_ma_period']
|
|
722
|
+
if n >= medium_period + 1:
|
|
723
|
+
volume_20d_avg = np.mean(volume_data[-medium_period-1:-1])
|
|
724
|
+
result['volume_20d_avg_wan'] = round(volume_20d_avg / 10000, 1)
|
|
725
|
+
result['volume_vs_20d_avg'] = round((current_volume / volume_20d_avg - 1) * 100, 2)
|
|
726
|
+
|
|
727
|
+
# 量能位置评级
|
|
728
|
+
if 'volume_5d_avg_wan' in result and result['volume_5d_avg_wan'] > 0:
|
|
729
|
+
ratio = current_volume / volume_5d_avg if volume_5d_avg > 0 else 1
|
|
730
|
+
if ratio >= config['high_volume_threshold']:
|
|
731
|
+
result['volume_level'] = '高位'
|
|
732
|
+
elif ratio <= config['low_volume_threshold']:
|
|
733
|
+
result['volume_level'] = '低位'
|
|
734
|
+
else:
|
|
735
|
+
result['volume_level'] = '正常'
|
|
736
|
+
else:
|
|
737
|
+
result['volume_level'] = '正常'
|
|
738
|
+
|
|
739
|
+
except Exception as e:
|
|
740
|
+
self.logger.warning(f"量能环比计算失败:{str(e)}")
|
|
741
|
+
|
|
742
|
+
return result
|
|
743
|
+
|
|
744
|
+
def calculate_consecutive_days(self, df: pd.DataFrame) -> dict:
|
|
745
|
+
"""
|
|
746
|
+
计算连续涨跌日统计数据
|
|
747
|
+
|
|
748
|
+
Args:
|
|
749
|
+
df: 历史数据DataFrame
|
|
750
|
+
|
|
751
|
+
Returns:
|
|
752
|
+
dict: 包含连续上涨日数、连续下跌日数、期间涨跌幅等数据
|
|
753
|
+
"""
|
|
754
|
+
result = {}
|
|
755
|
+
|
|
756
|
+
try:
|
|
757
|
+
close_data = df['close'].values
|
|
758
|
+
n = len(close_data)
|
|
759
|
+
|
|
760
|
+
if n < 2:
|
|
761
|
+
return result
|
|
762
|
+
|
|
763
|
+
# 计算每日涨跌(1为涨,-1为跌,0为平)
|
|
764
|
+
changes = np.diff(close_data)
|
|
765
|
+
|
|
766
|
+
# 统计连续上涨日数(从最新日期往前数)
|
|
767
|
+
consecutive_up = 0
|
|
768
|
+
consecutive_up_start_price = close_data[-1]
|
|
769
|
+
for i in range(len(changes) - 1, -1, -1):
|
|
770
|
+
if changes[i] > 0:
|
|
771
|
+
consecutive_up += 1
|
|
772
|
+
elif changes[i] < 0:
|
|
773
|
+
break
|
|
774
|
+
# 如果涨跌幅为0,继续往前数
|
|
775
|
+
|
|
776
|
+
if consecutive_up > 0:
|
|
777
|
+
consecutive_up_start_idx = n - 1 - consecutive_up
|
|
778
|
+
consecutive_up_start_price = close_data[consecutive_up_start_idx] if consecutive_up_start_idx >= 0 else close_data[0]
|
|
779
|
+
consecutive_up_gain = ((close_data[-1] - consecutive_up_start_price) / consecutive_up_start_price * 100) if consecutive_up_start_price > 0 else 0
|
|
780
|
+
result['consecutive_up_days'] = consecutive_up
|
|
781
|
+
result['consecutive_up_start_price'] = round(consecutive_up_start_price, 2)
|
|
782
|
+
result['consecutive_up_gain_pct'] = round(consecutive_up_gain, 2)
|
|
783
|
+
else:
|
|
784
|
+
result['consecutive_up_days'] = 0
|
|
785
|
+
result['consecutive_up_start_price'] = round(close_data[-1], 2)
|
|
786
|
+
result['consecutive_up_gain_pct'] = 0.0
|
|
787
|
+
|
|
788
|
+
# 统计连续下跌日数(从最新日期往前数)
|
|
789
|
+
consecutive_down = 0
|
|
790
|
+
for i in range(len(changes) - 1, -1, -1):
|
|
791
|
+
if changes[i] < 0:
|
|
792
|
+
consecutive_down += 1
|
|
793
|
+
elif changes[i] > 0:
|
|
794
|
+
break
|
|
795
|
+
# 如果涨跌幅为0,继续往前数
|
|
796
|
+
|
|
797
|
+
if consecutive_down > 0:
|
|
798
|
+
consecutive_down_start_idx = n - 1 - consecutive_down
|
|
799
|
+
consecutive_down_start_price = close_data[consecutive_down_start_idx] if consecutive_down_start_idx >= 0 else close_data[0]
|
|
800
|
+
consecutive_down_loss = ((close_data[-1] - consecutive_down_start_price) / consecutive_down_start_price * 100) if consecutive_down_start_price > 0 else 0
|
|
801
|
+
result['consecutive_down_days'] = consecutive_down
|
|
802
|
+
result['consecutive_down_start_price'] = round(consecutive_down_start_price, 2)
|
|
803
|
+
result['consecutive_down_loss_pct'] = round(consecutive_down_loss, 2)
|
|
804
|
+
else:
|
|
805
|
+
result['consecutive_down_days'] = 0
|
|
806
|
+
result['consecutive_down_start_price'] = round(close_data[-1], 2)
|
|
807
|
+
result['consecutive_down_loss_pct'] = 0.0
|
|
808
|
+
|
|
809
|
+
except Exception as e:
|
|
810
|
+
self.logger.warning(f"连续涨跌日统计计算失败:{str(e)}")
|
|
811
|
+
|
|
812
|
+
return result
|
|
@@ -42,7 +42,7 @@ dev_requires = read_requirements("requirements-dev.txt")
|
|
|
42
42
|
|
|
43
43
|
setup(
|
|
44
44
|
name="aishare-txt",
|
|
45
|
-
version="
|
|
45
|
+
version="2026.02.06.38",
|
|
46
46
|
author="AIShareTxt Team",
|
|
47
47
|
author_email="chaofanat@gmail.com",
|
|
48
48
|
description="中国股票技术指标文本生成工具包,用于为金融分析相关领域的AI智能体提供上下文服务。",
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aishare_txt-2025.11.27.36 → aishare_txt-2026.2.6.38}/aishare_txt.egg-info/dependency_links.txt
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
|