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
+34 -6
View File
@@ -1623,6 +1623,22 @@ function rejectManualOrderRr(rr){
alert(`计划盈亏比 ${rr === null ? '无效' : rr.toFixed(2)}:1 低于最低要求 ${MANUAL_MIN_PLANNED_RR}:1,已阻止人工下单。`);
return true;
}
function stopIsProfitProtecting(direction, entry, sl){
const e = Number(entry), s = Number(sl);
if(!Number.isFinite(e) || !Number.isFinite(s)) return false;
return (direction || "long") === "short" ? s < e : s > e;
}
function entryPriceFromOrderCard(card){
if(!card) return null;
const raw = card.getAttribute("data-entry");
if(raw === null || raw === "") return null;
const e = Number(raw);
return Number.isFinite(e) ? e : null;
}
function tpslRrCheckPasses(direction, entry, sl, tp){
if(stopIsProfitProtecting(direction, entry, sl)) return true;
return !rejectManualOrderRr(calcClientRr(direction, entry, sl, tp));
}
let tpslEntrustMonitorId = null;
function formatExTpslLine(role, slot){
const label = role === 'sl' ? '止损' : '止盈';
@@ -1720,16 +1736,28 @@ function submitTpslEntrust(){
}).catch(()=>alert('委托请求失败'));
};
if(mode === 'pct'){ post(); return; }
const sl = Number(body.sl), tp = Number(body.tp);
let entry = sl;
let sl = Number(body.sl);
let tp = Number(body.tp);
const planTp = card && card.getAttribute('data-plan-tp');
if((!Number.isFinite(tp) || tp <= 0) && planTp){
const pt = Number(planTp);
if(Number.isFinite(pt) && pt > 0) tp = pt;
}
if(!Number.isFinite(sl) || sl <= 0){ alert('请填写止损价格'); return; }
if(!Number.isFinite(tp) || tp <= 0){ alert('请填写止盈价格,或保留原计划止盈'); return; }
let entry = entryPriceFromOrderCard(card);
const sym = (card && card.getAttribute('data-symbol')) || '';
if(!sym){ if(rejectManualOrderRr(calcClientRr(direction, entry, sl, tp))) return; post(); return; }
const finishRr = (entryPx)=>{
const e = entryPx != null ? entryPx : entry;
if(!tpslRrCheckPasses(direction, e, sl, tp)) return;
post();
};
if(entry != null){ finishRr(entry); return; }
if(!sym){ finishRr(sl); return; }
fetch(`/api/order_defaults?symbol=${encodeURIComponent(sym)}&direction=${encodeURIComponent(direction)}`)
.then(r=>r.json()).then(data=>{
const px = data.last_price || data.price;
if(px) entry = Number(px);
if(rejectManualOrderRr(calcClientRr(direction, entry, sl, tp))) return;
post();
finishRr(px ? Number(px) : null);
}).catch(()=>alert('无法校验盈亏比'));
}
function relinkOrphanPosition(symbol, direction){