feat: 止盈支持按盈亏比自动反算(根据止损点位差)
This commit is contained in:
74
index.html
74
index.html
@@ -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='';
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user