feat: 增加专业分析内容 - 市场情绪/资金流向/板块强弱/投资建议

This commit is contained in:
2026-04-10 17:42:59 +08:00
parent bb19b67460
commit bd1ec2ea01
2 changed files with 246 additions and 39 deletions

View File

@@ -92,6 +92,127 @@ def get_board_data(board_type: str, sort_by: str = "f3", limit: int = 100) -> Op
return None return None
def analyze_market(all_industry: List, all_concept: List) -> Dict:
"""
专业市场分析
返回各种分析指标
"""
analysis = {}
# 1. 市场整体统计
if all_industry:
up_count = len([b for b in all_industry if b['pct_change'] > 0])
down_count = len([b for b in all_industry if b['pct_change'] < 0])
flat_count = len([b for b in all_industry if b['pct_change'] == 0])
avg_pct = sum(b['pct_change'] for b in all_industry) / len(all_industry)
max_pct = max(b['pct_change'] for b in all_industry)
min_pct = min(b['pct_change'] for b in all_industry)
total_flow = sum(b['main_flow'] for b in all_industry)
analysis['market_stats'] = {
'up_count': up_count,
'down_count': down_count,
'flat_count': flat_count,
'up_ratio': up_count / len(all_industry) * 100,
'avg_pct': avg_pct,
'max_pct': max_pct,
'min_pct': min_pct,
'total_flow': total_flow,
}
# 2. 资金集中度分析
if all_industry:
sorted_by_flow = sorted(all_industry, key=lambda x: x['main_flow'], reverse=True)
top5_flow = sum(b['main_flow'] for b in sorted_by_flow[:5])
analysis['fund_concentration'] = {
'top5_flow': top5_flow,
'top5_ratio': abs(top5_flow) / abs(analysis['market_stats']['total_flow']) * 100 if analysis['market_stats']['total_flow'] != 0 else 0,
}
# 3. 板块强弱分析
if all_industry:
strong_boards = [b for b in all_industry if b['pct_change'] > 1 and b['main_flow'] > 5]
weak_boards = [b for b in all_industry if b['pct_change'] < -1 and b['main_flow'] < -5]
analysis['strength'] = {
'strong_count': len(strong_boards),
'weak_count': len(weak_boards),
'strong_boards': strong_boards[:5],
'weak_boards': weak_boards[:5],
}
# 4. 概念板块热度分析
if all_concept:
hot_concepts = sorted([b for b in all_concept if b['main_flow'] > 0],
key=lambda x: x['main_flow'], reverse=True)[:5]
cold_concepts = sorted([b for b in all_concept if b['main_flow'] < 0],
key=lambda x: x['main_flow'])[:5]
analysis['concept_heat'] = {
'hot': hot_concepts,
'cold': cold_concepts,
}
# 5. 市场情绪判断
if analysis.get('market_stats'):
stats = analysis['market_stats']
# 综合判断
sentiment_score = 0
# 涨跌比例贡献
if stats['up_ratio'] > 70:
sentiment_score += 2
elif stats['up_ratio'] > 50:
sentiment_score += 1
elif stats['up_ratio'] < 30:
sentiment_score -= 2
elif stats['up_ratio'] < 50:
sentiment_score -= 1
# 平均涨跌幅贡献
if stats['avg_pct'] > 0.5:
sentiment_score += 1
elif stats['avg_pct'] < -0.5:
sentiment_score -= 1
# 资金流向贡献
if stats['total_flow'] > 50:
sentiment_score += 2
elif stats['total_flow'] > 0:
sentiment_score += 1
elif stats['total_flow'] < -50:
sentiment_score -= 2
elif stats['total_flow'] < 0:
sentiment_score -= 1
# 情绪等级
if sentiment_score >= 4:
sentiment = '强势上涨'
sentiment_desc = '市场情绪高涨,多数板块上涨,资金大幅流入,建议关注强势板块机会。'
elif sentiment_score >= 2:
sentiment = '偏强'
sentiment_desc = '市场整体偏强,资金流向积极,可适度参与热门板块。'
elif sentiment_score >= 0:
sentiment = '平稳'
sentiment_desc = '市场情绪平稳,涨跌均衡,建议观望或轻仓布局。'
elif sentiment_score >= -2:
sentiment = '偏弱'
sentiment_desc = '市场整体偏弱,资金流出明显,建议谨慎操作,关注防御性板块。'
else:
sentiment = '弱势下跌'
sentiment_desc = '市场情绪低迷,多数板块下跌,资金大幅流出,建议规避风险,等待企稳信号。'
analysis['sentiment'] = {
'score': sentiment_score,
'level': sentiment,
'description': sentiment_desc,
}
return analysis
def generate_daily_report(boards_data: Dict, to_email: str = "wlq@tphai.com") -> bool: def generate_daily_report(boards_data: Dict, to_email: str = "wlq@tphai.com") -> bool:
""" """
生成盘后分析报告并发送邮件 生成盘后分析报告并发送邮件
@@ -113,90 +234,176 @@ def generate_daily_report(boards_data: Dict, to_email: str = "wlq@tphai.com") ->
# 分析总结 # 分析总结
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 计算市场趋势
avg_pct = 0
if all_industry:
avg_pct = sum(b['pct_change'] for b in all_industry) / len(all_industry)
market_trend = '平稳'
if avg_pct > 0.5:
market_trend = '上涨'
elif avg_pct < -0.5:
market_trend = '下跌'
# 排序数据 # 排序数据
industry_by_pct = sorted(all_industry, key=lambda x: x['pct_change'], reverse=True) industry_by_pct = sorted(all_industry, key=lambda x: x['pct_change'], reverse=True)
industry_by_flow = sorted(all_industry, key=lambda x: x['main_flow'], reverse=True) industry_by_flow = sorted(all_industry, key=lambda x: x['main_flow'], reverse=True)
concept_by_pct = sorted(all_concept, key=lambda x: x['pct_change'], reverse=True) concept_by_pct = sorted(all_concept, key=lambda x: x['pct_change'], reverse=True)
concept_by_flow = sorted(all_concept, key=lambda x: x['main_flow'], reverse=True) concept_by_flow = sorted(all_concept, key=lambda x: x['main_flow'], reverse=True)
# 生成HTML正文分析总结 # 执行专业分析
analysis = analyze_market(all_industry, all_concept)
# ========== 生成HTML正文 ==========
html_lines = [ html_lines = [
"<h2>📊 A股板块盘后分析报告</h2>", "<h2>📊 A股板块盘后分析报告</h2>",
f"<p>报告时间: {timestamp}</p>", f"<p>报告时间: {timestamp}</p>",
f"<p>市场整体: <strong>{market_trend}</strong> (行业平均涨跌 {avg_pct:+.2f}%)</p>",
"", "",
"<hr>", "<hr>",
"",
"<h3>🔥 今日热门概念板块 TOP5</h3>",
"<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>",
"<tr style='background:#f0f0f0'><th>板块</th><th>涨跌幅</th><th>主力资金</th></tr>",
] ]
for board in concept_by_pct[:5]: # ===== 一、市场情绪分析 =====
pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%" if analysis.get('sentiment'):
flow_str = f"+{board['main_flow']:.2f}亿" if board['main_flow'] > 0 else f"{board['main_flow']:.2f}亿" sent = analysis['sentiment']
html_lines.append(f"<tr><td>{board['name']}</td><td>{pct_str}</td><td>{flow_str}</td></tr>") stats = analysis.get('market_stats', {})
html_lines.append("")
html_lines.append("<h3>一、市场情绪分析</h3>")
html_lines.append(f"<p><strong>市场评级: {sent['level']}</strong></p>")
html_lines.append(f"<p>{sent['description']}</p>")
if stats:
html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>")
html_lines.append("<tr><td>上涨板块</td><td>{}</td><td>占比 {:.1f}%</td></tr>".format(stats['up_count'], stats['up_ratio']))
html_lines.append("<tr><td>下跌板块</td><td>{}</td><td>占比 {:.1f}%</td></tr>".format(stats['down_count'], 100 - stats['up_ratio']))
html_lines.append("<tr><td>平均涨跌</td><td>{:.2f}%</td><td>最大涨幅 {:.2f}%</td></tr>".format(stats['avg_pct'], stats['max_pct']))
html_lines.append("<tr><td>资金净流</td><td>{:.2f}亿</td><td>最大跌幅 {:.2f}%</td></tr>".format(stats['total_flow'], stats['min_pct']))
html_lines.append("</table>")
html_lines.append("</table>") # ===== 二、资金流向分析 =====
if analysis.get('fund_concentration'):
fund = analysis['fund_concentration']
html_lines.append("")
html_lines.append("<h3>二、资金流向分析</h3>")
html_lines.append(f"<p>TOP5板块资金合计: <strong>{fund['top5_flow']:.2f}亿</strong></p>")
html_lines.append(f"<p>资金集中度: <strong>{fund['top5_ratio']:.1f}%</strong>(流入资金集中在少数板块)</p>")
# ===== 三、板块强弱分析 =====
if analysis.get('strength'):
strength = analysis['strength']
html_lines.append("")
html_lines.append("<h3>三、板块强弱分析</h3>")
html_lines.append(f"<p>强势板块(涨幅>1%且资金流入>5亿: <strong>{strength['strong_count']} 个</strong></p>")
html_lines.append(f"<p>弱势板块(跌幅>1%且资金流出>5亿: <strong>{strength['weak_count']} 个</strong></p>")
if strength['strong_boards']:
html_lines.append("<p>强势板块示例:</p>")
html_lines.append("<ul>")
for b in strength['strong_boards'][:3]:
html_lines.append("<li>{name}: +{pct:.2f}%, 资金+{flow:.2f}亿</li>".format(
name=b['name'], pct=b['pct_change'], flow=b['main_flow']))
html_lines.append("</ul>")
if strength['weak_boards']:
html_lines.append("<p>弱势板块示例:</p>")
html_lines.append("<ul>")
for b in strength['weak_boards'][:3]:
html_lines.append("<li>{name}: {pct:.2f}%, 资金{flow:.2f}亿</li>".format(
name=b['name'], pct=b['pct_change'], flow=b['main_flow']))
html_lines.append("</ul>")
# ===== 四、热门概念分析 =====
if analysis.get('concept_heat'):
heat = analysis['concept_heat']
html_lines.append("")
html_lines.append("<h3>四、概念板块热度</h3>")
if heat['hot']:
html_lines.append("<p>🔥 热门概念资金流入TOP:</p>")
html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>")
html_lines.append("<tr style='background:#fff0f0'><th>概念</th><th>资金(亿)</th><th>涨跌</th></tr>")
for b in heat['hot'][:5]:
pct_str = f"+{b['pct_change']:.2f}%" if b['pct_change'] > 0 else f"{b['pct_change']:.2f}%"
html_lines.append(f"<tr><td>{b['name']}</td><td>+{b['main_flow']:.2f}</td><td>{pct_str}</td></tr>")
html_lines.append("</table>")
if heat['cold']:
html_lines.append("<p>❄️ 冷门概念资金流出TOP:</p>")
html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>")
html_lines.append("<tr style='background:#f0f0ff'><th>概念</th><th>资金(亿)</th><th>涨跌</th></tr>")
for b in heat['cold'][:5]:
pct_str = f"+{b['pct_change']:.2f}%" if b['pct_change'] > 0 else f"{b['pct_change']:.2f}%"
html_lines.append(f"<tr><td>{b['name']}</td><td>{b['main_flow']:.2f}</td><td>{pct_str}</td></tr>")
html_lines.append("</table>")
# ===== 五、行业板块排行 =====
html_lines.append("") html_lines.append("")
html_lines.append("<hr>")
html_lines.append("")
html_lines.append("<h3>五、行业板块涨跌排行</h3>")
html_lines.append("<h3>📈 行业板块涨幅 TOP5</h3>") html_lines.append("<p>📈 涨幅 TOP5:</p>")
html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>") html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>")
html_lines.append("<tr style='background:#f0f0f0'><th>板块</th><th>涨跌幅</th><th>主力资金</th><th>领涨股</th></tr>") html_lines.append("<tr style='background:#f0f0f0'><th>板块</th><th>涨跌幅</th><th>主力资金</th><th>领涨股</th></tr>")
for board in industry_by_pct[:5]: for board in industry_by_pct[:5]:
pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%" pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%"
flow_str = f"+{board['main_flow']:.2f}亿" if board['main_flow'] > 0 else f"{board['main_flow']:.2f}亿" flow_str = f"+{board['main_flow']:.2f}亿" if board['main_flow'] > 0 else f"{board['main_flow']:.2f}亿"
html_lines.append(f"<tr><td>{board['name']}</td><td>{pct_str}</td><td>{flow_str}</td><td>{board['leader_name'] or '-'}</td></tr>") html_lines.append(f"<tr><td>{board['name']}</td><td>{pct_str}</td><td>{flow_str}</td><td>{board['leader_name'] or '-'}</td></tr>")
html_lines.append("</table>") html_lines.append("</table>")
html_lines.append("")
html_lines.append("<h3>📉 行业板块跌幅 TOP5</h3>") html_lines.append("<p>📉 跌幅 TOP5:</p>")
html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>") html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>")
html_lines.append("<tr style='background:#f0f0f0'><th>板块</th><th>涨跌幅</th><th>主力资金</th></tr>") html_lines.append("<tr style='background:#f0f0f0'><th>板块</th><th>涨跌幅</th><th>主力资金</th></tr>")
for board in industry_by_pct[-5:]: for board in industry_by_pct[-5:]:
pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%" pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%"
flow_str = f"+{board['main_flow']:.2f}亿" if board['main_flow'] > 0 else f"{board['main_flow']:.2f}亿" flow_str = f"+{board['main_flow']:.2f}亿" if board['main_flow'] > 0 else f"{board['main_flow']:.2f}亿"
html_lines.append(f"<tr><td>{board['name']}</td><td>{pct_str}</td><td>{flow_str}</td></tr>") html_lines.append(f"<tr><td>{board['name']}</td><td>{pct_str}</td><td>{flow_str}</td></tr>")
html_lines.append("</table>") html_lines.append("</table>")
# ===== 六、主力资金排行 =====
html_lines.append("") html_lines.append("")
html_lines.append("<h3>六、主力资金流向排行</h3>")
html_lines.append("<h3>💰 主力资金大幅流入 TOP10</h3>") html_lines.append("<p>💰 大幅流入 TOP10 (≥10亿):</p>")
html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>") html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>")
html_lines.append("<tr style='background:#f0f0f0'><th>板块</th><th>资金流入(亿)</th><th>涨跌</th></tr>") html_lines.append("<tr style='background:#f0f0f0'><th>板块</th><th>资金(亿)</th><th>涨跌</th></tr>")
inflow_boards = [b for b in industry_by_flow if b['main_flow'] > 10][:10] inflow_boards = [b for b in industry_by_flow if b['main_flow'] > 10][:10]
for board in inflow_boards: for board in inflow_boards:
pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%" pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%"
html_lines.append(f"<tr><td>{board['name']}</td><td>+{board['main_flow']:.2f}</td><td>{pct_str}</td></tr>") html_lines.append(f"<tr><td>{board['name']}</td><td>+{board['main_flow']:.2f}</td><td>{pct_str}</td></tr>")
html_lines.append("</table>") html_lines.append("</table>")
html_lines.append("")
html_lines.append("<h3>💸 主力资金大幅流出 TOP10</h3>") html_lines.append("<p>💸 大幅流出 TOP10 (≤-10亿):</p>")
html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>") html_lines.append("<table border='1' cellpadding='6' cellspacing='0' style='border-collapse: collapse;'>")
html_lines.append("<tr style='background:#f0f0f0'><th>板块</th><th>资金流出(亿)</th><th>涨跌</th></tr>") html_lines.append("<tr style='background:#f0f0f0'><th>板块</th><th>资金(亿)</th><th>涨跌</th></tr>")
outflow_boards = [b for b in industry_by_flow if b['main_flow'] < -10][:10] outflow_boards = [b for b in industry_by_flow if b['main_flow'] < -10][:10]
for board in outflow_boards: for board in outflow_boards:
pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%" pct_str = f"+{board['pct_change']:.2f}%" if board['pct_change'] > 0 else f"{board['pct_change']:.2f}%"
html_lines.append(f"<tr><td>{board['name']}</td><td>{board['main_flow']:.2f}</td><td>{pct_str}</td></tr>") html_lines.append(f"<tr><td>{board['name']}</td><td>{board['main_flow']:.2f}</td><td>{pct_str}</td></tr>")
html_lines.append("</table>") html_lines.append("</table>")
# ===== 七、投资建议 =====
html_lines.append("")
html_lines.append("<hr>")
html_lines.append("<h3>七、投资建议</h3>")
if analysis.get('sentiment'):
sent = analysis['sentiment']
if sent['score'] >= 2:
html_lines.append("<p><strong>策略建议: 积极参与</strong></p>")
html_lines.append("<ul>")
html_lines.append("<li>关注资金大幅流入的热门板块,如新能源、科技类</li>")
html_lines.append("<li>可适当追涨强势板块龙头股</li>")
html_lines.append("<li>注意板块轮动节奏,避免追高</li>")
html_lines.append("</ul>")
elif sent['score'] >= 0:
html_lines.append("<p><strong>策略建议: 观望为主</strong></p>")
html_lines.append("<ul>")
html_lines.append("<li>等待明确的市场方向信号</li>")
html_lines.append("<li>可轻仓布局有资金流入的潜力板块</li>")
html_lines.append("<li>规避资金流出明显的板块</li>")
html_lines.append("</ul>")
else:
html_lines.append("<p><strong>策略建议: 谨慎防守</strong></p>")
html_lines.append("<ul>")
html_lines.append("<li>控制仓位,规避风险板块</li>")
html_lines.append("<li>关注防御性板块如银行、医药等</li>")
html_lines.append("<li>等待市场企稳后再介入</li>")
html_lines.append("</ul>")
html_lines.append("") html_lines.append("")
html_lines.append("<hr>") html_lines.append("<hr>")
html_lines.append("<p><em>📊 详细数据请查看附件 CSV 文件</em></p>") html_lines.append("<p><em>📊 详细数据请查看附件 CSV 文件</em></p>")

View File

@@ -1,5 +1,5 @@
# A股板块详细数据 # A股板块详细数据
# 生成时间: 2026-04-10 17:19:21 # 生成时间: 2026-04-10 17:42:51
=== 行业板块涨跌幅排行 === === 行业板块涨跌幅排行 ===
板块名称,涨跌幅(%),主力资金(亿),领涨股 板块名称,涨跌幅(%),主力资金(亿),领涨股
1 # A股板块详细数据
2 # 生成时间: 2026-04-10 17:19:21 # 生成时间: 2026-04-10 17:42:51
3 === 行业板块涨跌幅排行 ===
4 板块名称,涨跌幅(%),主力资金(亿),领涨股
5 蓄电池及其他电池,0.06,6.17,9