增加智策板块
This commit is contained in:
@@ -0,0 +1,462 @@
|
||||
"""
|
||||
智策板块数据采集模块
|
||||
使用AKShare获取板块相关数据
|
||||
"""
|
||||
|
||||
import akshare as ak
|
||||
import pandas as pd
|
||||
from datetime import datetime, timedelta
|
||||
import warnings
|
||||
import time
|
||||
|
||||
warnings.filterwarnings('ignore')
|
||||
|
||||
|
||||
class SectorStrategyDataFetcher:
|
||||
"""板块策略数据获取类"""
|
||||
|
||||
def __init__(self):
|
||||
print("[智策] 板块数据获取器初始化...")
|
||||
self.max_retries = 3 # 最大重试次数
|
||||
self.retry_delay = 2 # 重试延迟(秒)
|
||||
self.request_delay = 1 # 请求间隔(秒)
|
||||
|
||||
def _safe_request(self, func, *args, **kwargs):
|
||||
"""安全的请求函数,包含重试机制"""
|
||||
for attempt in range(self.max_retries):
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
# 添加请求延迟,避免请求过快
|
||||
time.sleep(self.request_delay)
|
||||
return result
|
||||
except Exception as e:
|
||||
if attempt < self.max_retries - 1:
|
||||
print(f" 请求失败,{self.retry_delay}秒后重试... (尝试 {attempt + 1}/{self.max_retries})")
|
||||
time.sleep(self.retry_delay)
|
||||
else:
|
||||
print(f" 请求失败,已达最大重试次数: {e}")
|
||||
raise e
|
||||
|
||||
def get_all_sector_data(self):
|
||||
"""
|
||||
获取所有板块的综合数据
|
||||
|
||||
Returns:
|
||||
dict: 包含多个维度的板块数据
|
||||
"""
|
||||
print("[智策] 开始获取板块综合数据...")
|
||||
|
||||
data = {
|
||||
"success": False,
|
||||
"timestamp": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
"sectors": {},
|
||||
"sector_fund_flow": {},
|
||||
"market_overview": {},
|
||||
"north_flow": {},
|
||||
"news": []
|
||||
}
|
||||
|
||||
try:
|
||||
# 1. 获取行业板块数据
|
||||
print(" [1/6] 获取行业板块行情...")
|
||||
sectors_data = self._get_sector_performance()
|
||||
if sectors_data:
|
||||
data["sectors"] = sectors_data
|
||||
print(f" ✓ 成功获取 {len(sectors_data)} 个行业板块数据")
|
||||
|
||||
# 2. 获取概念板块数据
|
||||
print(" [2/6] 获取概念板块行情...")
|
||||
concept_data = self._get_concept_performance()
|
||||
if concept_data:
|
||||
data["concepts"] = concept_data
|
||||
print(f" ✓ 成功获取 {len(concept_data)} 个概念板块数据")
|
||||
|
||||
# 3. 获取板块资金流向
|
||||
print(" [3/6] 获取行业资金流向...")
|
||||
fund_flow_data = self._get_sector_fund_flow()
|
||||
if fund_flow_data:
|
||||
data["sector_fund_flow"] = fund_flow_data
|
||||
print(f" ✓ 成功获取资金流向数据")
|
||||
|
||||
# 4. 获取市场总体情况
|
||||
print(" [4/6] 获取市场总体情况...")
|
||||
market_data = self._get_market_overview()
|
||||
if market_data:
|
||||
data["market_overview"] = market_data
|
||||
print(f" ✓ 成功获取市场概况")
|
||||
|
||||
# 5. 获取北向资金流向
|
||||
print(" [5/6] 获取北向资金流向...")
|
||||
north_flow = self._get_north_money_flow()
|
||||
if north_flow:
|
||||
data["north_flow"] = north_flow
|
||||
print(f" ✓ 成功获取北向资金数据")
|
||||
|
||||
# 6. 获取财经新闻
|
||||
print(" [6/6] 获取财经新闻...")
|
||||
news_data = self._get_financial_news()
|
||||
if news_data:
|
||||
data["news"] = news_data
|
||||
print(f" ✓ 成功获取 {len(news_data)} 条新闻")
|
||||
|
||||
data["success"] = True
|
||||
print("[智策] ✓ 板块数据获取完成!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[智策] ✗ 数据获取出错: {e}")
|
||||
data["error"] = str(e)
|
||||
|
||||
return data
|
||||
|
||||
def _get_sector_performance(self):
|
||||
"""获取行业板块表现"""
|
||||
try:
|
||||
# 获取行业板块实时行情(使用重试机制)
|
||||
df = self._safe_request(ak.stock_board_industry_name_em)
|
||||
|
||||
if df is None or df.empty:
|
||||
return {}
|
||||
|
||||
# 转换为字典格式
|
||||
sectors = {}
|
||||
for idx, row in df.iterrows():
|
||||
sector_name = row.get('板块名称', '')
|
||||
if sector_name:
|
||||
sectors[sector_name] = {
|
||||
"name": sector_name,
|
||||
"change_pct": row.get('涨跌幅', 0),
|
||||
"turnover": row.get('换手率', 0),
|
||||
"total_market_cap": row.get('总市值', 0),
|
||||
"top_stock": row.get('领涨股票', ''),
|
||||
"top_stock_change": row.get('领涨股票涨跌幅', 0),
|
||||
"up_count": row.get('上涨家数', 0),
|
||||
"down_count": row.get('下跌家数', 0)
|
||||
}
|
||||
|
||||
return sectors
|
||||
|
||||
except Exception as e:
|
||||
print(f" 获取行业板块数据失败: {e}")
|
||||
return {}
|
||||
|
||||
def _get_concept_performance(self):
|
||||
"""获取概念板块表现"""
|
||||
try:
|
||||
# 获取概念板块实时行情(使用重试机制)
|
||||
df = self._safe_request(ak.stock_board_concept_name_em)
|
||||
|
||||
if df is None or df.empty:
|
||||
return {}
|
||||
|
||||
# 转换为字典格式
|
||||
concepts = {}
|
||||
for idx, row in df.iterrows():
|
||||
concept_name = row.get('板块名称', '')
|
||||
if concept_name:
|
||||
concepts[concept_name] = {
|
||||
"name": concept_name,
|
||||
"change_pct": row.get('涨跌幅', 0),
|
||||
"turnover": row.get('换手率', 0),
|
||||
"total_market_cap": row.get('总市值', 0),
|
||||
"top_stock": row.get('领涨股票', ''),
|
||||
"top_stock_change": row.get('领涨股票涨跌幅', 0),
|
||||
"up_count": row.get('上涨家数', 0),
|
||||
"down_count": row.get('下跌家数', 0)
|
||||
}
|
||||
|
||||
return concepts
|
||||
|
||||
except Exception as e:
|
||||
print(f" 获取概念板块数据失败: {e}")
|
||||
return {}
|
||||
|
||||
def _get_sector_fund_flow(self):
|
||||
"""获取行业资金流向"""
|
||||
try:
|
||||
# 获取行业资金流向(使用重试机制)
|
||||
df = self._safe_request(ak.stock_sector_fund_flow_rank, indicator="今日")
|
||||
|
||||
if df is None or df.empty:
|
||||
return {}
|
||||
|
||||
# 转换为字典格式
|
||||
fund_flow = {
|
||||
"today": [],
|
||||
"update_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
}
|
||||
|
||||
for idx, row in df.head(50).iterrows(): # 取前50个
|
||||
fund_flow["today"].append({
|
||||
"sector": row.get('名称', ''),
|
||||
"main_net_inflow": row.get('今日主力净流入-净额', 0),
|
||||
"main_net_inflow_pct": row.get('今日主力净流入-净占比', 0),
|
||||
"super_large_net_inflow": row.get('今日超大单净流入-净额', 0),
|
||||
"large_net_inflow": row.get('今日大单净流入-净额', 0),
|
||||
"medium_net_inflow": row.get('今日中单净流入-净额', 0),
|
||||
"small_net_inflow": row.get('今日小单净流入-净额', 0),
|
||||
"change_pct": row.get('今日涨跌幅', 0)
|
||||
})
|
||||
|
||||
return fund_flow
|
||||
|
||||
except Exception as e:
|
||||
print(f" 获取行业资金流向失败: {e}")
|
||||
return {}
|
||||
|
||||
def _get_market_overview(self):
|
||||
"""获取市场总体情况"""
|
||||
try:
|
||||
# 获取A股市场统计
|
||||
overview = {}
|
||||
|
||||
# 涨跌家数
|
||||
try:
|
||||
df_stat = self._safe_request(ak.stock_zh_a_spot_em)
|
||||
if df_stat is not None and not df_stat.empty:
|
||||
total_count = len(df_stat)
|
||||
up_count = len(df_stat[df_stat['涨跌幅'] > 0])
|
||||
down_count = len(df_stat[df_stat['涨跌幅'] < 0])
|
||||
flat_count = total_count - up_count - down_count
|
||||
|
||||
overview["total_stocks"] = total_count
|
||||
overview["up_count"] = up_count
|
||||
overview["down_count"] = down_count
|
||||
overview["flat_count"] = flat_count
|
||||
overview["up_ratio"] = round(up_count / total_count * 100, 2) if total_count > 0 else 0
|
||||
|
||||
# 涨停跌停
|
||||
limit_up = len(df_stat[df_stat['涨跌幅'] >= 9.5])
|
||||
limit_down = len(df_stat[df_stat['涨跌幅'] <= -9.5])
|
||||
overview["limit_up"] = limit_up
|
||||
overview["limit_down"] = limit_down
|
||||
except:
|
||||
pass
|
||||
|
||||
# 大盘指数
|
||||
try:
|
||||
# 上证指数
|
||||
df_sh = ak.stock_zh_index_spot_em(symbol="上证指数")
|
||||
if df_sh is not None and not df_sh.empty:
|
||||
overview["sh_index"] = {
|
||||
"code": "000001",
|
||||
"name": "上证指数",
|
||||
"close": df_sh.iloc[0].get('最新价', 0),
|
||||
"change_pct": df_sh.iloc[0].get('涨跌幅', 0),
|
||||
"change": df_sh.iloc[0].get('涨跌额', 0)
|
||||
}
|
||||
|
||||
# 深证成指
|
||||
df_sz = self._safe_request(ak.stock_zh_index_spot_em, symbol="深证成指")
|
||||
if df_sz is not None and not df_sz.empty:
|
||||
overview["sz_index"] = {
|
||||
"code": "399001",
|
||||
"name": "深证成指",
|
||||
"close": df_sz.iloc[0].get('最新价', 0),
|
||||
"change_pct": df_sz.iloc[0].get('涨跌幅', 0),
|
||||
"change": df_sz.iloc[0].get('涨跌额', 0)
|
||||
}
|
||||
|
||||
# 创业板指
|
||||
df_cyb = self._safe_request(ak.stock_zh_index_spot_em, symbol="创业板指")
|
||||
if df_cyb is not None and not df_cyb.empty:
|
||||
overview["cyb_index"] = {
|
||||
"code": "399006",
|
||||
"name": "创业板指",
|
||||
"close": df_cyb.iloc[0].get('最新价', 0),
|
||||
"change_pct": df_cyb.iloc[0].get('涨跌幅', 0),
|
||||
"change": df_cyb.iloc[0].get('涨跌额', 0)
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
return overview
|
||||
|
||||
except Exception as e:
|
||||
print(f" 获取市场概况失败: {e}")
|
||||
return {}
|
||||
|
||||
def _get_north_money_flow(self):
|
||||
"""获取北向资金流向"""
|
||||
try:
|
||||
# 获取沪深港通资金流向(使用重试机制)
|
||||
df = self._safe_request(ak.stock_hsgt_fund_flow_summary_em)
|
||||
|
||||
if df is None or df.empty:
|
||||
return {}
|
||||
|
||||
# 获取最新数据
|
||||
latest = df.iloc[0]
|
||||
|
||||
north_flow = {
|
||||
"date": str(latest.get('日期', '')),
|
||||
"north_net_inflow": latest.get('北向资金-成交净买额', 0),
|
||||
"hgt_net_inflow": latest.get('沪股通-成交净买额', 0),
|
||||
"sgt_net_inflow": latest.get('深股通-成交净买额', 0),
|
||||
"north_total_amount": latest.get('北向资金-成交金额', 0)
|
||||
}
|
||||
|
||||
# 获取历史趋势(最近10天)
|
||||
history = []
|
||||
for idx, row in df.head(10).iterrows():
|
||||
history.append({
|
||||
"date": str(row.get('日期', '')),
|
||||
"net_inflow": row.get('北向资金-成交净买额', 0)
|
||||
})
|
||||
north_flow["history"] = history
|
||||
|
||||
return north_flow
|
||||
|
||||
except Exception as e:
|
||||
print(f" 获取北向资金失败: {e}")
|
||||
return {}
|
||||
|
||||
def _get_financial_news(self):
|
||||
"""获取财经新闻"""
|
||||
try:
|
||||
# 获取东方财富财经新闻(使用重试机制)
|
||||
df = self._safe_request(ak.stock_news_em, symbol="全球")
|
||||
|
||||
if df is None or df.empty:
|
||||
return []
|
||||
|
||||
news_list = []
|
||||
for idx, row in df.head(150).iterrows(): # 取前150条
|
||||
news_list.append({
|
||||
"title": row.get('新闻标题', ''),
|
||||
"content": row.get('新闻内容', ''),
|
||||
"publish_time": str(row.get('发布时间', '')),
|
||||
"source": row.get('文章来源', ''),
|
||||
"url": row.get('新闻链接', '')
|
||||
})
|
||||
|
||||
return news_list
|
||||
|
||||
except Exception as e:
|
||||
print(f" 获取财经新闻失败: {e}")
|
||||
return []
|
||||
|
||||
def format_data_for_ai(self, data):
|
||||
"""
|
||||
将数据格式化为适合AI分析的文本格式
|
||||
"""
|
||||
if not data.get("success"):
|
||||
return "数据获取失败"
|
||||
|
||||
text_parts = []
|
||||
|
||||
# 市场概况
|
||||
if data.get("market_overview"):
|
||||
market = data["market_overview"]
|
||||
text_parts.append(f"""
|
||||
【市场总体情况】
|
||||
时间: {data.get('timestamp', 'N/A')}
|
||||
|
||||
大盘指数:
|
||||
""")
|
||||
if market.get("sh_index"):
|
||||
sh = market["sh_index"]
|
||||
text_parts.append(f" 上证指数: {sh['close']} ({sh['change_pct']:+.2f}%)")
|
||||
if market.get("sz_index"):
|
||||
sz = market["sz_index"]
|
||||
text_parts.append(f" 深证成指: {sz['close']} ({sz['change_pct']:+.2f}%)")
|
||||
if market.get("cyb_index"):
|
||||
cyb = market["cyb_index"]
|
||||
text_parts.append(f" 创业板指: {cyb['close']} ({cyb['change_pct']:+.2f}%)")
|
||||
|
||||
if market.get("total_stocks"):
|
||||
text_parts.append(f"""
|
||||
市场统计:
|
||||
总股票数: {market['total_stocks']}
|
||||
上涨: {market['up_count']} ({market['up_ratio']:.1f}%)
|
||||
下跌: {market['down_count']}
|
||||
平盘: {market['flat_count']}
|
||||
涨停: {market['limit_up']}
|
||||
跌停: {market['limit_down']}
|
||||
""")
|
||||
|
||||
# 北向资金
|
||||
if data.get("north_flow"):
|
||||
north = data["north_flow"]
|
||||
text_parts.append(f"""
|
||||
【北向资金流向】
|
||||
日期: {north.get('date', 'N/A')}
|
||||
北向资金净流入: {north.get('north_net_inflow', 0):.2f} 万元
|
||||
沪股通: {north.get('hgt_net_inflow', 0):.2f} 万元
|
||||
深股通: {north.get('sgt_net_inflow', 0):.2f} 万元
|
||||
""")
|
||||
|
||||
# 行业板块表现(前20)
|
||||
if data.get("sectors"):
|
||||
sectors = data["sectors"]
|
||||
sorted_sectors = sorted(sectors.items(), key=lambda x: x[1]["change_pct"], reverse=True)
|
||||
|
||||
text_parts.append(f"""
|
||||
【行业板块表现 TOP20】
|
||||
涨幅榜前10:
|
||||
""")
|
||||
for name, info in sorted_sectors[:10]:
|
||||
text_parts.append(f" {name}: {info['change_pct']:+.2f}% | 领涨: {info['top_stock']} ({info['top_stock_change']:+.2f}%)")
|
||||
|
||||
text_parts.append(f"""
|
||||
跌幅榜前10:
|
||||
""")
|
||||
for name, info in sorted_sectors[-10:]:
|
||||
text_parts.append(f" {name}: {info['change_pct']:+.2f}% | 领跌: {info['top_stock']} ({info['top_stock_change']:+.2f}%)")
|
||||
|
||||
# 概念板块表现(前20)
|
||||
if data.get("concepts"):
|
||||
concepts = data["concepts"]
|
||||
sorted_concepts = sorted(concepts.items(), key=lambda x: x[1]["change_pct"], reverse=True)
|
||||
|
||||
text_parts.append(f"""
|
||||
【概念板块表现 TOP20】
|
||||
涨幅榜前10:
|
||||
""")
|
||||
for name, info in sorted_concepts[:10]:
|
||||
text_parts.append(f" {name}: {info['change_pct']:+.2f}% | 领涨: {info['top_stock']} ({info['top_stock_change']:+.2f}%)")
|
||||
|
||||
# 板块资金流向(前15)
|
||||
if data.get("sector_fund_flow") and data["sector_fund_flow"].get("today"):
|
||||
flow = data["sector_fund_flow"]["today"]
|
||||
|
||||
text_parts.append(f"""
|
||||
【行业资金流向 TOP15】
|
||||
主力资金净流入前15:
|
||||
""")
|
||||
sorted_flow = sorted(flow, key=lambda x: x["main_net_inflow"], reverse=True)
|
||||
for item in sorted_flow[:15]:
|
||||
text_parts.append(f" {item['sector']}: {item['main_net_inflow']:.2f}万 ({item['main_net_inflow_pct']:+.2f}%) | 涨跌: {item['change_pct']:+.2f}%")
|
||||
|
||||
# 重要新闻(前20条)
|
||||
if data.get("news"):
|
||||
text_parts.append(f"""
|
||||
【重要财经新闻 TOP20】
|
||||
""")
|
||||
for idx, news in enumerate(data["news"][:20], 1):
|
||||
text_parts.append(f"{idx}. [{news['publish_time']}] {news['title']}")
|
||||
if news.get('content') and len(news['content']) > 100:
|
||||
text_parts.append(f" {news['content'][:100]}...")
|
||||
|
||||
return "\n".join(text_parts)
|
||||
|
||||
|
||||
# 测试函数
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("测试智策板块数据采集模块")
|
||||
print("=" * 60)
|
||||
|
||||
fetcher = SectorStrategyDataFetcher()
|
||||
data = fetcher.get_all_sector_data()
|
||||
|
||||
if data.get("success"):
|
||||
print("\n" + "=" * 60)
|
||||
print("数据采集成功!")
|
||||
print("=" * 60)
|
||||
|
||||
formatted_text = fetcher.format_data_for_ai(data)
|
||||
print(formatted_text[:3000]) # 显示前3000字符
|
||||
print(f"\n... (总长度: {len(formatted_text)} 字符)")
|
||||
else:
|
||||
print(f"\n数据采集失败: {data.get('error', '未知错误')}")
|
||||
|
||||
Reference in New Issue
Block a user