fix(gate-bot): allow profit-side stop loss on TP/SL entrust

Skip min planned RR when stop is on the winning side of entry; validate entrust against open price and fall back to plan take-profit when omitted.
This commit is contained in:
dekun
2026-06-04 16:28:47 +08:00
parent 1042f135ed
commit 88fc21e278
3 changed files with 102 additions and 19 deletions
+35 -13
View File
@@ -54,7 +54,9 @@ from form_submit_lib import check_duplicate_submit, submit_scope_add_key, submit
from order_monitor_display_lib import (
apply_order_price_display_fields,
enrich_order_display_fields,
stop_is_profit_protecting,
tpsl_slot_trigger_price,
tpsl_update_passes_rr_gate,
)
from journal_chart_lib import (
JOURNAL_CHART_DEFAULT_LIMIT,
@@ -3357,7 +3359,9 @@ def cancel_gate_tpsl_slot(exchange_symbol, slot):
exchange.cancel_order(str(oid), exchange_symbol, params)
def _resolve_tpsl_prices_for_manual(direction, live_price, sltp_mode, data):
def _resolve_tpsl_prices_for_manual(
direction, live_price, sltp_mode, data, *, fallback_sl=None, fallback_tp=None
):
sltp_mode = (sltp_mode or "price").strip().lower()
if sltp_mode == "pct":
sl_pct = float(data.get("sl_pct") or 0)
@@ -3376,8 +3380,14 @@ def _resolve_tpsl_prices_for_manual(direction, live_price, sltp_mode, data):
else:
stop_loss = float(data.get("sl") or data.get("stop_loss") or 0)
take_profit = float(data.get("tp") or data.get("take_profit") or data.get("tgt") or 0)
if stop_loss <= 0 or take_profit <= 0:
raise ValueError("止盈止损价格须大于 0")
if stop_loss <= 0 and fallback_sl is not None:
stop_loss = float(fallback_sl)
if take_profit <= 0 and fallback_tp is not None:
take_profit = float(fallback_tp)
if stop_loss <= 0:
raise ValueError("止损价格须大于 0")
if take_profit <= 0:
raise ValueError("请填写止盈价格,或保留原计划止盈")
return stop_loss, take_profit
@@ -6044,20 +6054,32 @@ def api_order_place_tpsl(order_id):
return jsonify({"ok": False, "msg": "获取交易所实时价格失败"}), 400
try:
sltp_mode = (data.get("sltp_mode") or "price").strip().lower()
stop_loss, take_profit = _resolve_tpsl_prices_for_manual(direction, live_price, sltp_mode, data)
stop_loss, take_profit = _resolve_tpsl_prices_for_manual(
direction,
live_price,
sltp_mode,
data,
fallback_sl=row["stop_loss"],
fallback_tp=row["take_profit"],
)
except Exception as e:
conn.close()
return jsonify({"ok": False, "msg": str(e)}), 400
planned_rr = calc_rr_ratio(direction, live_price, stop_loss, take_profit)
if planned_rr is None or planned_rr < MANUAL_MIN_PLANNED_RR:
entry_price = float(row["trigger_price"] or live_price or 0)
rr_ok, rr_err = tpsl_update_passes_rr_gate(
direction,
entry_price,
stop_loss,
take_profit,
MANUAL_MIN_PLANNED_RR,
calc_rr_ratio,
)
if not rr_ok:
conn.close()
rr_txt = f"{planned_rr:.4f}" if planned_rr is not None else "无法计算"
return jsonify(
{
"ok": False,
"msg": f"计划盈亏比 {rr_txt}:1 低于最低要求 {MANUAL_MIN_PLANNED_RR}:1",
}
), 400
return jsonify({"ok": False, "msg": rr_err}), 400
planned_rr = calc_rr_ratio(direction, entry_price, stop_loss, take_profit)
if stop_is_profit_protecting(direction, entry_price, stop_loss):
planned_rr = None
try:
replace_active_monitor_tpsl_on_exchange(row, stop_loss, take_profit)
except Exception as e: