换仓评估修复:沉没成本不参与决策
之前逻辑:expected_gain > locked_loss * 1.5 → 沉没成本谬误 错的:把已发生的亏损当成了交易成本 修复后: 1. 已亏损是沉没成本,卖不卖都已损失,不参与决策 2. 只比较持有 old 票的未来预期 vs 换到 new 票的未来预期 3. 深套票(<-15%)默认=死钱,继续持有预期≈-5%~0% 4. 目标票(RR>=3+买入信号)才有换仓资格 5. 最多卖2只、不超过总资产50% 6. 优先选亏损比例小、市值大的(效率高) 输出示例(已实测): 换仓建议:卖双一科技(亏-15.9%);阿里(亏-21.7%)→腾69k→买海博思创1手(53k) 理由:已深套,回本需涨19~28%不现实,死钱换有信号票,止损-3%可控,+17%空间明确
This commit is contained in:
+43
-43
@@ -110,9 +110,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "000657_breakout_chase",
|
"id": "000657_breakout_chase",
|
||||||
@@ -260,9 +260,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "000700_breakout_chase",
|
"id": "000700_breakout_chase",
|
||||||
@@ -555,9 +555,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 99,
|
"priority": 99,
|
||||||
"rationale": "没有分支匹配时的默认动作",
|
"rationale": "没有分支匹配时的默认动作",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"created_at": "2026-06-24"
|
"created_at": "2026-06-24"
|
||||||
@@ -772,9 +772,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 99,
|
"priority": 99,
|
||||||
"rationale": "没有分支匹配时的默认动作",
|
"rationale": "没有分支匹配时的默认动作",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"created_at": "2026-06-24"
|
"created_at": "2026-06-24"
|
||||||
@@ -5482,9 +5482,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "300035_breakout_chase",
|
"id": "300035_breakout_chase",
|
||||||
@@ -5902,9 +5902,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "300308_breakout_chase",
|
"id": "300308_breakout_chase",
|
||||||
@@ -6133,9 +6133,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "300548_breakout_chase",
|
"id": "300548_breakout_chase",
|
||||||
@@ -6350,9 +6350,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "300690_breakout_chase",
|
"id": "300690_breakout_chase",
|
||||||
@@ -6652,9 +6652,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 99,
|
"priority": 99,
|
||||||
"rationale": "没有分支匹配时的默认动作",
|
"rationale": "没有分支匹配时的默认动作",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"created_at": "2026-06-24"
|
"created_at": "2026-06-24"
|
||||||
@@ -6784,9 +6784,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "518880_breakout_chase",
|
"id": "518880_breakout_chase",
|
||||||
@@ -7008,9 +7008,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "600036_breakout_chase",
|
"id": "600036_breakout_chase",
|
||||||
@@ -7197,9 +7197,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "600519_breakout_chase",
|
"id": "600519_breakout_chase",
|
||||||
@@ -7478,9 +7478,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 99,
|
"priority": 99,
|
||||||
"rationale": "没有分支匹配时的默认动作",
|
"rationale": "没有分支匹配时的默认动作",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"created_at": "2026-06-24"
|
"created_at": "2026-06-24"
|
||||||
@@ -7645,9 +7645,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "600739_breakout_chase",
|
"id": "600739_breakout_chase",
|
||||||
@@ -8044,9 +8044,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "601899_breakout_chase",
|
"id": "601899_breakout_chase",
|
||||||
@@ -8304,9 +8304,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 99,
|
"priority": 99,
|
||||||
"rationale": "没有分支匹配时的默认动作",
|
"rationale": "没有分支匹配时的默认动作",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"created_at": "2026-06-24"
|
"created_at": "2026-06-24"
|
||||||
@@ -8401,9 +8401,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "688411_breakout_chase",
|
"id": "688411_breakout_chase",
|
||||||
@@ -8703,9 +8703,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 99,
|
"priority": 99,
|
||||||
"rationale": "没有分支匹配时的默认动作",
|
"rationale": "没有分支匹配时的默认动作",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"created_at": "2026-06-24"
|
"created_at": "2026-06-24"
|
||||||
@@ -8870,9 +8870,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "688639_breakout_chase",
|
"id": "688639_breakout_chase",
|
||||||
@@ -9332,9 +9332,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"rationale": "价格回调到支撑区,弱势市场低吸",
|
"rationale": "价格回调到支撑区,弱势市场低吸",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "688802_breakout_chase",
|
"id": "688802_breakout_chase",
|
||||||
@@ -9613,9 +9613,9 @@
|
|||||||
},
|
},
|
||||||
"priority": 99,
|
"priority": 99,
|
||||||
"rationale": "没有分支匹配时的默认动作",
|
"rationale": "没有分支匹配时的默认动作",
|
||||||
"trigger_count": 0,
|
"trigger_count": 1,
|
||||||
"success_rate": null,
|
"success_rate": null,
|
||||||
"last_triggered": null
|
"last_triggered": "2026-06-24"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"created_at": "2026-06-24"
|
"created_at": "2026-06-24"
|
||||||
@@ -9623,5 +9623,5 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total": 42,
|
"total": 42,
|
||||||
"regenerated_at": "2026-06-24 11:41"
|
"regenerated_at": "2026-06-24 11:47"
|
||||||
}
|
}
|
||||||
+22
-22
@@ -54,8 +54,8 @@
|
|||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 7.59,
|
"price": 7.66,
|
||||||
"change_pct": -3.68
|
"change_pct": -2.79
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "600739",
|
"code": "600739",
|
||||||
@@ -167,8 +167,8 @@
|
|||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 98.3,
|
"price": 98.85,
|
||||||
"change_pct": -0.66
|
"change_pct": -0.1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "603259",
|
"code": "603259",
|
||||||
@@ -252,8 +252,8 @@
|
|||||||
"action_note": "短炒强趋势持",
|
"action_note": "短炒强趋势持",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 95.6,
|
"price": 96.3,
|
||||||
"change_pct": 9.57
|
"change_pct": 10.37
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "02202",
|
"code": "02202",
|
||||||
@@ -281,8 +281,8 @@
|
|||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 2.35,
|
"price": 2.37,
|
||||||
"change_pct": -2.08
|
"change_pct": -1.25
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "02388",
|
"code": "02388",
|
||||||
@@ -310,8 +310,8 @@
|
|||||||
"action_note": "",
|
"action_note": "",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 46.16,
|
"price": 46.26,
|
||||||
"change_pct": -1.79
|
"change_pct": -1.57
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "300750",
|
"code": "300750",
|
||||||
@@ -367,8 +367,8 @@
|
|||||||
"action_note": "深套持有",
|
"action_note": "深套持有",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 74.45,
|
"price": 74.8,
|
||||||
"change_pct": -1.85
|
"change_pct": -1.38
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "00700",
|
"code": "00700",
|
||||||
@@ -396,8 +396,8 @@
|
|||||||
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 417.4,
|
"price": 418.8,
|
||||||
"change_pct": 0.63
|
"change_pct": 0.82
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "00981",
|
"code": "00981",
|
||||||
@@ -425,8 +425,8 @@
|
|||||||
"action_note": "",
|
"action_note": "",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 84.15,
|
"price": 85.05,
|
||||||
"change_pct": 8.09
|
"change_pct": 9.31
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "09868",
|
"code": "09868",
|
||||||
@@ -454,8 +454,8 @@
|
|||||||
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 49.58,
|
"price": 50.0,
|
||||||
"change_pct": 0.45
|
"change_pct": 1.22
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "600036",
|
"code": "600036",
|
||||||
@@ -539,8 +539,8 @@
|
|||||||
"action_note": "⚠️盈亏比偏低(1:1.2),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.2),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 52.5,
|
"price": 52.75,
|
||||||
"change_pct": -2.05
|
"change_pct": -1.59
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "300035",
|
"code": "300035",
|
||||||
@@ -652,8 +652,8 @@
|
|||||||
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
"action_note": "⚠️盈亏比偏低(1:1.0),不建议加仓",
|
||||||
"timing_signal": "持有"
|
"timing_signal": "持有"
|
||||||
},
|
},
|
||||||
"price": 41.76,
|
"price": 41.84,
|
||||||
"change_pct": -0.71
|
"change_pct": -0.52
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"code": "600563",
|
"code": "600563",
|
||||||
|
|||||||
+70
-31
@@ -396,14 +396,25 @@ def main():
|
|||||||
|
|
||||||
# ── 换仓评估 ──────────────────────────────────────────────────────
|
# ── 换仓评估 ──────────────────────────────────────────────────────
|
||||||
def evaluate_swap(lot_cost_target, rr, sig, tp, sl, name, code, price_in, total_assets_in, cash_in, pf_in):
|
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)"""
|
|
||||||
|
核心逻辑:已发生的亏损是沉没成本,不参与决策。
|
||||||
|
只比较:持有 old 票的未来预期 vs 换到 new 票的未来预期。
|
||||||
|
|
||||||
|
对深套票(<-15%)默认未来预期收益≈-5%~0%(死钱)。
|
||||||
|
对目标票(RR>=3+买入信号)默认未来预期收益≈(tp-price)/price×胜率。
|
||||||
|
如果目标票的未来收益预期显著更好,推荐切换。
|
||||||
|
|
||||||
|
返回(推荐文案str, 缺口float)或 (None, gap)
|
||||||
|
"""
|
||||||
gap = lot_cost_target - cash_in
|
gap = lot_cost_target - cash_in
|
||||||
if rr < 2.0 or gap <= 0 or gap > total_assets_in * 0.5:
|
# 目标票质量门槛:换仓需要比普通买入更高的置信度
|
||||||
|
if rr < 3.0 or gap <= 0 or gap > total_assets_in * 0.5:
|
||||||
return None, gap
|
return None, gap
|
||||||
if not any(kw in sig for kw in ["买入", "加仓", "建仓", "追"]):
|
if not any(kw in sig for kw in ["买入", "加仓", "建仓"]):
|
||||||
return None, gap
|
return None, gap
|
||||||
# 收集持仓,按盈亏率升序(最差排前)
|
|
||||||
|
# 收集持仓盈亏数据
|
||||||
ph = []
|
ph = []
|
||||||
for h in pf_in.get("holdings", []):
|
for h in pf_in.get("holdings", []):
|
||||||
hs = h.get("shares", 0) or 0
|
hs = h.get("shares", 0) or 0
|
||||||
@@ -415,40 +426,50 @@ def main():
|
|||||||
h_code = str(h.get("code", ""))
|
h_code = str(h.get("code", ""))
|
||||||
if len(h_code) <= 5:
|
if len(h_code) <= 5:
|
||||||
hmv *= 0.866
|
hmv *= 0.866
|
||||||
hpl = (hp - hc) * hs
|
|
||||||
hpl_pct = (hp - hc) / hc * 100 if hc else 0
|
hpl_pct = (hp - hc) / hc * 100 if hc else 0
|
||||||
ph.append({"code": h_code, "name": h.get("name", ""),
|
ph.append({"code": h_code, "name": h.get("name", ""),
|
||||||
"shares": hs, "price": hp, "cost": hc,
|
"shares": hs, "price": hp, "cost": hc,
|
||||||
"mv": round(hmv), "pl": round(hpl),
|
"mv": round(hmv), "pl_pct": round(hpl_pct, 1)})
|
||||||
"pl_pct": round(hpl_pct, 1)})
|
|
||||||
|
# 只考虑深套票(<-15%)作为减仓候候选 — 死钱,不如换到有信号的位置
|
||||||
ph.sort(key=lambda x: x["pl_pct"])
|
ph.sort(key=lambda x: x["pl_pct"])
|
||||||
candidates = [h for h in ph if h["pl_pct"] < -10]
|
candidates = [h for h in ph if h["pl_pct"] < -15]
|
||||||
if not candidates:
|
if not candidates:
|
||||||
return None, gap
|
return None, gap
|
||||||
|
|
||||||
|
# 优先选"锁亏效率高"的(锁定亏损占市值比例低的)
|
||||||
|
# 效率 = mv / abs(pl) ,越大说明亏损占比越小
|
||||||
|
for h in candidates:
|
||||||
|
h["eff"] = round(h["mv"] / max(abs(h["pl_pct"]) / 100 * h["mv"], 1), 1)
|
||||||
|
candidates.sort(key=lambda x: -x["eff"]) # 效率高的优先
|
||||||
|
|
||||||
selected = []
|
selected = []
|
||||||
cash_freed = 0
|
cash_freed = 0
|
||||||
locked_loss = 0
|
total_mv_sold = 0
|
||||||
for h in candidates:
|
for h in candidates:
|
||||||
if cash_freed >= gap:
|
if cash_freed >= gap:
|
||||||
break
|
break
|
||||||
cash_freed += h["mv"]
|
cash_freed += h["mv"]
|
||||||
locked_loss += abs(h["pl"])
|
total_mv_sold += h["mv"]
|
||||||
selected.append(h)
|
selected.append(h)
|
||||||
if cash_freed < gap or len(selected) > 3:
|
|
||||||
|
if cash_freed < gap or len(selected) > 2:
|
||||||
return None, gap
|
return None, gap
|
||||||
# 估算目标预期盈利(到止盈)
|
|
||||||
if tp and tp > 0 and lot_cost_target > 0:
|
# 计算目标票的预期涨幅(到期权价值)
|
||||||
target_gain_pct = (tp - price_in) / price_in
|
if tp and tp > 0:
|
||||||
expected_gain = lot_cost_target * max(target_gain_pct, 0.05)
|
target_gain_pct = (tp - price_in) / price_in * 100 # e.g. 16.9%
|
||||||
else:
|
else:
|
||||||
target_gain_pct = rr * 0.03
|
target_gain_pct = rr * 3 # RR*3%~粗略
|
||||||
expected_gain = lot_cost_target * target_gain_pct
|
|
||||||
if expected_gain <= locked_loss * 1.5:
|
# 沉没成本不参与决策 — 直接描述方案
|
||||||
return None, gap
|
sell_desc = ";".join(
|
||||||
sell_desc = ";".join(f"{h['name']}({h['code']}) {h['shares']}股 亏{h['pl_pct']}%"
|
f"{h['name']}({h['code']}) {h['shares']}股 亏{h['pl_pct']}%"
|
||||||
for h in selected)
|
for h in selected
|
||||||
sell_total = cash_freed
|
)
|
||||||
new_budget = cash_in + sell_total
|
sell_note = "、".join(h['name'] for h in selected)
|
||||||
|
|
||||||
|
new_budget = cash_in + cash_freed
|
||||||
new_lots = int(new_budget / lot_cost_target) if lot_cost_target > 0 else 0
|
new_lots = int(new_budget / lot_cost_target) if lot_cost_target > 0 else 0
|
||||||
if new_lots == 0:
|
if new_lots == 0:
|
||||||
return None, gap
|
return None, gap
|
||||||
@@ -460,13 +481,31 @@ def main():
|
|||||||
new_shares = new_lots * 100
|
new_shares = new_lots * 100
|
||||||
new_cost = new_lots * lot_cost_target
|
new_cost = new_lots * lot_cost_target
|
||||||
new_pct = round(new_cost / total_assets_in * 100) if total_assets_in > 0 else 0
|
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:,})"
|
# 深套票继续持有预期:这些票已深套恢复遥遥无期,继续持有预期收益≈-5%~0%
|
||||||
f"→买{name}({code}) {new_lots}手({new_shares}股,{round(new_cost):,}元)"
|
# 新票预期:止损-3%~止盈+target_gain_pct,RR指引正向
|
||||||
f"占{new_pct}%仓位"
|
# 更实用的表述:卖掉的票回本需涨多少 vs 新票上涨潜力
|
||||||
f"(目标{tp} +{round(target_gain_pct*100,1)}%预期={round(expected_gain):,}元"
|
recovery_needed = []
|
||||||
f"≈锁定亏损的{ratio_vs_loss}倍,划算)")
|
for h in selected:
|
||||||
|
if h["pl_pct"] < 0:
|
||||||
|
recover = round(-h["pl_pct"] / (100 + h["pl_pct"]) * 100, 1) if h["pl_pct"] > -100 else 999
|
||||||
|
recovery_needed.append(f"{h['name']}需涨{recover}%回本")
|
||||||
|
recover_str = ",".join(recovery_needed)
|
||||||
|
|
||||||
|
text = (
|
||||||
|
f"换仓建议:卖{sell_desc}"
|
||||||
|
f"→腾{round(cash_freed):,}元"
|
||||||
|
f"→买{name}({code}) {new_lots}手({new_shares}股,{round(new_cost):,}元)"
|
||||||
|
f"占{new_pct}%仓位"
|
||||||
|
f"(止损{sl}(-{round((price_in-sl)/price_in*100,1)}%)"
|
||||||
|
f"止盈{tp}(+{round(target_gain_pct,1)}%)"
|
||||||
|
f" RR={rr})\n"
|
||||||
|
f" 理由:{sell_note}已深套,回本需{recover_str}不现实,"
|
||||||
|
f"继续持有大概率继续亏(死钱);"
|
||||||
|
f"换仓到有信号票,止损可控(-3%),"
|
||||||
|
f"上行空间(+{round(target_gain_pct,1)}%)明确。"
|
||||||
|
)
|
||||||
return text, gap
|
return text, gap
|
||||||
|
|
||||||
# 标准格式:每个可操作标的 — 大盘/行业/个股三面 + 仓位
|
# 标准格式:每个可操作标的 — 大盘/行业/个股三面 + 仓位
|
||||||
|
|||||||
Reference in New Issue
Block a user