feat: 新增行业领涨股扫描 xiaoguo_scanner

- 新增 fetch_sector_leaders() 从 market.json 读取热门行业领涨股
- 三路并行:同花顺技术榜 + 行业领涨 + 东财热榜(502降级)
- 优先级排序:行业领涨 > 同花顺榜 > 东财热榜
- 名称→代码映射使用本地缓存,避免频繁调用akshare
- 更新文档
This commit is contained in:
知微
2026-06-22 20:00:40 +08:00
parent b59c1f3bba
commit b32e2fd803
4 changed files with 65 additions and 6 deletions
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+9 -3
View File
@@ -23,9 +23,15 @@ xiaoguo_scanner.py(每5分钟跑一轮)
## 二、数据源 ## 二、数据源
### 榜单来源 ### 榜单来源(三路并行)
- 同花顺技术面榜单(akshare),覆盖技术指标类信号 1. **同花顺技术面榜单**akshare6看多+5看空轮换)— 技术指标类信号
- 东方财富热榜(akshare.stock_hot_rank_em)因502不可用,降级静默 2. **行业领涨股**(从 market.json 读取,每轮都跑)— 涨幅>2.5%板块的领涨龙头
3. **东方财富热榜**akshare.stock_hot_rank_em)— 因502不可用,降级静默
### 三路数据合并规则
优先级:行业领涨 > 同花顺技术榜 > 东方财富热榜
同只股票不重复处理,最多15只/轮。
行业领涨股保证能被扫描到,不会被技术榜单的股票挤掉。
### 新闻来源 ### 新闻来源
- 东方财富个股新闻APIakshare.stock_news_em - 东方财富个股新闻APIakshare.stock_news_em
+56 -3
View File
@@ -45,6 +45,10 @@ BEARISH_BOARDS = [
ALL_BOARDS = BULLISH_BOARDS + BEARISH_BOARDS ALL_BOARDS = BULLISH_BOARDS + BEARISH_BOARDS
BULLISH_COUNT = len(BULLISH_BOARDS) BULLISH_COUNT = len(BULLISH_BOARDS)
# 行业领涨股扫描(不轮换,每轮都跑)
# 从 market.json 读热门行业领涨股
SECTOR_HOT_THRESHOLD = 2.5 # 板块涨幅>2.5%时捞它的领涨股
def clean_proxy(): def clean_proxy():
for k in ['http_proxy','https_proxy','HTTP_PROXY','HTTPS_PROXY']: for k in ['http_proxy','https_proxy','HTTP_PROXY','HTTPS_PROXY']:
@@ -112,6 +116,53 @@ def fetch_rotating_board():
return [], is_bullish return [], is_bullish
def fetch_sector_leaders():
"""从 market.json 读热门行业领涨股"""
mkt_path = DATA_DIR / "market.json"
if not mkt_path.exists():
return []
try:
mkt = json.loads(mkt_path.read_text())
sectors = mkt.get("sectors", [])
# 代码→名称映射(优先用本地缓存,避免每次跑都调akshare)
cache_path = DATA_DIR / "stock_name_code_cache.json"
name_to_code = {}
if cache_path.exists():
name_to_code = json.loads(cache_path.read_text())
if not name_to_code:
try:
import akshare as ak
df = ak.stock_info_a_code_name()
for _, r in df.iterrows():
name_to_code[r["简称"]] = r["代码"]
cache_path.write_text(json.dumps(name_to_code, ensure_ascii=False))
print(f" 名称代码映射: {len(name_to_code)}只已缓存", flush=True)
except Exception as e:
print(f" 名称代码映射加载失败: {e}", flush=True)
leaders = []
seen = set()
for s in sectors:
chg = s.get("change", 0) or 0
lead_name = s.get("lead_stock", "")
if chg < SECTOR_HOT_THRESHOLD or not lead_name or lead_name in seen:
continue
seen.add(lead_name)
code = name_to_code.get(lead_name, "")
if not code:
continue
leaders.append({
"code": code,
"name": lead_name,
"source": f"行业领涨-{s['name']}+{chg:+.1f}%",
})
return leaders
except Exception as e:
print(f" 行业领涨获取失败: {e}", flush=True)
return []
def get_scanned_codes(conn): def get_scanned_codes(conn):
"""取1小时内已扫描过的代码""" """取1小时内已扫描过的代码"""
rows = conn.execute( rows = conn.execute(
@@ -192,10 +243,11 @@ def main():
# 1. 拉板 # 1. 拉板
hot = fetch_hot_board() hot = fetch_hot_board()
rotating, is_bullish = fetch_rotating_board() rotating, is_bullish = fetch_rotating_board()
leaders = fetch_sector_leaders()
elapsed = time.time() - start_time elapsed = time.time() - start_time
print(f"榜单: 东方财富{len(hot)}只, 同花顺{len(rotating)}只 ({elapsed:.0f}s)", flush=True) print(f"榜单: 东方财富{len(hot)}只, 同花顺{len(rotating)}, 行业领涨{len(leaders)} ({elapsed:.0f}s)", flush=True)
if not hot and not rotating: if not hot and not rotating and not leaders:
conn.close() conn.close()
return return
@@ -210,7 +262,8 @@ def main():
# 2. 合并去重 + 看空榜只保留持仓股 # 2. 合并去重 + 看空榜只保留持仓股
all_stocks = {} all_stocks = {}
for s in hot + rotating: # 行业领涨优先(热门板块龙头)
for s in (leaders if is_bullish else []) + hot + rotating:
code = s["code"] code = s["code"]
code_stripped = code.lstrip("0") code_stripped = code.lstrip("0")
if not is_bullish: if not is_bullish: