333 lines
12 KiB
HTML
333 lines
12 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>合约仓位计算器</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{
|
||
--bg:#0e1419;
|
||
--panel:#161d27;
|
||
--panel-2:#1b2430;
|
||
--border:#283340;
|
||
--border-hi:#36465a;
|
||
--text:#e6edf3;
|
||
--muted:#7d8896;
|
||
--faint:#566374;
|
||
--long:#16c784;
|
||
--short:#ea3943;
|
||
--gold:#f0b90b;
|
||
--mono:"JetBrains Mono",ui-monospace,"SF Mono","Menlo",monospace;
|
||
--sans:"Inter",system-ui,-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;
|
||
}
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
body{
|
||
background:var(--bg);
|
||
color:var(--text);
|
||
font-family:var(--sans);
|
||
line-height:1.5;
|
||
padding:24px 16px 64px;
|
||
-webkit-font-smoothing:antialiased;
|
||
text-rendering:optimizeLegibility;
|
||
}
|
||
.mono,input[type=number],.val,.v,.lev-table,.tag,.result-head .risk b,.big .lbl,.pair .lbl,.eyebrow,.card h2,.lev-table th{
|
||
font-variant-numeric:tabular-nums;
|
||
font-feature-settings:"tnum" 1,"cv01" 1,"ss01" 1;
|
||
}
|
||
.wrap{max-width:880px;margin:0 auto}
|
||
header{margin-bottom:24px}
|
||
.eyebrow{
|
||
font-family:var(--mono);font-size:11px;letter-spacing:.18em;
|
||
text-transform:uppercase;color:var(--gold);margin-bottom:6px;
|
||
}
|
||
h1{font-size:22px;font-weight:700;letter-spacing:-.02em}
|
||
h1 .sym{color:var(--muted);font-weight:400;font-family:var(--mono)}
|
||
.sub{color:var(--muted);font-size:13px;margin-top:4px}
|
||
|
||
.grid{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:20px}
|
||
@media(max-width:640px){.grid{grid-template-columns:1fr}}
|
||
|
||
.card{
|
||
background:var(--panel);border:1px solid var(--border);
|
||
border-radius:12px;padding:18px;
|
||
}
|
||
.card h2{
|
||
font-size:11px;letter-spacing:.14em;text-transform:uppercase;
|
||
color:var(--faint);font-weight:600;margin-bottom:14px;font-family:var(--mono);
|
||
}
|
||
|
||
.field{margin-bottom:14px}
|
||
.field:last-child{margin-bottom:0}
|
||
label{display:block;font-size:12px;color:var(--muted);margin-bottom:6px}
|
||
input[type=number]{
|
||
width:100%;background:var(--panel-2);border:1px solid var(--border);
|
||
color:var(--text);font-family:var(--mono);font-size:15px;
|
||
padding:9px 12px;border-radius:8px;outline:none;
|
||
transition:border-color .15s;
|
||
}
|
||
input[type=number]:focus{border-color:var(--border-hi)}
|
||
input::-webkit-outer-spin-button,input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
|
||
|
||
.side-toggle{display:flex;gap:8px}
|
||
.side-toggle button{
|
||
flex:1;background:var(--panel-2);border:1px solid var(--border);
|
||
color:var(--muted);padding:9px;border-radius:8px;cursor:pointer;
|
||
font-size:14px;font-weight:600;transition:all .15s;
|
||
}
|
||
.side-toggle button.on[data-side=long]{background:rgba(22,199,132,.14);border-color:var(--long);color:var(--long)}
|
||
.side-toggle button.on[data-side=short]{background:rgba(234,57,67,.14);border-color:var(--short);color:var(--short)}
|
||
|
||
.sl-mode{display:flex;gap:6px;margin-bottom:8px}
|
||
.sl-mode button{
|
||
background:transparent;border:1px solid var(--border);color:var(--faint);
|
||
padding:4px 10px;border-radius:6px;cursor:pointer;font-size:11px;
|
||
font-family:var(--mono);transition:all .15s;
|
||
}
|
||
.sl-mode button.on{border-color:var(--gold);color:var(--gold)}
|
||
|
||
.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
||
|
||
/* results */
|
||
.result-head{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:16px}
|
||
.result-head .risk{font-size:12px;color:var(--muted)}
|
||
.result-head .risk b{color:var(--short);font-family:var(--mono);font-weight:600}
|
||
|
||
.big{margin-bottom:16px}
|
||
.big .lbl{font-size:11px;color:var(--faint);text-transform:uppercase;letter-spacing:.1em;font-family:var(--mono);margin-bottom:3px}
|
||
.big .val{font-family:var(--mono);font-size:27px;font-weight:700;letter-spacing:-.02em}
|
||
.big .val .u{font-size:13px;color:var(--muted);font-weight:400;margin-left:4px}
|
||
|
||
.pair{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px}
|
||
.pair .lbl{font-size:11px;color:var(--faint);text-transform:uppercase;letter-spacing:.08em;font-family:var(--mono);margin-bottom:2px}
|
||
.pair .v{font-family:var(--mono);font-size:16px;font-weight:600}
|
||
.pair .v .u{font-size:11px;color:var(--muted);font-weight:400}
|
||
|
||
.lev-table{width:100%;border-collapse:collapse;font-family:var(--mono);font-size:13px}
|
||
.lev-table th{
|
||
text-align:right;font-weight:500;color:var(--faint);font-size:10px;
|
||
text-transform:uppercase;letter-spacing:.08em;padding:0 0 8px;
|
||
}
|
||
.lev-table th:first-child{text-align:left}
|
||
.lev-table td{padding:7px 0;border-top:1px solid var(--border);text-align:right}
|
||
.lev-table td:first-child{text-align:left;color:var(--gold);font-weight:600}
|
||
.lev-table .liq{color:var(--muted)}
|
||
.safe{color:var(--long)}
|
||
.danger{color:var(--short)}
|
||
.tag{font-size:10px;padding:1px 6px;border-radius:4px;font-family:var(--mono)}
|
||
.tag.ok{background:rgba(22,199,132,.12);color:var(--long)}
|
||
.tag.no{background:rgba(234,57,67,.12);color:var(--short)}
|
||
|
||
.note{
|
||
margin-top:18px;padding:12px 14px;background:var(--panel);
|
||
border:1px solid var(--border);border-radius:10px;
|
||
font-size:12px;color:var(--muted);line-height:1.6;
|
||
}
|
||
.note b{color:var(--text);font-weight:600}
|
||
.full{grid-column:1/-1}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
<header>
|
||
<div class="eyebrow">Position Sizer · 逐仓</div>
|
||
<h1>合约仓位计算器 <span class="sym">/ DOGE-USDT</span></h1>
|
||
<div class="sub">先定亏损上限,再反推仓位。杠杆只决定爆仓距离,不决定你亏多少。</div>
|
||
</header>
|
||
|
||
<div class="grid">
|
||
<!-- INPUTS -->
|
||
<div class="card">
|
||
<h2>输入</h2>
|
||
<div class="field">
|
||
<label>方向</label>
|
||
<div class="side-toggle" id="side">
|
||
<button data-side="long" class="on">做多 Long</button>
|
||
<button data-side="short">做空 Short</button>
|
||
</div>
|
||
</div>
|
||
<div class="field">
|
||
<label>账户余额 (USDT)</label>
|
||
<input type="number" id="balance" value="200" step="any">
|
||
</div>
|
||
<div class="field">
|
||
<label>单笔风险 (% of 余额)</label>
|
||
<input type="number" id="riskPct" value="5" step="any">
|
||
</div>
|
||
<div class="field">
|
||
<label>入场价</label>
|
||
<input type="number" id="entry" value="0.08505" step="any">
|
||
</div>
|
||
<div class="field">
|
||
<label>止损</label>
|
||
<div class="sl-mode" id="slMode">
|
||
<button data-mode="pct" class="on">按百分比</button>
|
||
<button data-mode="price">按价格</button>
|
||
</div>
|
||
<div class="row2">
|
||
<input type="number" id="slPct" value="2" step="any" placeholder="止损 %">
|
||
<input type="number" id="slPrice" step="any" placeholder="止损价" disabled>
|
||
</div>
|
||
</div>
|
||
<div class="field">
|
||
<label>止盈价(选填,用于盈亏比)</label>
|
||
<input type="number" id="tp" step="any" placeholder="留空则不计算">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- RESULTS -->
|
||
<div class="card">
|
||
<h2>仓位</h2>
|
||
<div class="result-head">
|
||
<span class="risk">最大亏损 <b id="riskAmt">10.00 U</b></span>
|
||
<span class="risk" id="slShow"></span>
|
||
</div>
|
||
<div class="big">
|
||
<div class="lbl">建议仓位 · DOGE 数量</div>
|
||
<div class="val" id="qty">—</div>
|
||
</div>
|
||
<div class="pair">
|
||
<div>
|
||
<div class="lbl">名义价值</div>
|
||
<div class="v" id="notional">—</div>
|
||
</div>
|
||
<div>
|
||
<div class="lbl">盈亏比 R:R</div>
|
||
<div class="v" id="rr">—</div>
|
||
</div>
|
||
</div>
|
||
<div class="pair" style="margin-bottom:0">
|
||
<div>
|
||
<div class="lbl">止损价</div>
|
||
<div class="v" id="slPriceShow">—</div>
|
||
</div>
|
||
<div>
|
||
<div class="lbl">若止盈 · 盈利</div>
|
||
<div class="v" id="tpProfit">—</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- LEVERAGE LADDER -->
|
||
<div class="card full">
|
||
<h2>杠杆阶梯 · 保证金 & 爆仓校验</h2>
|
||
<table class="lev-table">
|
||
<thead>
|
||
<tr>
|
||
<th>杠杆</th>
|
||
<th>占用保证金</th>
|
||
<th>爆仓价</th>
|
||
<th>爆仓距离</th>
|
||
<th>状态</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="ladder"></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="note">
|
||
<b>说明:</b>真正把亏损锁定在风险金额的是<b>止损单</b>,不是杠杆。表中「状态」校验爆仓价是否在止损之外——
|
||
<span class="safe">安全</span>表示会先触发止损、不会被强平;<span class="danger">危险</span>表示杠杆过高、爆仓在止损之前。
|
||
爆仓价为<b>逐仓简化估算</b>(爆仓距离≈1/杠杆,忽略维持保证金与手续费),实盘以交易所显示为准。这只是数学计算,不构成投资建议。
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function(){
|
||
const $=id=>document.getElementById(id);
|
||
const levs=[3,5,10,20,25,50,75,100];
|
||
let side='long', slMode='pct';
|
||
|
||
// side toggle
|
||
$('side').addEventListener('click',e=>{
|
||
const b=e.target.closest('button'); if(!b)return;
|
||
side=b.dataset.side;
|
||
[...$('side').children].forEach(x=>x.classList.toggle('on',x===b));
|
||
calc();
|
||
});
|
||
// sl mode toggle
|
||
$('slMode').addEventListener('click',e=>{
|
||
const b=e.target.closest('button'); if(!b)return;
|
||
slMode=b.dataset.mode;
|
||
[...$('slMode').children].forEach(x=>x.classList.toggle('on',x===b));
|
||
$('slPct').disabled = slMode!=='pct';
|
||
$('slPrice').disabled = slMode!=='price';
|
||
calc();
|
||
});
|
||
|
||
['balance','riskPct','entry','slPct','slPrice','tp'].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}):'—';
|
||
|
||
function calc(){
|
||
const bal=+$('balance').value, riskPct=+$('riskPct').value, entry=+$('entry').value;
|
||
const risk = bal*riskPct/100;
|
||
$('riskAmt').textContent = isFinite(risk)? fmtN(risk)+' U' : '—';
|
||
|
||
if(!(entry>0)||!(risk>0)){ clearOut(); return; }
|
||
|
||
// resolve stop-loss price + distance
|
||
let slPrice, slPctVal;
|
||
if(slMode==='pct'){
|
||
slPctVal=+$('slPct').value;
|
||
if(!(slPctVal>0)){ clearOut(); return; }
|
||
slPrice = side==='long' ? entry*(1-slPctVal/100) : entry*(1+slPctVal/100);
|
||
}else{
|
||
slPrice=+$('slPrice').value;
|
||
if(!(slPrice>0)){ clearOut(); return; }
|
||
slPctVal = Math.abs(entry-slPrice)/entry*100;
|
||
}
|
||
const dist=Math.abs(entry-slPrice);
|
||
if(!(dist>0)){ clearOut(); return; }
|
||
|
||
const qty = risk/dist; // DOGE 数量
|
||
const notional = qty*entry; // 名义价值 U
|
||
|
||
$('qty').innerHTML = fmtN(qty,0)+' <span class="u">DOGE</span>';
|
||
$('notional').innerHTML = fmtN(notional)+' <span class="u">U</span>';
|
||
$('slPriceShow').textContent = fmtP(slPrice);
|
||
$('slShow').textContent = '止损 '+fmtN(slPctVal)+'%';
|
||
|
||
// take profit / RR
|
||
const tp=+$('tp').value;
|
||
if(tp>0){
|
||
const tpDist=Math.abs(tp-entry);
|
||
const profit=qty*tpDist;
|
||
const rr=tpDist/dist;
|
||
$('rr').textContent = '1 : '+fmtN(rr);
|
||
$('tpProfit').innerHTML = '+'+fmtN(profit)+' <span class="u">U</span>';
|
||
}else{ $('rr').textContent='—'; $('tpProfit').textContent='—'; }
|
||
|
||
// leverage ladder
|
||
const rows=levs.map(L=>{
|
||
const margin=notional/L;
|
||
const liq = side==='long' ? entry*(1-1/L) : entry*(1+1/L);
|
||
const liqDistPct = 100/L;
|
||
// safe if liquidation is farther than stop-loss
|
||
const safe = liqDistPct > slPctVal;
|
||
return `<tr>
|
||
<td>${L}x</td>
|
||
<td>${fmtN(margin)} U</td>
|
||
<td class="liq">${fmtP(liq)}</td>
|
||
<td>${fmtN(liqDistPct)}%</td>
|
||
<td><span class="tag ${safe?'ok':'no'}">${safe?'安全':'危险'}</span></td>
|
||
</tr>`;
|
||
}).join('');
|
||
$('ladder').innerHTML=rows;
|
||
}
|
||
|
||
function clearOut(){
|
||
['qty','notional','rr','slPriceShow','tpProfit'].forEach(id=>$(id).textContent='—');
|
||
$('slShow').textContent=''; $('ladder').innerHTML='';
|
||
}
|
||
|
||
calc();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|