feat: 五维推荐评分 + 优中选优Top5 + exec gate评分驱动 + 自动补tag + XMPP完整策略
This commit is contained in:
@@ -149,6 +149,7 @@ def get_watch():
|
||||
hs.action, hs.position_advice, hs.tag,
|
||||
hs.entry_low, hs.entry_high, hs.stop_loss, hs.take_profit,
|
||||
hs.rr_ratio, hs.rr_low, hs.rr_high, hs.full_analysis, hs.reassessed_at,
|
||||
hs.rec_score,
|
||||
lp.price, lp.change_pct,
|
||||
h.shares, h.position_pct
|
||||
FROM holding_strategies hs
|
||||
@@ -233,28 +234,29 @@ def get_watch():
|
||||
# 卖出类永远可执行(释放现金,不占买入预算),排最前;买入类按 RR 降序
|
||||
_sells = [d for d in _cands if (d.get('timing_signal') or '') in _SELL_SIGS]
|
||||
_buys = [d for d in _cands if (d.get('timing_signal') or '') not in _SELL_SIGS]
|
||||
_buys.sort(key=lambda x: x.get('rr_ratio') or 0, reverse=True)
|
||||
_buys.sort(key=lambda x: (x.get('rec_score') or 0, x.get('rr_ratio') or 0), reverse=True)
|
||||
for d in _sells:
|
||||
d['rec_exec'] = True # 卖出不需要现金,永远可执行
|
||||
d['suggested_position_pct'] = 0.0
|
||||
# 买入:RR≥1.5 才有可执行资格(prompt 自己的纪律:RR<1.5→不推荐);
|
||||
# 买入:score≥60 + RR≥2.0 才有可执行资格(2026-07-27 老爸:五维评分替代纯RR)
|
||||
# 达标者贪心装入现金预算,预算外/不达标标记"排队"(不再降级隐藏)
|
||||
_cum = 0.0
|
||||
# 弱信号不可执行(2026-07-23 老爸:信号不充分+盈利持有为何可执行?——
|
||||
# tag是LLM行动信号时的遗留,技术路径降级信号后无权摘tag,徽章层必须自己卡信号)
|
||||
# 弱信号不可执行
|
||||
_WEAK_SIGNALS = ('信号不充分', '关注', '弱势持有', '观望', '持有', '')
|
||||
_TOP_N = 5 # 优中选优:推荐区只展示 Top 5(剩余排入自选区)
|
||||
for d in _buys:
|
||||
pct = _sugg_pct(d)
|
||||
d['suggested_position_pct'] = pct
|
||||
rr = d.get('rr_ratio') or 0
|
||||
score = d.get('rec_score') or 0
|
||||
sig_now = d.get('timing_signal') or ''
|
||||
# 仓位必须明确%("减仓或观望/中等仓位"不算可执行的仓位——2026-07-24 老爸)
|
||||
# 仓位必须明确%
|
||||
_has_pos = bool(_re.search(r'(\d+(?:\.\d+)?)\s*%', d.get('position_advice') or ''))
|
||||
if sig_now in _WEAK_SIGNALS:
|
||||
d['rec_exec'] = False # 弱信号永远排队
|
||||
elif not _has_pos:
|
||||
d['rec_exec'] = False # 无明确仓位,排队
|
||||
elif rr >= 2.0 and _cum + pct <= _budget_pct + 1e-9:
|
||||
elif score >= 60 and rr >= 2.0 and _cum + pct <= _budget_pct + 1e-9:
|
||||
d['rec_exec'] = True # 可执行(2026-07-24 老爸:门槛1.5→2.0,边缘推荐不算优)
|
||||
_cum += pct
|
||||
else:
|
||||
@@ -264,6 +266,14 @@ def get_watch():
|
||||
if d['sort_group'] == 0 and not _is_fresh(d):
|
||||
d['sort_group'] = 1 if d['decision_type'] == '持仓策略' else 2
|
||||
|
||||
# ── 优中选优(2026-07-27 老爸):推荐区只展示 Top 5 买入,太多选不过来 ──
|
||||
# 卖出/止盈永远保留在推荐区;买入按评分降序,第6名起降入自选区
|
||||
_rec_buys = [d for d in results if d['sort_group'] == 0
|
||||
and (d.get('timing_signal') or '') not in _SELL_SIGS]
|
||||
_rec_buys.sort(key=lambda x: (x.get('rec_score') or 0, x.get('rr_ratio') or 0), reverse=True)
|
||||
for d in _rec_buys[_TOP_N:]:
|
||||
d['sort_group'] = 2 # 超额买入降入自选区
|
||||
|
||||
# 排序:group → signal_rank → group-internal (持仓按position_pct desc, 自选按rr desc)
|
||||
def skey(x):
|
||||
g = x['sort_group']
|
||||
@@ -467,6 +477,7 @@ def api_watchlist():
|
||||
WHEN hs.timing_signal IN ('弱势持有') THEN 4
|
||||
ELSE 5
|
||||
END,
|
||||
COALESCE(hs.rec_score, 0) DESC,
|
||||
COALESCE(hs.rr_ratio,0) DESC,
|
||||
hs.code
|
||||
""").fetchall()
|
||||
@@ -535,7 +546,7 @@ def api_stock(code):
|
||||
SELECT hs.code, hs.name, hs.timing_signal, hs.action, hs.position_advice,
|
||||
hs.entry_low, hs.entry_high, hs.stop_loss, hs.take_profit,
|
||||
hs.rr_ratio, hs.rr_low, hs.rr_high, hs.full_analysis, hs.reassessed_at,
|
||||
hs.tag, hs.decision_type,
|
||||
hs.tag, hs.decision_type, hs.rec_score,
|
||||
lp.price AS live_price, lp.change_pct
|
||||
FROM holding_strategies hs
|
||||
LEFT JOIN live_prices lp ON hs.code = lp.code
|
||||
|
||||
Reference in New Issue
Block a user