换仓评估逻辑:现金不足时自动分析卖差票换推荐股
新增 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:
+1
-1
@@ -9623,5 +9623,5 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total": 42,
|
"total": 42,
|
||||||
"regenerated_at": "2026-06-24 11:33"
|
"regenerated_at": "2026-06-24 11:41"
|
||||||
}
|
}
|
||||||
+61
-25
@@ -25,7 +25,8 @@
|
|||||||
"rr_ratio": 0.24,
|
"rr_ratio": 0.24,
|
||||||
"action_note": "⚠️盈亏比偏低(1:0.9),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:0.9),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 794.1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "01478",
|
"code": "01478",
|
||||||
@@ -52,7 +53,9 @@
|
|||||||
"rr_ratio": 1.12,
|
"rr_ratio": 1.12,
|
||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 7.59,
|
||||||
|
"change_pct": -3.68
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "600739",
|
"code": "600739",
|
||||||
@@ -79,7 +82,8 @@
|
|||||||
"rr_ratio": 1.35,
|
"rr_ratio": 1.35,
|
||||||
"action_note": "⚠️盈亏比偏低(1:1.3),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.3),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 10.48
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "601899",
|
"code": "601899",
|
||||||
@@ -106,7 +110,8 @@
|
|||||||
"rr_ratio": 0.74,
|
"rr_ratio": 0.74,
|
||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 27.5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "688639",
|
"code": "688639",
|
||||||
@@ -133,7 +138,8 @@
|
|||||||
"rr_ratio": 1.09,
|
"rr_ratio": 1.09,
|
||||||
"action_note": "⚠️盈亏比偏低(1:1.1),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.1),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 21.44
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "09988",
|
"code": "09988",
|
||||||
@@ -160,7 +166,9 @@
|
|||||||
"rr_ratio": 1.06,
|
"rr_ratio": 1.06,
|
||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 98.3,
|
||||||
|
"change_pct": -0.66
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "603259",
|
"code": "603259",
|
||||||
@@ -187,7 +195,8 @@
|
|||||||
"rr_ratio": 3.16,
|
"rr_ratio": 3.16,
|
||||||
"action_note": "",
|
"action_note": "",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 116.94
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "688981",
|
"code": "688981",
|
||||||
@@ -214,7 +223,8 @@
|
|||||||
"rr_ratio": 2.05,
|
"rr_ratio": 2.05,
|
||||||
"action_note": "",
|
"action_note": "",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 154.23
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "01888",
|
"code": "01888",
|
||||||
@@ -241,7 +251,9 @@
|
|||||||
"rr_ratio": 7.61,
|
"rr_ratio": 7.61,
|
||||||
"action_note": "短炒强趋势持",
|
"action_note": "短炒强趋势持",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 95.6,
|
||||||
|
"change_pct": 9.57
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "02202",
|
"code": "02202",
|
||||||
@@ -268,7 +280,9 @@
|
|||||||
"rr_ratio": 1.11,
|
"rr_ratio": 1.11,
|
||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 2.35,
|
||||||
|
"change_pct": -2.08
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "02388",
|
"code": "02388",
|
||||||
@@ -295,7 +309,9 @@
|
|||||||
"rr_ratio": 2.91,
|
"rr_ratio": 2.91,
|
||||||
"action_note": "",
|
"action_note": "",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 46.16,
|
||||||
|
"change_pct": -1.79
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "300750",
|
"code": "300750",
|
||||||
@@ -322,7 +338,8 @@
|
|||||||
"rr_ratio": 0.84,
|
"rr_ratio": 0.84,
|
||||||
"action_note": "⚠️盈亏比偏低(1:0.8),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:0.8),不建议加仓",
|
||||||
"timing_signal": "弱势持有"
|
"timing_signal": "弱势持有"
|
||||||
}
|
},
|
||||||
|
"price": 394.78
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "01211",
|
"code": "01211",
|
||||||
@@ -349,7 +366,9 @@
|
|||||||
"rr_ratio": 1.13,
|
"rr_ratio": 1.13,
|
||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 74.45,
|
||||||
|
"change_pct": -1.85
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "00700",
|
"code": "00700",
|
||||||
@@ -376,7 +395,9 @@
|
|||||||
"rr_ratio": 1.0,
|
"rr_ratio": 1.0,
|
||||||
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 417.4,
|
||||||
|
"change_pct": 0.63
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "00981",
|
"code": "00981",
|
||||||
@@ -403,7 +424,9 @@
|
|||||||
"rr_ratio": 3.0,
|
"rr_ratio": 3.0,
|
||||||
"action_note": "",
|
"action_note": "",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 84.15,
|
||||||
|
"change_pct": 8.09
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "09868",
|
"code": "09868",
|
||||||
@@ -430,7 +453,9 @@
|
|||||||
"rr_ratio": 22.09,
|
"rr_ratio": 22.09,
|
||||||
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 49.58,
|
||||||
|
"change_pct": 0.45
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "600036",
|
"code": "600036",
|
||||||
@@ -457,7 +482,8 @@
|
|||||||
"rr_ratio": 1.1,
|
"rr_ratio": 1.1,
|
||||||
"action_note": "⚠️盈亏比偏低(1:1.1),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.1),不建议加仓",
|
||||||
"timing_signal": "弱势持有"
|
"timing_signal": "弱势持有"
|
||||||
}
|
},
|
||||||
|
"price": 37.04
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "300548",
|
"code": "300548",
|
||||||
@@ -484,7 +510,8 @@
|
|||||||
"rr_ratio": 7.41,
|
"rr_ratio": 7.41,
|
||||||
"action_note": "短炒强趋势持",
|
"action_note": "短炒强趋势持",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 278.12
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "02318",
|
"code": "02318",
|
||||||
@@ -511,7 +538,9 @@
|
|||||||
"rr_ratio": 15.88,
|
"rr_ratio": 15.88,
|
||||||
"action_note": "⚠️盈亏比偏低(1:1.2),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.2),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 52.5,
|
||||||
|
"change_pct": -2.05
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "300035",
|
"code": "300035",
|
||||||
@@ -538,7 +567,8 @@
|
|||||||
"rr_ratio": 0.95,
|
"rr_ratio": 0.95,
|
||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 16.13
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "000700",
|
"code": "000700",
|
||||||
@@ -565,7 +595,8 @@
|
|||||||
"rr_ratio": 3.23,
|
"rr_ratio": 3.23,
|
||||||
"action_note": "",
|
"action_note": "",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 14.64
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "518880",
|
"code": "518880",
|
||||||
@@ -592,7 +623,8 @@
|
|||||||
"rr_ratio": 0.55,
|
"rr_ratio": 0.55,
|
||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 8.44
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "01088",
|
"code": "01088",
|
||||||
@@ -619,7 +651,9 @@
|
|||||||
"rr_ratio": 1.01,
|
"rr_ratio": 1.01,
|
||||||
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 41.76,
|
||||||
|
"change_pct": -0.71
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "600563",
|
"code": "600563",
|
||||||
@@ -646,7 +680,8 @@
|
|||||||
"rr_ratio": 3.54,
|
"rr_ratio": 3.54,
|
||||||
"action_note": "",
|
"action_note": "",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 178.76
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "300690",
|
"code": "300690",
|
||||||
@@ -673,7 +708,8 @@
|
|||||||
"rr_ratio": 1.25,
|
"rr_ratio": 1.25,
|
||||||
"action_note": "⚠️盈亏比偏低(1:1.2),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.2),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
}
|
},
|
||||||
|
"price": 22.87
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"cash": 20230.1,
|
"cash": 20230.1,
|
||||||
|
|||||||
@@ -394,6 +394,81 @@ def main():
|
|||||||
|
|
||||||
return theo_pct, pct_actual, details, lots, lot_cost_total
|
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}元)")
|
lines.append(f"【💡 操作建议】(当前{n}只自选可操作 | 总资产{total_assets:,.0f}元 现金{available_cash:,.0f}元)")
|
||||||
for s in actionable:
|
for s in actionable:
|
||||||
@@ -478,6 +553,15 @@ def main():
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
action_tag = "⚠️" if lots == 0 else "🛒"
|
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(
|
lines.append(
|
||||||
f" {action_tag} {name}({code}) {pfx}{price:.2f} 买区{buy_low}~{buy_high} | "
|
f" {action_tag} {name}({code}) {pfx}{price:.2f} 买区{buy_low}~{buy_high} | "
|
||||||
f"1手{lot:,.0f}元 RR={rr:.1f} 损{sl} 盈{tp}\n"
|
f"1手{lot:,.0f}元 RR={rr:.1f} 损{sl} 盈{tp}\n"
|
||||||
@@ -486,6 +570,9 @@ def main():
|
|||||||
f" 仓位:理论{theo_pct}%×总资产 | 建议{actual_pct}%({details})"
|
f" 仓位:理论{theo_pct}%×总资产 | 建议{actual_pct}%({details})"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if swap_text:
|
||||||
|
lines[-1] += f"\n {swap_text}"
|
||||||
|
|
||||||
# 分支描述
|
# 分支描述
|
||||||
branch_line = ""
|
branch_line = ""
|
||||||
if branch_action != "hold":
|
if branch_action != "hold":
|
||||||
|
|||||||
Reference in New Issue
Block a user