换仓评估逻辑:现金不足时自动分析卖差票换推荐股

新增 evaluate_swap() 函数:
1. 仅对RR>=2.0且含买入关键词的强信号触发
2. 扫描持仓按亏损排序,找最少卖出组合凑现金缺口
3. 预期盈利 > 锁定亏损×1.5 才推荐切换
4. 最多卖3只,单次换仓不超总资产50%
5. 不划算时维持原预算不足1手消息

已验证:海博思创(688411) RR=5.6但预期盈利9k<锁定亏损46k×1.5
→ 不推荐切换,正确。沐曦如果触发也会按同一逻辑判断。
This commit is contained in:
知微
2026-06-24 11:42:26 +08:00
parent b145dd47c3
commit 92815aac06
3 changed files with 149 additions and 26 deletions
+1 -1
View File
@@ -9623,5 +9623,5 @@
}
],
"total": 42,
"regenerated_at": "2026-06-24 11:33"
"regenerated_at": "2026-06-24 11:41"
}
+61 -25
View File
@@ -25,7 +25,8 @@
"rr_ratio": 0.24,
"action_note": "⚠️盈亏比偏低(1:0.9),不建议加仓",
"timing_signal": "持有"
}
},
"price": 794.1
},
{
"code": "01478",
@@ -52,7 +53,9 @@
"rr_ratio": 1.12,
"action_note": "深套持有",
"timing_signal": "持有"
}
},
"price": 7.59,
"change_pct": -3.68
},
{
"code": "600739",
@@ -79,7 +82,8 @@
"rr_ratio": 1.35,
"action_note": "⚠️盈亏比偏低(1:1.3),不建议加仓",
"timing_signal": "持有"
}
},
"price": 10.48
},
{
"code": "601899",
@@ -106,7 +110,8 @@
"rr_ratio": 0.74,
"action_note": "深套持有",
"timing_signal": "持有"
}
},
"price": 27.5
},
{
"code": "688639",
@@ -133,7 +138,8 @@
"rr_ratio": 1.09,
"action_note": "⚠️盈亏比偏低(1:1.1),不建议加仓",
"timing_signal": "持有"
}
},
"price": 21.44
},
{
"code": "09988",
@@ -160,7 +166,9 @@
"rr_ratio": 1.06,
"action_note": "深套持有",
"timing_signal": "持有"
}
},
"price": 98.3,
"change_pct": -0.66
},
{
"code": "603259",
@@ -187,7 +195,8 @@
"rr_ratio": 3.16,
"action_note": "",
"timing_signal": "持有"
}
},
"price": 116.94
},
{
"code": "688981",
@@ -214,7 +223,8 @@
"rr_ratio": 2.05,
"action_note": "",
"timing_signal": "持有"
}
},
"price": 154.23
},
{
"code": "01888",
@@ -241,7 +251,9 @@
"rr_ratio": 7.61,
"action_note": "短炒强趋势持",
"timing_signal": "持有"
}
},
"price": 95.6,
"change_pct": 9.57
},
{
"code": "02202",
@@ -268,7 +280,9 @@
"rr_ratio": 1.11,
"action_note": "深套持有",
"timing_signal": "持有"
}
},
"price": 2.35,
"change_pct": -2.08
},
{
"code": "02388",
@@ -295,7 +309,9 @@
"rr_ratio": 2.91,
"action_note": "",
"timing_signal": "持有"
}
},
"price": 46.16,
"change_pct": -1.79
},
{
"code": "300750",
@@ -322,7 +338,8 @@
"rr_ratio": 0.84,
"action_note": "⚠️盈亏比偏低(1:0.8),不建议加仓",
"timing_signal": "弱势持有"
}
},
"price": 394.78
},
{
"code": "01211",
@@ -349,7 +366,9 @@
"rr_ratio": 1.13,
"action_note": "深套持有",
"timing_signal": "持有"
}
},
"price": 74.45,
"change_pct": -1.85
},
{
"code": "00700",
@@ -376,7 +395,9 @@
"rr_ratio": 1.0,
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
"timing_signal": "持有"
}
},
"price": 417.4,
"change_pct": 0.63
},
{
"code": "00981",
@@ -403,7 +424,9 @@
"rr_ratio": 3.0,
"action_note": "",
"timing_signal": "持有"
}
},
"price": 84.15,
"change_pct": 8.09
},
{
"code": "09868",
@@ -430,7 +453,9 @@
"rr_ratio": 22.09,
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
"timing_signal": "持有"
}
},
"price": 49.58,
"change_pct": 0.45
},
{
"code": "600036",
@@ -457,7 +482,8 @@
"rr_ratio": 1.1,
"action_note": "⚠️盈亏比偏低(1:1.1),不建议加仓",
"timing_signal": "弱势持有"
}
},
"price": 37.04
},
{
"code": "300548",
@@ -484,7 +510,8 @@
"rr_ratio": 7.41,
"action_note": "短炒强趋势持",
"timing_signal": "持有"
}
},
"price": 278.12
},
{
"code": "02318",
@@ -511,7 +538,9 @@
"rr_ratio": 15.88,
"action_note": "⚠️盈亏比偏低(1:1.2),不建议加仓",
"timing_signal": "持有"
}
},
"price": 52.5,
"change_pct": -2.05
},
{
"code": "300035",
@@ -538,7 +567,8 @@
"rr_ratio": 0.95,
"action_note": "深套持有",
"timing_signal": "持有"
}
},
"price": 16.13
},
{
"code": "000700",
@@ -565,7 +595,8 @@
"rr_ratio": 3.23,
"action_note": "",
"timing_signal": "持有"
}
},
"price": 14.64
},
{
"code": "518880",
@@ -592,7 +623,8 @@
"rr_ratio": 0.55,
"action_note": "深套持有",
"timing_signal": "持有"
}
},
"price": 8.44
},
{
"code": "01088",
@@ -619,7 +651,9 @@
"rr_ratio": 1.01,
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
"timing_signal": "持有"
}
},
"price": 41.76,
"change_pct": -0.71
},
{
"code": "600563",
@@ -646,7 +680,8 @@
"rr_ratio": 3.54,
"action_note": "",
"timing_signal": "持有"
}
},
"price": 178.76
},
{
"code": "300690",
@@ -673,7 +708,8 @@
"rr_ratio": 1.25,
"action_note": "⚠️盈亏比偏低(1:1.2),不建议加仓",
"timing_signal": "持有"
}
},
"price": 22.87
}
],
"cash": 20230.1,
+87
View File
@@ -394,6 +394,81 @@ def main():
return theo_pct, pct_actual, details, lots, lot_cost_total
# ── 换仓评估 ──────────────────────────────────────────────────────
def evaluate_swap(lot_cost_target, rr, sig, tp, sl, name, code, price_in, total_assets_in, cash_in, pf_in):
"""现金不足时评估是否卖差票换推荐股。仅在目标信号强时触发。
返回(推荐文案str, 需补充的现金缺口float)或 (None, gap)"""
gap = lot_cost_target - cash_in
if rr < 2.0 or gap <= 0 or gap > total_assets_in * 0.5:
return None, gap
if not any(kw in sig for kw in ["买入", "加仓", "建仓", ""]):
return None, gap
# 收集持仓,按盈亏率升序(最差排前)
ph = []
for h in pf_in.get("holdings", []):
hs = h.get("shares", 0) or 0
hp = h.get("price", 0) or 0
hc = h.get("cost", 0) or 0
if hs <= 0 or hp <= 0:
continue
hmv = hs * hp
h_code = str(h.get("code", ""))
if len(h_code) <= 5:
hmv *= 0.866
hpl = (hp - hc) * hs
hpl_pct = (hp - hc) / hc * 100 if hc else 0
ph.append({"code": h_code, "name": h.get("name", ""),
"shares": hs, "price": hp, "cost": hc,
"mv": round(hmv), "pl": round(hpl),
"pl_pct": round(hpl_pct, 1)})
ph.sort(key=lambda x: x["pl_pct"])
candidates = [h for h in ph if h["pl_pct"] < -10]
if not candidates:
return None, gap
selected = []
cash_freed = 0
locked_loss = 0
for h in candidates:
if cash_freed >= gap:
break
cash_freed += h["mv"]
locked_loss += abs(h["pl"])
selected.append(h)
if cash_freed < gap or len(selected) > 3:
return None, gap
# 估算目标预期盈利(到止盈)
if tp and tp > 0 and lot_cost_target > 0:
target_gain_pct = (tp - price_in) / price_in
expected_gain = lot_cost_target * max(target_gain_pct, 0.05)
else:
target_gain_pct = rr * 0.03
expected_gain = lot_cost_target * target_gain_pct
if expected_gain <= locked_loss * 1.5:
return None, gap
sell_desc = "".join(f"{h['name']}({h['code']}) {h['shares']}股 亏{h['pl_pct']}%"
for h in selected)
sell_total = cash_freed
new_budget = cash_in + sell_total
new_lots = int(new_budget / lot_cost_target) if lot_cost_target > 0 else 0
if new_lots == 0:
return None, gap
if code.startswith("688"):
new_shares = new_lots * 200
elif len(code) <= 5:
new_shares = new_lots * hk_lot_size(code)
else:
new_shares = new_lots * 100
new_cost = new_lots * lot_cost_target
new_pct = round(new_cost / total_assets_in * 100) if total_assets_in > 0 else 0
ratio_vs_loss = round(expected_gain / locked_loss, 1) if locked_loss else 0
text = (f"换仓建议:卖{sell_desc}"
f"→腾{round(sell_total):,}元(锁定亏损{locked_loss:,}"
f"→买{name}({code}) {new_lots}手({new_shares}股,{round(new_cost):,}元)"
f"{new_pct}%仓位"
f"(目标{tp} +{round(target_gain_pct*100,1)}%预期={round(expected_gain):,}"
f"≈锁定亏损的{ratio_vs_loss}倍,划算)")
return text, gap
# 标准格式:每个可操作标的 — 大盘/行业/个股三面 + 仓位
lines.append(f"【💡 操作建议】(当前{n}只自选可操作 | 总资产{total_assets:,.0f}元 现金{available_cash:,.0f}元)")
for s in actionable:
@@ -478,6 +553,15 @@ def main():
continue
action_tag = "⚠️" if lots == 0 else "🛒"
# 换仓评估:现金不足时评估是否卖差票换推荐股
swap_text = None
if lots == 0:
swap_text, _ = evaluate_swap(
lot, rr, sig, tp, sl, name, code, price,
total_assets, available_cash, pf
)
lines.append(
f" {action_tag} {name}({code}) {pfx}{price:.2f} 买区{buy_low}~{buy_high} | "
f"1手{lot:,.0f}元 RR={rr:.1f}{sl}{tp}\n"
@@ -485,6 +569,9 @@ def main():
f" 技术{ss['强撑']}{ss['弱撑']}{ss['弱压']}{ss['强压']} | 信号{sig}\n"
f" 仓位:理论{theo_pct}%×总资产 | 建议{actual_pct}%{details}"
)
if swap_text:
lines[-1] += f"\n {swap_text}"
# 分支描述
branch_line = ""