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,783 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ 报告生成模块
5
+ 负责格式化和生成股票分析报告
6
+ """
7
+
8
+ from .config import IndicatorConfig as Config
9
+
10
+
11
+ class ReportGenerator:
12
+ """报告生成器"""
13
+
14
+ def __init__(self):
15
+ self.config = Config()
16
+
17
+ def generate_report(self, stock_code, indicators, stock_info=None):
18
+ """
19
+ 生成完整的股票分析报告
20
+
21
+ Args:
22
+ stock_code (str): 股票代码
23
+ indicators (dict): 指标数据字典
24
+ stock_info (dict): 股票基本信息
25
+
26
+ Returns:
27
+ str: 格式化的报告文本
28
+ """
29
+ if indicators is None:
30
+ return "无法生成指标报告:数据不足"
31
+
32
+ report = []
33
+
34
+ # 报告头部
35
+ report.extend(self._generate_header(stock_code, indicators))
36
+
37
+ # 股票基本信息
38
+ if stock_info:
39
+ report.extend(self._generate_basic_info_section(stock_info))
40
+
41
+ # 技术指标各个部分
42
+ report.extend(self._generate_ma_section(indicators))
43
+ report.extend(self._generate_ma_derived_section(indicators))
44
+ report.extend(self._generate_volume_price_section(indicators))
45
+ report.extend(self._generate_momentum_section(indicators))
46
+ report.extend(self._generate_volatility_section(indicators))
47
+ report.extend(self._generate_fund_flow_section(indicators))
48
+ report.extend(self._generate_trend_strength_section(indicators))
49
+ report.extend(self._generate_summary_section(indicators))
50
+
51
+ # 报告尾部
52
+ report.extend(self._generate_footer())
53
+
54
+ return "\n".join(report)
55
+
56
+ def _generate_header(self, stock_code, indicators):
57
+ """生成报告头部"""
58
+ header = []
59
+ header.append(self.config.REPORT_CONFIG['title_separator'])
60
+ header.append(f"股票代码:{stock_code}")
61
+ header.append(f"分析日期:{indicators.get('date', 'N/A')}")
62
+ header.append(f"当前价格:{indicators.get('current_price', 0):.{self.config.DISPLAY_PRECISION['price']}f} 元")
63
+ header.append(self.config.REPORT_CONFIG['title_separator'])
64
+ return header
65
+
66
+ def _generate_basic_info_section(self, stock_info):
67
+ """生成基本信息部分"""
68
+ section = []
69
+ section.append("\n【股票基本信息】")
70
+ section.append(self.config.REPORT_CONFIG['section_separator'])
71
+ section.append(f"股票简称:{stock_info.get('股票简称', '未知')}")
72
+ section.append(f"所属行业:{stock_info.get('行业', '未知')}")
73
+
74
+ # 股本信息
75
+ if stock_info.get('总股本_亿股', 0) > 0:
76
+ section.append(f"总股本: {stock_info.get('总股本_亿股', 0):.{self.config.DISPLAY_PRECISION['ratio']}f} 亿股")
77
+ if stock_info.get('流通股本_亿股', 0) > 0:
78
+ section.append(f"流通股本:{stock_info.get('流通股本_亿股', 0):.{self.config.DISPLAY_PRECISION['ratio']}f} 亿股")
79
+
80
+ # 市值信息
81
+ if stock_info.get('总市值_亿', 0) > 0:
82
+ section.append(f"总市值: {stock_info.get('总市值_亿', 0):.{self.config.DISPLAY_PRECISION['ratio']}f} 亿元")
83
+ if stock_info.get('流通市值_亿', 0) > 0:
84
+ section.append(f"流通市值:{stock_info.get('流通市值_亿', 0):.{self.config.DISPLAY_PRECISION['ratio']}f} 亿元")
85
+
86
+ # 估值信息
87
+ pe_ratio = stock_info.get('市盈率', '未知')
88
+ pb_ratio = stock_info.get('市净率', '未知')
89
+ if pe_ratio != '未知' and pe_ratio != '-':
90
+ section.append(f"市盈率: {pe_ratio}")
91
+ if pb_ratio != '未知' and pb_ratio != '-':
92
+ section.append(f"市净率: {pb_ratio}")
93
+
94
+ # 市值规模判断
95
+ section.append(f"市值规模:{self._get_market_cap_category(stock_info)}")
96
+
97
+ return section
98
+
99
+ def _generate_ma_section(self, indicators):
100
+ """生成均线指标部分"""
101
+ section = []
102
+ section.append("\n【二、核心均线指标】")
103
+ section.append(self.config.REPORT_CONFIG['section_separator'])
104
+
105
+ # 移动平均线
106
+ if 'MA_5' in indicators:
107
+ section.append("移动平均线 (MA) - 标准指标:")
108
+ for period in self.config.MA_PERIODS['short']:
109
+ section.append(f" MA{period}: {indicators.get(f'MA_{period}', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
110
+ for period in self.config.MA_PERIODS['medium']:
111
+ section.append(f" MA{period}: {indicators.get(f'MA_{period}', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
112
+ for period in self.config.MA_PERIODS['long']:
113
+ section.append(f" MA{period}: {indicators.get(f'MA_{period}', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
114
+
115
+ # 均线形态分析
116
+ section.extend(self._generate_ma_pattern_analysis(indicators))
117
+
118
+ # EMA
119
+ if 'EMA_5' in indicators:
120
+ section.append("\n指数移动平均线 (EMA):")
121
+ for period in self.config.EMA_PERIODS:
122
+ section.append(f" EMA{period}: {indicators.get(f'EMA_{period}', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
123
+
124
+ # EMA位置关系
125
+ section.append(f" EMA关系: {self._get_ema_relationship(indicators)}")
126
+
127
+ # WMA
128
+ if 'WMA_10' in indicators:
129
+ section.append("\n加权移动平均线 (WMA):")
130
+ for period in self.config.WMA_PERIODS:
131
+ section.append(f" WMA{period}: {indicators.get(f'WMA_{period}', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
132
+
133
+ return section
134
+
135
+ def _generate_ma_derived_section(self, indicators):
136
+ """生成均线衍生指标部分"""
137
+ section = []
138
+ section.append("\n【三、均线衍生指标】")
139
+ section.append(self.config.REPORT_CONFIG['section_separator'])
140
+
141
+ # BIAS乖离率
142
+ if 'BIAS_5' in indicators:
143
+ section.append("乖离率 (BIAS):")
144
+ for period in self.config.BIAS_PERIODS:
145
+ section.append(f" {period}日BIAS: {indicators.get(f'BIAS_{period}', 0):.{self.config.DISPLAY_PRECISION['percentage']}f}%")
146
+
147
+ # MACD
148
+ if 'MACD_DIF' in indicators:
149
+ section.append("\nMACD 指标:")
150
+ section.append(f" MACD DIF: {indicators.get('MACD_DIF', 0):.{self.config.DISPLAY_PRECISION['macd']}f}")
151
+ section.append(f" MACD DEA: {indicators.get('MACD_DEA', 0):.{self.config.DISPLAY_PRECISION['macd']}f}")
152
+ section.append(f" MACD柱: {indicators.get('MACD_HIST', 0):.{self.config.DISPLAY_PRECISION['macd']}f}")
153
+ section.append(f" MACD状态: {self._get_macd_status(indicators)}")
154
+
155
+ # 布林带
156
+ if 'BB_UPPER' in indicators:
157
+ section.append("\n布林带 (Bollinger Bands):")
158
+ section.append(f" 上轨: {indicators.get('BB_UPPER', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
159
+ section.append(f" 中轨: {indicators.get('BB_MIDDLE', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
160
+ section.append(f" 下轨: {indicators.get('BB_LOWER', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
161
+ section.append(f" 带宽: {indicators.get('BB_WIDTH', 0):.{self.config.DISPLAY_PRECISION['percentage']}f}%")
162
+ section.append(f" 价格位置: {self._get_bb_position(indicators)}")
163
+
164
+ return section
165
+
166
+ def _generate_volume_price_section(self, indicators):
167
+ """生成量价指标部分"""
168
+ section = []
169
+ section.append("\n【四、量价辅助指标】")
170
+ section.append(self.config.REPORT_CONFIG['section_separator'])
171
+
172
+ # VWAP
173
+ if 'VWAP_14' in indicators:
174
+ section.append("成交量加权平均价 (VWAP):")
175
+ section.append(f" 14日VWAP: {indicators.get('VWAP_14', 0):.{self.config.DISPLAY_PRECISION['price']}f}")
176
+ section.append(f" 价格关系: {self._get_vwap_relationship(indicators)}")
177
+
178
+ # OBV
179
+ if 'OBV_current' in indicators:
180
+ section.extend(self._generate_obv_analysis(indicators))
181
+
182
+ # 成交量指标
183
+ if 'VOLUME_RATIO' in indicators:
184
+ section.extend(self._generate_volume_analysis(indicators))
185
+
186
+ return section
187
+
188
+ def _generate_momentum_section(self, indicators):
189
+ """生成动量振荡指标部分"""
190
+ section = []
191
+ section.append("\n【五、动量振荡指标】")
192
+ section.append(self.config.REPORT_CONFIG['section_separator'])
193
+
194
+ # RSI
195
+ if 'RSI_14' in indicators:
196
+ section.append("相对强弱指数 (RSI):")
197
+ for period in self.config.RSI_PERIODS:
198
+ section.append(f" RSI({period}): {indicators.get(f'RSI_{period}', 0):.{self.config.DISPLAY_PRECISION['ratio']}f}")
199
+ section.append(f" RSI区间: {self._get_rsi_zone(indicators.get('RSI_14', 0))}")
200
+
201
+ # KD指标
202
+ if 'STOCH_K' in indicators:
203
+ section.append("\n随机振荡器 (KD指标):")
204
+ section.append(f" K值: {indicators.get('STOCH_K', 0):.{self.config.DISPLAY_PRECISION['ratio']}f}")
205
+ section.append(f" D值: {indicators.get('STOCH_D', 0):.{self.config.DISPLAY_PRECISION['ratio']}f}")
206
+ section.append(f" K-D差值: {indicators.get('STOCH_KD_DIFF', 0):.{self.config.DISPLAY_PRECISION['ratio']}f}")
207
+ section.append(f" KD关系: {self._get_kd_relationship(indicators)}")
208
+
209
+ return section
210
+
211
+ def _generate_volatility_section(self, indicators):
212
+ """生成波动率指标部分"""
213
+ section = []
214
+ section.append("\n【六、波动率指标】")
215
+ section.append(self.config.REPORT_CONFIG['section_separator'])
216
+
217
+ if 'ATR_14' in indicators:
218
+ section.append("平均真实波幅 (ATR):")
219
+ section.append(f" ATR(14): {indicators.get('ATR_14', 0):.{self.config.DISPLAY_PRECISION['atr']}f}")
220
+ section.append(f" ATR比率: {indicators.get('ATR_RATIO', 0):.{self.config.DISPLAY_PRECISION['percentage']}f}%")
221
+ section.append(f" 波动等级: {self._get_volatility_level(indicators.get('ATR_RATIO', 0))}")
222
+
223
+ return section
224
+
225
+ def _generate_fund_flow_section(self, indicators):
226
+ """生成主力资金流部分"""
227
+ section = []
228
+ section.append("\n【七、主力资金流指标】")
229
+ section.append(self.config.REPORT_CONFIG['section_separator'])
230
+
231
+ has_fund_data = '主力净流入额' in indicators
232
+ has_5day_data = '5日主力净流入额' in indicators
233
+
234
+ if has_fund_data or has_5day_data:
235
+ section.append("主力资金流向:")
236
+
237
+ # 最新日期的资金流数据
238
+ if has_fund_data:
239
+ section.extend(self._generate_daily_fund_flow(indicators))
240
+
241
+ # 5日累计数据
242
+ if has_5day_data:
243
+ section.extend(self._generate_5day_fund_flow(indicators))
244
+
245
+ # 资金流向分析
246
+ section.extend(self._generate_fund_flow_analysis(indicators, has_fund_data, has_5day_data))
247
+ else:
248
+ section.append("暂无主力资金流数据")
249
+
250
+ return section
251
+
252
+ def _generate_trend_strength_section(self, indicators):
253
+ """生成趋势强度指标部分"""
254
+ section = []
255
+ section.append("\n【八、趋势强度指标】")
256
+ section.append(self.config.REPORT_CONFIG['section_separator'])
257
+
258
+ if 'ADX' in indicators:
259
+ section.append("趋势方向指标 (DMI/ADX):")
260
+ section.append(f" ADX值: {indicators.get('ADX', 0):.{self.config.DISPLAY_PRECISION['ratio']}f}")
261
+
262
+ if 'DI_PLUS' in indicators and 'DI_MINUS' in indicators:
263
+ section.append(f" DI+值: {indicators.get('DI_PLUS', 0):.{self.config.DISPLAY_PRECISION['ratio']}f}")
264
+ section.append(f" DI-值: {indicators.get('DI_MINUS', 0):.{self.config.DISPLAY_PRECISION['ratio']}f}")
265
+ section.append(f" DI关系: {self._get_di_relationship(indicators)}")
266
+
267
+ section.append(f" ADX范围: {self._get_adx_range(indicators.get('ADX', 0))}")
268
+
269
+ if 'ADX_TREND' in indicators:
270
+ section.extend(self._generate_adx_trend_analysis(indicators))
271
+
272
+ return section
273
+
274
+ def _generate_summary_section(self, indicators):
275
+ """生成指标状态汇总部分"""
276
+ section = []
277
+ section.append("\n【九、指标状态汇总】")
278
+ section.append(self.config.REPORT_CONFIG['section_separator'])
279
+
280
+ summary = self._collect_summary_items(indicators)
281
+
282
+ for item in summary:
283
+ section.append(f"• {item}")
284
+
285
+ return section
286
+
287
+ def _generate_footer(self):
288
+ """生成报告尾部"""
289
+ footer = []
290
+ footer.append("\n" + self.config.REPORT_CONFIG['title_separator'])
291
+ return footer
292
+
293
+ # 辅助方法
294
+ def _get_market_cap_category(self, stock_info):
295
+ """获取市值规模分类"""
296
+ total_market_cap = stock_info.get('总市值_亿', 0)
297
+ levels = self.config.MARKET_CAP_LEVELS
298
+
299
+ if total_market_cap >= levels['mega']:
300
+ return f"超大盘股(≥{levels['mega']}亿)"
301
+ elif total_market_cap >= levels['large']:
302
+ return f"大盘股({levels['large']}-{levels['mega']}亿)"
303
+ elif total_market_cap >= levels['medium']:
304
+ return f"中盘股({levels['medium']}-{levels['large']}亿)"
305
+ elif total_market_cap >= levels['small_medium']:
306
+ return f"中小盘股({levels['small_medium']}-{levels['medium']}亿)"
307
+ elif total_market_cap > 0:
308
+ return f"小盘股(<{levels['small_medium']}亿)"
309
+ else:
310
+ return "市值未知"
311
+
312
+ def _generate_ma_pattern_analysis(self, indicators):
313
+ """生成均线形态分析"""
314
+ section = []
315
+ section.append("\n均线形态分析:")
316
+
317
+ pattern_keys = ['trend_pattern', 'cross_pattern', 'position_pattern',
318
+ 'arrangement_pattern', 'support_resistance']
319
+ pattern_names = ['趋势形态', '交叉形态', '位置形态', '排列形态', '均线接近']
320
+
321
+ for key, name in zip(pattern_keys, pattern_names):
322
+ if key in indicators and indicators[key]:
323
+ section.append(f" {name}: {indicators[key]}")
324
+
325
+ return section
326
+
327
+ def _get_ema_relationship(self, indicators):
328
+ """获取EMA关系"""
329
+ ema5 = indicators.get('EMA_5', 0)
330
+ ema10 = indicators.get('EMA_10', 0)
331
+ return "EMA5>EMA10" if ema5 > ema10 else "EMA5<EMA10"
332
+
333
+ def _get_macd_status(self, indicators):
334
+ """获取MACD状态"""
335
+ macd_hist = indicators.get('MACD_HIST', 0)
336
+ if macd_hist > 0:
337
+ return "HIST>0"
338
+ elif macd_hist < 0:
339
+ return "HIST<0"
340
+ else:
341
+ return "HIST=0"
342
+
343
+ def _get_bb_position(self, indicators):
344
+ """获取布林带位置"""
345
+ current_price = indicators.get('current_price', 0)
346
+ bb_upper = indicators.get('BB_UPPER', 0)
347
+ bb_lower = indicators.get('BB_LOWER', 0)
348
+
349
+ if current_price > bb_upper:
350
+ return "价格>上轨"
351
+ elif current_price < bb_lower:
352
+ return "价格<下轨"
353
+ else:
354
+ return "价格在带内"
355
+
356
+ def _get_vwap_relationship(self, indicators):
357
+ """获取VWAP关系"""
358
+ current_price = indicators.get('current_price', 0)
359
+ vwap = indicators.get('VWAP_14', 0)
360
+ return "价格>VWAP" if current_price > vwap else "价格<VWAP"
361
+
362
+ def _generate_obv_analysis(self, indicators):
363
+ """生成OBV分析"""
364
+ section = []
365
+ section.append("\n能量潮 (OBV):")
366
+ section.append(f" 当前OBV: {indicators.get('OBV_current', 0):.{self.config.DISPLAY_PRECISION['volume_wan']}f}")
367
+ section.append(f" 5日变化: {indicators.get('OBV_5d_change', 0):+.{self.config.DISPLAY_PRECISION['percentage']}f}%")
368
+ section.append(f" 20日变化: {indicators.get('OBV_20d_change', 0):+.{self.config.DISPLAY_PRECISION['percentage']}f}%")
369
+
370
+ # OBV变化方向
371
+ obv_5d_change = indicators.get('OBV_5d_change', 0)
372
+ if obv_5d_change > 0:
373
+ obv_signal = "5日OBV上升"
374
+ elif obv_5d_change < 0:
375
+ obv_signal = "5日OBV下降"
376
+ else:
377
+ obv_signal = "5日OBV平稳"
378
+ section.append(f" OBV趋势: {obv_signal}")
379
+
380
+ # OBV斜率
381
+ obv_trend = indicators.get('OBV_trend', 0)
382
+ if obv_trend:
383
+ volume_price_signal = "OBV斜率为正" if obv_trend > 0 else "OBV斜率为负"
384
+ section.append(f" OBV斜率: {volume_price_signal}")
385
+
386
+ # OBV背离分析
387
+ if 'OBV_DIVERGENCE' in indicators:
388
+ section.append(f" OBV背离: {indicators['OBV_DIVERGENCE']}")
389
+
390
+ return section
391
+
392
+ def _generate_volume_analysis(self, indicators):
393
+ """生成成交量分析"""
394
+ section = []
395
+ section.append("\n量比指标:")
396
+ volume_ratio = indicators.get('VOLUME_RATIO', 0)
397
+ section.append(f" 量比: {volume_ratio:.{self.config.DISPLAY_PRECISION['ratio']}f}")
398
+ section.append(f" 成交量: {self._get_volume_level(volume_ratio)}")
399
+
400
+ if 'VOLUME_TREND' in indicators:
401
+ vol_trend = indicators.get('VOLUME_TREND', 0)
402
+ vol_trend_desc = self._get_volume_trend_description(vol_trend)
403
+ section.append(f" 量能趋势: {vol_trend_desc}")
404
+
405
+ return section
406
+
407
+ def _get_rsi_zone(self, rsi_value):
408
+ """获取RSI区间"""
409
+ zones = self.config.RSI_ZONES
410
+ if rsi_value >= zones['overbought']:
411
+ return f"RSI≥{zones['overbought']}"
412
+ elif rsi_value >= zones['neutral_high']:
413
+ return f"{zones['neutral_high']}≤RSI<{zones['overbought']}"
414
+ elif rsi_value >= zones['neutral_low']:
415
+ return f"{zones['neutral_low']}≤RSI<{zones['neutral_high']}"
416
+ else:
417
+ return f"RSI<{zones['neutral_low']}"
418
+
419
+ def _get_kd_relationship(self, indicators):
420
+ """获取KD关系"""
421
+ k_val = indicators.get('STOCH_K', 0)
422
+ d_val = indicators.get('STOCH_D', 0)
423
+ if k_val > d_val:
424
+ return "K>D"
425
+ elif k_val < d_val:
426
+ return "K<D"
427
+ else:
428
+ return "K=D"
429
+
430
+ def _get_volatility_level(self, atr_ratio):
431
+ """获取波动率等级"""
432
+ levels = self.config.VOLATILITY_LEVELS
433
+ if atr_ratio >= levels['high']:
434
+ return f"高波动(≥{levels['high']}%)"
435
+ elif atr_ratio >= levels['medium']:
436
+ return f"中等波动({levels['medium']}-{levels['high']}%)"
437
+ elif atr_ratio >= levels['low']:
438
+ return f"低波动({levels['low']}-{levels['medium']}%)"
439
+ else:
440
+ return f"极低波动(<{levels['low']}%)"
441
+
442
+ def _get_volume_level(self, volume_ratio):
443
+ """获取成交量等级"""
444
+ levels = self.config.VOLUME_RATIO_LEVELS
445
+ if volume_ratio >= levels['heavy']:
446
+ return f"放量(≥{levels['heavy']}倍)"
447
+ elif volume_ratio >= levels['moderate']:
448
+ return f"温和放量({levels['moderate']}-{levels['heavy']}倍)"
449
+ elif volume_ratio >= levels['normal']:
450
+ return "正常成交量"
451
+ else:
452
+ return f"缩量(<{levels['normal']}倍)"
453
+
454
+ def _get_volume_trend_description(self, vol_trend):
455
+ """获取成交量趋势描述"""
456
+ if vol_trend > 0:
457
+ return "成交量递增"
458
+ elif vol_trend < 0:
459
+ return "成交量递减"
460
+ else:
461
+ return "成交量平稳"
462
+
463
+ def _generate_daily_fund_flow(self, indicators):
464
+ """生成当日资金流"""
465
+ section = []
466
+ fund_date = indicators.get('日期', '最新')
467
+ section.append(f"\n{fund_date}资金流:")
468
+
469
+ fund_items = [
470
+ ('主力净流入额', '主力净流入占比', '主力净流入'),
471
+ ('超大单净流入额', '超大单净流入占比', '超大单'),
472
+ ('大单净流入额', '大单净流入占比', '大单'),
473
+ ('中单净流入额', '中单净流入占比', '中单'),
474
+ ('小单净流入额', '小单净流入占比', '小单')
475
+ ]
476
+
477
+ for amount_key, ratio_key, label in fund_items:
478
+ amount = indicators.get(amount_key, 0)
479
+ ratio = indicators.get(ratio_key, 0)
480
+ amount_wan = amount / 10000 if amount != 0 else 0
481
+ section.append(f" {label:<8}: {amount_wan:.{self.config.DISPLAY_PRECISION['volume_wan']}f}万元 ({ratio:+.{self.config.DISPLAY_PRECISION['percentage']}f}%)")
482
+
483
+ return section
484
+
485
+ def _generate_5day_fund_flow(self, indicators):
486
+ """生成5日累计资金流"""
487
+ section = []
488
+ section.append("\n5日累计资金流:")
489
+
490
+ fund_items = [
491
+ ('5日主力净流入额', '5日主力净流入占比', '主力净流入'),
492
+ ('5日超大单净流入额', '5日超大单净流入占比', '超大单'),
493
+ ('5日大单净流入额', '5日大单净流入占比', '大单'),
494
+ ('5日中单净流入额', '5日中单净流入占比', '中单'),
495
+ ('5日小单净流入额', '5日小单净流入占比', '小单')
496
+ ]
497
+
498
+ for amount_key, ratio_key, label in fund_items:
499
+ amount = indicators.get(amount_key, 0)
500
+ ratio = indicators.get(ratio_key, 0)
501
+ amount_wan = amount / 10000 if amount != 0 else 0
502
+ section.append(f" {label}: {amount_wan:.{self.config.DISPLAY_PRECISION['volume_wan']}f}万元 (均值:{ratio:+.{self.config.DISPLAY_PRECISION['percentage']}f}%)")
503
+
504
+ return section
505
+
506
+ def _generate_fund_flow_analysis(self, indicators, has_fund_data, has_5day_data):
507
+ """生成资金流向分析"""
508
+ section = []
509
+ section.append("\n资金流向分析:")
510
+
511
+ if has_fund_data:
512
+ section.extend(self._analyze_current_fund_flow(indicators))
513
+
514
+ if has_5day_data:
515
+ section.extend(self._analyze_5day_fund_flow(indicators))
516
+
517
+ if has_fund_data and has_5day_data:
518
+ section.extend(self._analyze_fund_flow_trend(indicators))
519
+
520
+ return section
521
+
522
+ def _analyze_current_fund_flow(self, indicators):
523
+ """分析当前资金流"""
524
+ section = []
525
+ main_amount = indicators.get('主力净流入额', 0)
526
+ main_ratio = indicators.get('主力净流入占比', 0)
527
+
528
+ # 资金流向
529
+ if main_amount > 0:
530
+ current_status = "最新日主力资金净流入"
531
+ elif main_amount < 0:
532
+ current_status = "最新日主力资金净流出"
533
+ else:
534
+ current_status = "最新日主力资金平衡"
535
+
536
+ # 资金强度判断
537
+ current_strength = self._get_fund_flow_strength(abs(main_ratio))
538
+
539
+ section.append(f" 当前状态: {current_status}")
540
+ section.append(f" 流动强度: {current_strength}")
541
+
542
+ return section
543
+
544
+ def _analyze_5day_fund_flow(self, indicators):
545
+ """分析5日资金流"""
546
+ section = []
547
+ day5_main = indicators.get('5日主力净流入额', 0)
548
+ day5_main_ratio = indicators.get('5日主力净流入占比', 0)
549
+
550
+ if day5_main > 0:
551
+ day5_status = "5日累计主力资金净流入"
552
+ elif day5_main < 0:
553
+ day5_status = "5日累计主力资金净流出"
554
+ else:
555
+ day5_status = "5日累计主力资金平衡"
556
+
557
+ section.append(f" 5日状态: {day5_status}")
558
+
559
+ # 5日资金流强度
560
+ day5_strength = self._get_5day_fund_flow_strength(abs(day5_main_ratio))
561
+ section.append(f" 5日强度: {day5_strength}")
562
+
563
+ return section
564
+
565
+ def _analyze_fund_flow_trend(self, indicators):
566
+ """分析资金流趋势对比"""
567
+ section = []
568
+ current_main = indicators.get('主力净流入额', 0)
569
+ day5_main = indicators.get('5日主力净流入额', 0)
570
+
571
+ # 计算5日日均资金流
572
+ day5_avg = day5_main / 5 if day5_main != 0 else 0
573
+
574
+ trend_analysis = self._determine_trend_analysis(current_main, day5_main, day5_avg)
575
+ section.append(f" 趋势对比: {trend_analysis}")
576
+
577
+ return section
578
+
579
+ def _get_fund_flow_strength(self, ratio):
580
+ """获取资金流强度"""
581
+ levels = self.config.FUND_FLOW_LEVELS
582
+ if ratio >= levels['strong']:
583
+ return "资金流动强烈"
584
+ elif ratio >= levels['active']:
585
+ return "资金流动活跃"
586
+ elif ratio >= levels['moderate']:
587
+ return "资金流动温和"
588
+ else:
589
+ return "资金流动平淡"
590
+
591
+ def _get_5day_fund_flow_strength(self, ratio):
592
+ """获取5日资金流强度"""
593
+ if ratio >= 3:
594
+ return "5日资金流动活跃"
595
+ elif ratio >= 1:
596
+ return "5日资金流动温和"
597
+ else:
598
+ return "5日资金流动平淡"
599
+
600
+ def _determine_trend_analysis(self, current_main, day5_main, day5_avg):
601
+ """确定趋势分析"""
602
+ if current_main > 0 and day5_main > 0:
603
+ if current_main > abs(day5_avg) * 2:
604
+ return "当日资金流入明显加速"
605
+ elif current_main > abs(day5_avg):
606
+ return "当日资金流入超过近期均值"
607
+ else:
608
+ return "当日资金流入符合近期趋势"
609
+ elif current_main < 0 and day5_main < 0:
610
+ if abs(current_main) > abs(day5_avg) * 2:
611
+ return "当日资金流出明显加速"
612
+ elif abs(current_main) > abs(day5_avg):
613
+ return "当日资金流出超过近期均值"
614
+ else:
615
+ return "当日资金流出符合近期趋势"
616
+ elif current_main > 0 and day5_main < 0:
617
+ return "当日资金流向与近期趋势相反(转为流入)"
618
+ elif current_main < 0 and day5_main > 0:
619
+ return "当日资金流向与近期趋势相反(转为流出)"
620
+ else:
621
+ return "资金流向变化不明显"
622
+
623
+ def _get_di_relationship(self, indicators):
624
+ """获取DI关系"""
625
+ di_plus = indicators.get('DI_PLUS', 0)
626
+ di_minus = indicators.get('DI_MINUS', 0)
627
+ di_diff = indicators.get('DI_DIFF', 0)
628
+
629
+ if di_plus > di_minus:
630
+ return f"DI+>DI-(差值:{di_diff:.{self.config.DISPLAY_PRECISION['ratio']}f})"
631
+ elif di_plus < di_minus:
632
+ return f"DI+<DI-(差值:{di_diff:.{self.config.DISPLAY_PRECISION['ratio']}f})"
633
+ else:
634
+ return "DI+=DI-"
635
+
636
+ def _get_adx_range(self, adx_value):
637
+ """获取ADX范围"""
638
+ levels = self.config.ADX_LEVELS
639
+ if adx_value > levels['strong_trend']:
640
+ return f"ADX>{levels['strong_trend']}"
641
+ elif adx_value > levels['medium_trend']:
642
+ return f"{levels['medium_trend']}<ADX≤{levels['strong_trend']}"
643
+ else:
644
+ return f"ADX≤{levels['medium_trend']}"
645
+
646
+ def _generate_adx_trend_analysis(self, indicators):
647
+ """生成ADX趋势分析"""
648
+ section = []
649
+ adx_trend = indicators.get('ADX_TREND', 0)
650
+ adx_5d_change = indicators.get('ADX_5D_CHANGE', 0)
651
+
652
+ if adx_trend > 0:
653
+ trend_direction = "ADX上升"
654
+ elif adx_trend < 0:
655
+ trend_direction = "ADX下降"
656
+ else:
657
+ trend_direction = "ADX平稳"
658
+
659
+ section.append(f" ADX趋势: {trend_direction}")
660
+ section.append(f" 5日变化: {adx_5d_change:+.{self.config.DISPLAY_PRECISION['percentage']}f}%")
661
+
662
+ return section
663
+
664
+ def _collect_summary_items(self, indicators):
665
+ """收集汇总项目"""
666
+ summary = []
667
+
668
+ # 均线状态
669
+ if 'trend_pattern' in indicators and indicators['trend_pattern']:
670
+ summary.append(f"均线形态:{indicators['trend_pattern']}")
671
+
672
+ if 'cross_pattern' in indicators and indicators['cross_pattern'] != "无交叉信号":
673
+ summary.append(f"均线交叉:{indicators['cross_pattern']}")
674
+
675
+ if 'arrangement_pattern' in indicators and indicators['arrangement_pattern']:
676
+ summary.append(f"均线排列:{indicators['arrangement_pattern']}")
677
+
678
+ # MACD状态
679
+ if 'MACD_HIST' in indicators:
680
+ macd_status = "HIST>0" if indicators['MACD_HIST'] > 0 else "HIST<0"
681
+ summary.append(f"MACD:{macd_status}")
682
+
683
+ # DMI/ADX状态
684
+ if 'ADX' in indicators:
685
+ summary.append(f"ADX:{indicators['ADX']:.1f}")
686
+
687
+ if 'DI_PLUS' in indicators and 'DI_MINUS' in indicators:
688
+ di_plus = indicators['DI_PLUS']
689
+ di_minus = indicators['DI_MINUS']
690
+ if di_plus > di_minus:
691
+ summary.append(f"DI+({di_plus:.1f})>DI-({di_minus:.1f})")
692
+ else:
693
+ summary.append(f"DI+({di_plus:.1f})<DI-({di_minus:.1f})")
694
+
695
+ # 其他指标状态
696
+ summary.extend(self._collect_other_indicators_summary(indicators))
697
+
698
+ return summary
699
+
700
+ def _collect_other_indicators_summary(self, indicators):
701
+ """收集其他指标汇总"""
702
+ summary = []
703
+
704
+ # OBV状态
705
+ if 'OBV_5d_change' in indicators:
706
+ obv_5d = indicators['OBV_5d_change']
707
+ summary.append(f"OBV 5日变化:{obv_5d:+.{self.config.DISPLAY_PRECISION['percentage']}f}%")
708
+
709
+ # RSI状态
710
+ if 'RSI_14' in indicators:
711
+ rsi_14 = indicators['RSI_14']
712
+ summary.append(f"RSI(14):{rsi_14:.1f}")
713
+
714
+ # KD状态
715
+ if 'STOCH_K' in indicators and 'STOCH_D' in indicators:
716
+ k_val = indicators['STOCH_K']
717
+ d_val = indicators['STOCH_D']
718
+ summary.append(f"KD:K({k_val:.1f}) D({d_val:.1f})")
719
+
720
+ # ATR状态
721
+ if 'ATR_RATIO' in indicators:
722
+ atr_ratio = indicators['ATR_RATIO']
723
+ summary.append(f"ATR波动率:{atr_ratio:.{self.config.DISPLAY_PRECISION['percentage']}f}%")
724
+
725
+ # 量比状态
726
+ if 'VOLUME_RATIO' in indicators:
727
+ vol_ratio = indicators['VOLUME_RATIO']
728
+ summary.append(f"量比:{vol_ratio:.{self.config.DISPLAY_PRECISION['ratio']}f}")
729
+
730
+ # OBV背离状态
731
+ if 'OBV_DIVERGENCE' in indicators and indicators['OBV_DIVERGENCE'] != "无明显背离":
732
+ divergence = indicators['OBV_DIVERGENCE']
733
+ summary.append(f"OBV:{divergence}")
734
+
735
+ # 资金流状态
736
+ summary.extend(self._collect_fund_flow_summary(indicators))
737
+
738
+ return summary
739
+
740
+ def _collect_fund_flow_summary(self, indicators):
741
+ """收集资金流汇总"""
742
+ summary = []
743
+
744
+ # 最新主力资金流状态
745
+ if '主力净流入额' in indicators:
746
+ main_amount = indicators.get('主力净流入额', 0)
747
+ main_ratio = indicators.get('主力净流入占比', 0)
748
+ main_amount_wan = main_amount / 10000
749
+
750
+ if main_amount > 0:
751
+ summary.append(f"主力资金:净流入{main_amount_wan:.{self.config.DISPLAY_PRECISION['volume_wan']}f}万元({main_ratio:+.1f}%)")
752
+ elif main_amount < 0:
753
+ summary.append(f"主力资金:净流出{abs(main_amount_wan):.{self.config.DISPLAY_PRECISION['volume_wan']}f}万元({main_ratio:+.1f}%)")
754
+ else:
755
+ summary.append("主力资金:资金平衡")
756
+
757
+ # 5日主力资金流状态
758
+ if '5日主力净流入额' in indicators:
759
+ day5_main_amount = indicators.get('5日主力净流入额', 0)
760
+ day5_main_ratio = indicators.get('5日主力净流入占比', 0)
761
+ day5_main_wan = day5_main_amount / 10000
762
+
763
+ if day5_main_amount > 0:
764
+ summary.append(f"5日主力:累计净流入{day5_main_wan:.{self.config.DISPLAY_PRECISION['volume_wan']}f}万元(均值{day5_main_ratio:+.1f}%)")
765
+ elif day5_main_amount < 0:
766
+ summary.append(f"5日主力:累计净流出{abs(day5_main_wan):.{self.config.DISPLAY_PRECISION['volume_wan']}f}万元(均值{day5_main_ratio:+.1f}%)")
767
+ else:
768
+ summary.append("5日主力:资金平衡")
769
+
770
+ # 超大单资金状态
771
+ if '超大单净流入额' in indicators:
772
+ super_amount = indicators.get('超大单净流入额', 0)
773
+ super_ratio = indicators.get('超大单净流入占比', 0)
774
+ super_amount_wan = super_amount / 10000
775
+
776
+ threshold = self.config.REPORT_CONFIG['min_fund_display_threshold']
777
+ if abs(super_amount_wan) > threshold:
778
+ if super_amount > 0:
779
+ summary.append(f"超大单:净流入{super_amount_wan:.{self.config.DISPLAY_PRECISION['volume_wan']}f}万元({super_ratio:+.1f}%)")
780
+ else:
781
+ summary.append(f"超大单:净流出{abs(super_amount_wan):.{self.config.DISPLAY_PRECISION['volume_wan']}f}万元({super_ratio:+.1f}%)")
782
+
783
+ return summary