信义光能问题修复:两层级过滤+趋势检查
Bug 1 — strategy_lifecycle.py: enrich_timing_signal 用 factors[-1] 当信号
信义光能: base_signal=neutral, factors=[大盘中性,行业偏弱,高估值]
旧逻辑: factors[-1]='行业偏弱'→成为timing_signal→无效信号
新逻辑: 先找有效操作方向(买入/加仓/观望/持有/关注/信号不充分),
找不到→信号不充分。不再从上下文因子里拼凑信号。
Bug 2 — stale_push_wlin.py: 信号过滤太松
旧逻辑: 只跳过特定关键词(等企稳/关注/信号不充分/持有)
新逻辑: 信号必须含"买入"或"加仓"才进推荐,其他一律跳过
Check 3 — 趋势检查(新增)
fetch_trend_data(): 取实时行情+30日K线计算MA排列
空头排列/弱势震荡→不推荐
药明康德通过(多头排列+买入信号) 信义光能不通过(空头+行业偏弱)
This commit is contained in:
@@ -27,6 +27,72 @@ except ImportError:
|
||||
sys.path.insert(0, "/home/hmo/MoFin/scripts")
|
||||
from stock_scorer import score_future_outlook, is_hk_stock, settlement_delay_note
|
||||
|
||||
# ── 趋势检查 ────────────────────────────────────────────────────
|
||||
def fetch_trend_data(code):
|
||||
"""取均线数据判断趋势状态。返回 (current_price, ma5, trend_label) 或 None"""
|
||||
try:
|
||||
prefix = "sh" if code.startswith(('60','68','51','56','50')) else "sz" if code.startswith(('00','30','15')) else "hk"
|
||||
url = f"http://qt.gtimg.cn/q={prefix}{code}"
|
||||
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
|
||||
resp = urlopen(req, timeout=5).read().decode('gbk')
|
||||
fld = resp.split('=')[1].strip().strip('"').strip(';').split('~')
|
||||
current = float(fld[3]) if len(fld) > 3 else 0
|
||||
except:
|
||||
return None
|
||||
|
||||
try:
|
||||
url = f"http://ifzq.gtimg.cn/appstock/app/fqkline/get?param={prefix}{code},day,,,30,qfq"
|
||||
req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
|
||||
resp = urlopen(req, timeout=5).read().decode('utf-8')
|
||||
data = json.loads(resp)
|
||||
day_key = 'qfqday' if prefix != 'hk' else 'day'
|
||||
bars = data.get('data', {}).get(f'{prefix}{code}', {}).get(day_key, [])
|
||||
except:
|
||||
return None
|
||||
|
||||
if not bars or current <= 0:
|
||||
return None
|
||||
closes = [float(b[2]) for b in bars]
|
||||
if len(closes) < 5:
|
||||
return None
|
||||
|
||||
def ma(n):
|
||||
return sum(closes[-n:]) / n
|
||||
ma5 = ma(5)
|
||||
ma10 = ma(10) if len(closes) >= 10 else None
|
||||
ma20 = ma(20) if len(closes) >= 20 else None
|
||||
|
||||
# 趋势分析
|
||||
pct_above_ma5 = (current - ma5) / ma5 * 100
|
||||
uptrend = False
|
||||
|
||||
if ma20 and ma10:
|
||||
if ma5 > ma10 > ma20:
|
||||
trend_label = "多头排列"
|
||||
uptrend = True
|
||||
elif current < ma5 and ma5 < ma10 and current < ma10:
|
||||
trend_label = "空头排列"
|
||||
elif current > ma5 and ma5 > ma10:
|
||||
trend_label = "短期转强"
|
||||
uptrend = True
|
||||
else:
|
||||
trend_label = "震荡"
|
||||
if current > ma5 > ma10:
|
||||
uptrend = True
|
||||
else:
|
||||
trend_label = "数据不足"
|
||||
|
||||
return {
|
||||
'price': current,
|
||||
'ma5': round(ma5, 2),
|
||||
'ma10': round(ma10, 2) if ma10 else None,
|
||||
'ma20': round(ma20, 2) if ma20 else None,
|
||||
'pct_above_ma5': round(pct_above_ma5, 1),
|
||||
'trend': trend_label,
|
||||
'uptrend': uptrend,
|
||||
}
|
||||
|
||||
# ── XMPP
|
||||
XMPP_BRIDGE = "http://127.0.0.1:5805/"
|
||||
XMPP_USER = "hmo@yoin.fun"
|
||||
|
||||
@@ -278,10 +344,11 @@ def main():
|
||||
-code_data.get(s[1], {}).get("rr_ratio", 0)
|
||||
))
|
||||
|
||||
# 只展示有清晰操作信号的个股:不含"等企稳""关注""信号不充分""neutral"及纯持有信号
|
||||
SKIP_KEYWORDS = ["等企稳", "关注", "信号不充分"]
|
||||
|
||||
BUY_KEYWORDS = ["买入", "加仓"]
|
||||
# 只展示有清晰操作信号的个股
|
||||
# timing_signal 必须是明确操作方向:买入/加仓/观望/关注/信号不充分
|
||||
# 行业描述(行业偏弱/行业偏强/大盘变盘等)不是操作信号,一律跳过
|
||||
VALID_SIGNALS = {"买入", "加仓", "观望", "关注", "信号不充分"}
|
||||
SKIP_KEYWORDS = ["等企稳", "信号不充分"]
|
||||
|
||||
actionable = []
|
||||
for s in stocks:
|
||||
@@ -292,9 +359,19 @@ def main():
|
||||
if any(kw in sig for kw in SKIP_KEYWORDS):
|
||||
continue
|
||||
# 中性信号跳过
|
||||
stripped = sig.strip().lower()
|
||||
if not stripped or stripped in ("", "neutral", "持有", "深套持有", "弱势持有"):
|
||||
stripped = sig.strip()
|
||||
if not stripped or stripped.lower() in ("", "neutral", "持有", "深套持有", "弱势持有"):
|
||||
continue
|
||||
# 信号必须含买入/加仓才推荐——其他非操作信号跳过
|
||||
if not any(kw in sig for kw in ["买入", "加仓"]):
|
||||
continue
|
||||
# 趋势检查:必须不是空头排列(价格在MA5以下且MA5<MA10)
|
||||
trend = fetch_trend_data(s[1])
|
||||
if trend:
|
||||
if not trend['uptrend']:
|
||||
# 空头排列或弱势震荡,不推荐
|
||||
continue
|
||||
# (如果趋势数据获取失败,放行—不因数据问题错杀)
|
||||
actionable.append(s)
|
||||
|
||||
if not actionable:
|
||||
@@ -613,6 +690,15 @@ def main():
|
||||
cooled, elapsed, cd_key = in_cooldown(code, branch_action, cooldown)
|
||||
if cooled:
|
||||
continue
|
||||
|
||||
# 策略质量过滤:只有正向/中性信号才推操作建议
|
||||
bad_keywords = ["偏弱", "弱势", "观望", "卖出", "回避", "回避"]
|
||||
if any(kw in sig for kw in bad_keywords):
|
||||
continue
|
||||
|
||||
# 行业背景过滤:行业大跌时不在买入区推荐(即使个股信号好)
|
||||
if "大跌" in sector:
|
||||
continue
|
||||
|
||||
# 换仓评估:现金不足时评估是否卖差票换推荐股
|
||||
swap_text = None
|
||||
|
||||
Reference in New Issue
Block a user