#!/usr/bin/env python3 # -*- coding: utf-8 -*- """leader_scanner.py — MoFin 龙头识别策略(bull_trend 适用,2026-08-13 落地) 核心逻辑(12维方法论 + 龙头识别低波动优化版理念): - 适用状态:bull_trend(above_ma20 + rsi>55 + adx>20) - 入场:龙头股回调到 MA20 附近(趋势中的健康回调,不追高) - 12维条件(复用 stock_indicators 已有字段,无拍脑袋): 1. 个股 close > MA20(趋势向上) 2. dist_ma20 ∈ [-3%, +2%](回调到 MA20 附近,不追高) 3. 行业强势(sector_above_ma20=1 或 sector_ret20>0,需 sector 数据) 4. 市值/流动性过滤(mcap_q > 0.3,龙头非小盘) 5. RSI ∈ [45, 65](强势但未超买) 6. 量能配合(vol_ratio > 0.8,非极度缩量) - 出场:跌破 MA20 或达到上方压力位 - RR:压力位(前高/筹码阻力)/ 支撑位(MA20)计算 注意:这是初版框架,需 MoFin 引擎验证达标(年化≥13.2%)才正式上线 """ import sqlite3 import json from datetime import datetime from pathlib import Path DB = "/home/hmo/MoFin/data/mofin.db" OUT = Path("/home/hmo/MoFin/data/leader_signals.json") def load_market_state(): p = Path("/home/hmo/MoFin/data/market_state.json") if p.exists(): return json.loads(p.read_text(encoding="utf-8")) return {"state": "neutral"} def scan_leaders(): """扫描龙头股回调买点(bull_trend 状态)""" ms = load_market_state() state = ms.get("state", "neutral") if state != "bull_trend": print(f"当前状态 {state},非 bull_trend,不扫描龙头(避免追高)") return [] c = sqlite3.connect(DB) # 最新交易日 row = c.execute("SELECT MAX(date) FROM stock_indicators").fetchone() if not row or not row[0]: c.close() return [] latest = row[0] print(f"扫描日期: {latest}") # 龙头条件(12维) rows = c.execute( """SELECT code, ma20, rsi, dist_ma20, mcap_q, pe_q, vol_ratio, trend_aligned FROM stock_indicators WHERE date=? AND ma20 IS NOT NULL AND rsi IS NOT NULL""", (latest,) ).fetchall() c.close() signals = [] for r in rows: code, ma20, rsi, dist_ma20, mcap_q, pe_q, vol_ratio, trend_aligned = r # 条件1: 趋势向上(close > MA20 → dist_ma20 > 0,或接近) if dist_ma20 is None or dist_ma20 < -3 or dist_ma20 > 2: continue # 条件2: RSI 强势未超买 if rsi < 45 or rsi > 65: continue # 条件3: 市值/流动性(龙头非小盘,mcap_q > 0.3) if mcap_q is not None and mcap_q < 0.3: continue # 条件4: 量能配合 if vol_ratio is not None and vol_ratio < 0.8: continue # 条件5: 趋势共振(trend_aligned=1) if trend_aligned != 1: continue signals.append({ "code": code, "ma20": ma20, "rsi": rsi, "dist_ma20": dist_ma20, "mcap_q": mcap_q, "pe_q": pe_q, "vol_ratio": vol_ratio, "entry_reason": "龙头回调MA20", }) # 按 dist_ma20 排序(最接近 MA20 的优先) signals.sort(key=lambda x: abs(x["dist_ma20"])) print(f"龙头信号: {len(signals)} 只") for s in signals[:5]: print(f" {s['code']}: dist_ma20={s['dist_ma20']:.1f}% rsi={s['rsi']:.1f} mcap_q={s['mcap_q']}") return signals def main(): signals = scan_leaders() out = { "state": load_market_state().get("state", "neutral"), "signals": signals, "count": len(signals), "updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), } OUT.write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8") print(f"leader_signals.json 写入") if __name__ == "__main__": main()