feat: 止盈支持按盈亏比自动反算(根据止损点位差)

This commit is contained in:
2026-06-11 23:39:50 +00:00
parent 99e104c686
commit ccd0f4cfec

View File

@@ -69,6 +69,7 @@
transition:border-color .15s;
}
input[type=number]:focus{border-color:var(--border-hi)}
input[type=number]:disabled{opacity:.5;cursor:not-allowed}
input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.side-toggle{display:flex;gap:8px}
@@ -172,8 +173,15 @@
</div>
</div>
<div class="field">
<label>止盈价(选填,用于盈亏比)</label>
<input type="number" id="tp" step="any" placeholder="留空则不计算">
<label>止盈</label>
<div class="sl-mode" id="tpMode">
<button data-mode="rr" class="on">按盈亏比</button>
<button data-mode="price">按价格</button>
</div>
<div class="row2">
<input type="number" id="tpRR" value="2" step="any" placeholder="盈亏比 R如 2">
<input type="number" id="tp" step="any" placeholder="止盈价" disabled>
</div>
</div>
</div>
@@ -198,15 +206,25 @@
<div class="v" id="rr"></div>
</div>
</div>
<div class="pair" style="margin-bottom:0">
<div class="pair">
<div>
<div class="lbl">止损价</div>
<div class="v" id="slPriceShow"></div>
</div>
<div>
<div class="lbl">止盈价</div>
<div class="v" id="tpPriceShow"></div>
</div>
</div>
<div class="pair" style="margin-bottom:0">
<div>
<div class="lbl">若止盈 · 盈利</div>
<div class="v" id="tpProfit"></div>
</div>
<div>
<div class="lbl">止盈幅度</div>
<div class="v" id="tpPctShow"></div>
</div>
</div>
</div>
@@ -229,8 +247,9 @@
</div>
<div class="note">
<b>说明:</b>真正把亏损锁定在风险金额的是<b>止损单</b>,不是杠杆。表中「状态」校验爆仓价是否在止损之外——
<span class="safe">安全</span>表示会先触发止损、不会被强平;<span class="danger">危险</span>表示杠杆过高、爆仓在止损之前
<b>说明:</b>真正把亏损锁定在风险金额的是<b>止损单</b>,不是杠杆。止盈默认<b>按盈亏比自动反算</b>——
止盈点位差 = 止损点位差 × 盈亏比 R按方向算出止盈价也可切「按价格」手动输入
表中「状态」校验爆仓价是否在止损之外——<span class="safe">安全</span>表示会先触发止损、不会被强平;<span class="danger">危险</span>表示杠杆过高、爆仓在止损之前。
爆仓价为<b>逐仓简化估算</b>爆仓距离≈1/杠杆,忽略维持保证金与手续费),实盘以交易所显示为准。这只是数学计算,不构成投资建议。
</div>
</div>
@@ -239,7 +258,7 @@
(function(){
const $=id=>document.getElementById(id);
const levs=[3,5,10,20,25,50,75,100];
let side='long', slMode='pct';
let side='long', slMode='pct', tpMode='rr';
// side toggle
$('side').addEventListener('click',e=>{
@@ -258,7 +277,17 @@
calc();
});
['balance','riskPct','entry','slPct','slPrice','tp'].forEach(id=>$(id).addEventListener('input',calc));
// take-profit mode toggle
$('tpMode').addEventListener('click',e=>{
const b=e.target.closest('button'); if(!b)return;
tpMode=b.dataset.mode;
[...$('tpMode').children].forEach(x=>x.classList.toggle('on',x===b));
$('tpRR').disabled = tpMode!=='rr';
$('tp').disabled = tpMode!=='price';
calc();
});
['balance','riskPct','entry','slPct','slPrice','tp','tpRR'].forEach(id=>$(id).addEventListener('input',calc));
const fmtN=(n,d=2)=>isFinite(n)?n.toLocaleString('en-US',{minimumFractionDigits:d,maximumFractionDigits:d}):'—';
const fmtP=n=>isFinite(n)?n.toLocaleString('en-US',{minimumFractionDigits:5,maximumFractionDigits:5}):'—';
@@ -292,15 +321,32 @@
$('slPriceShow').textContent = fmtP(slPrice);
$('slShow').textContent = '止损 '+fmtN(slPctVal)+'%';
// take profit / RR
const tp=+$('tp').value;
if(tp>0){
const tpDist=Math.abs(tp-entry);
// take profit — 两种模式:按盈亏比反算 / 按价格
let tpPrice, rr;
if(tpMode==='rr'){
rr=+$('tpRR').value; // 目标盈亏比
if(rr>0){
// 止盈点位差 = 止损点位差 × 盈亏比,按方向反算止盈价
tpPrice = side==='long' ? entry + dist*rr : entry - dist*rr;
$('tp').value = (+tpPrice.toPrecision(8)); // 同步显示到价格框,方便核对/切换
}
}else{
tpPrice=+$('tp').value;
if(tpPrice>0) rr = Math.abs(tpPrice-entry)/dist;
}
if(tpPrice>0 && rr>0){
const tpDist=Math.abs(tpPrice-entry);
const profit=qty*tpDist;
const rr=tpDist/dist;
const tpPct=tpDist/entry*100;
$('rr').textContent = '1 : '+fmtN(rr);
$('tpPriceShow').textContent = fmtP(tpPrice);
$('tpProfit').innerHTML = '+'+fmtN(profit)+' <span class="u">U</span>';
}else{ $('rr').textContent='—'; $('tpProfit').textContent=''; }
$('tpPctShow').textContent = fmtN(tpPct)+'%';
}else{
$('rr').textContent='—'; $('tpPriceShow').textContent='—';
$('tpProfit').textContent='—'; $('tpPctShow').textContent='—';
}
// leverage ladder
const rows=levs.map(L=>{
@@ -321,7 +367,7 @@
}
function clearOut(){
['qty','notional','rr','slPriceShow','tpProfit'].forEach(id=>$(id).textContent='—');
['qty','notional','rr','slPriceShow','tpPriceShow','tpProfit','tpPctShow'].forEach(id=>$(id).textContent='—');
$('slShow').textContent=''; $('ladder').innerHTML='';
}