536 lines
32 KiB
HTML
536 lines
32 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid px-2 py-2" style="background-color: #0b0e11; min-height: 90vh;">
|
|
<div class="row g-2">
|
|
<!-- Left: Coin List Sidebar -->
|
|
<div class="col-lg-2 d-none d-lg-block">
|
|
<div class="glass-card h-100 p-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
|
<div class="p-2 mb-2">
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text bg-dark border-secondary"><i class="bi bi-search text-secondary"></i></span>
|
|
<input type="text" id="coin-search" class="form-control bg-dark text-white border-secondary" placeholder="搜索币种" onkeyup="filterCoins()">
|
|
</div>
|
|
</div>
|
|
<div class="coin-list-container" style="max-height: calc(100vh - 160px); overflow-y: auto;">
|
|
<table class="table table-dark table-hover table-sm mb-0 align-middle" style="font-size: 12px;">
|
|
<thead>
|
|
<tr class="text-secondary">
|
|
<th class="border-0">币种</th>
|
|
<th class="border-0 text-end">价格</th>
|
|
<th class="border-0 text-end">24h</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="left-coin-list">
|
|
<!-- Populated by JS -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Middle: Main Trading Area -->
|
|
<div class="col-lg-7">
|
|
<!-- Market Info Bar -->
|
|
<div class="glass-card mb-2 p-2 d-flex align-items-center justify-content-between" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
|
<div class="d-flex align-items-center">
|
|
<div class="dropdown me-4">
|
|
<button class="btn btn-transparent text-warning fw-bold fs-5 p-0" type="button" data-bs-toggle="dropdown">
|
|
<span id="current-symbol-display">{{ symbol }}</span> <i class="bi bi-caret-down-fill small"></i>
|
|
</button>
|
|
<ul class="dropdown-menu dropdown-menu-dark shadow-lg" id="mobile-coin-list" style="max-height: 400px; overflow-y: auto; width: 250px;">
|
|
<!-- Populated by JS -->
|
|
</ul>
|
|
</div>
|
|
<div class="me-4">
|
|
<div class="fw-bold fs-5 text-success" id="header-price">--</div>
|
|
<div class="text-secondary x-small">≈ $<span id="header-price-usd">--</span></div>
|
|
</div>
|
|
<div class="me-4 text-center">
|
|
<div class="text-secondary x-small">24h 涨跌</div>
|
|
<div class="fw-bold" id="header-change">--</div>
|
|
</div>
|
|
<div class="text-center d-none d-md-block">
|
|
<div class="text-secondary x-small">24h 成交量(USDT)</div>
|
|
<div class="fw-bold small" id="header-volume">--</div>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex gap-1 bg-dark p-1" style="border-radius: 4px;">
|
|
<a href="{% url 'core:trade' 'spot' %}?symbol={{ symbol }}" class="btn btn-sm {% if trade_type == 'SPOT' %}btn-warning text-dark{% else %}btn-transparent text-secondary{% endif %} fw-bold px-3">现货</a>
|
|
<a href="{% url 'core:trade' 'contract' %}?symbol={{ symbol }}" class="btn btn-sm {% if trade_type == 'CONTRACT' %}btn-warning text-dark{% else %}btn-transparent text-secondary{% endif %} fw-bold px-3">合约</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- TradingView Chart -->
|
|
<div class="glass-card mb-2" style="height: 450px; border-radius: 4px; border: 1px solid #2b3139;">
|
|
<div id="tradingview_widget" style="height: 100%;"></div>
|
|
</div>
|
|
|
|
<!-- Trading Forms -->
|
|
<div class="glass-card p-3 mb-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
|
{% if trade_type == 'SPOT' %}
|
|
<div class="row g-4">
|
|
<!-- Buy -->
|
|
<div class="col-md-6 border-end border-secondary">
|
|
<div class="btn-group w-100 mb-3" role="group">
|
|
<input type="radio" class="btn-check" name="buy-mode" id="buy-limit" checked onchange="toggleMode('buy', 'limit')">
|
|
<label class="btn btn-outline-secondary btn-sm" for="buy-limit">限价委托</label>
|
|
<input type="radio" class="btn-check" name="buy-mode" id="buy-market" onchange="toggleMode('buy', 'market')">
|
|
<label class="btn btn-outline-secondary btn-sm" for="buy-market">市价委托</label>
|
|
</div>
|
|
|
|
<div class="input-group input-group-sm mb-3">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
|
|
<input type="text" id="buy-price" class="form-control bg-transparent text-white border-secondary" placeholder="0.00">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
|
|
</div>
|
|
|
|
<div class="input-group input-group-sm mb-3">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;" id="buy-label">数量</span>
|
|
<input type="number" id="buy-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0.00" oninput="updateSlider('buy')">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary" id="buy-unit">{{ base_symbol }}</span>
|
|
</div>
|
|
|
|
<div class="mb-4 px-1">
|
|
<input type="range" class="form-range custom-range" id="buy-slider" min="0" max="100" step="1" value="0" oninput="applySlider('buy')">
|
|
<div class="d-flex justify-content-between x-small text-secondary mt-1">
|
|
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between x-small mb-3">
|
|
<span class="text-secondary">可用余额</span>
|
|
<span class="text-white">{{ account.balance|default:"0.00" }} USDT</span>
|
|
</div>
|
|
<button class="btn btn-success w-100 fw-bold py-2 shadow" onclick="submitOrder('BUY')">买入 <span class="base-asset">{{ base_symbol }}</span></button>
|
|
</div>
|
|
|
|
<!-- Sell -->
|
|
<div class="col-md-6">
|
|
<div class="btn-group w-100 mb-3" role="group">
|
|
<input type="radio" class="btn-check" name="sell-mode" id="sell-limit" checked onchange="toggleMode('sell', 'limit')">
|
|
<label class="btn btn-outline-secondary btn-sm" for="sell-limit">限价委托</label>
|
|
<input type="radio" class="btn-check" name="sell-mode" id="sell-market" onchange="toggleMode('sell', 'market')">
|
|
<label class="btn btn-outline-secondary btn-sm" for="sell-market">市价委托</label>
|
|
</div>
|
|
|
|
<div class="input-group input-group-sm mb-3">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
|
|
<input type="text" id="sell-price" class="form-control bg-transparent text-white border-secondary" placeholder="0.00">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
|
|
</div>
|
|
|
|
<div class="input-group input-group-sm mb-3">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">数量</span>
|
|
<input type="number" id="sell-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0.00" oninput="updateSlider('sell')">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary">{{ base_symbol }}</span>
|
|
</div>
|
|
|
|
<div class="mb-4 px-1">
|
|
<input type="range" class="form-range custom-range-danger" id="sell-slider" min="0" max="100" step="1" value="0" oninput="applySlider('sell')">
|
|
<div class="d-flex justify-content-between x-small text-secondary mt-1">
|
|
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex justify-content-between x-small mb-3">
|
|
<span class="text-secondary">可用 <span class="base-asset">{{ base_symbol }}</span></span>
|
|
<span class="text-white" id="sell-available-display">{{ base_asset_balance|default:"0.00" }} {{ base_symbol }}</span>
|
|
</div>
|
|
<button class="btn btn-danger w-100 fw-bold py-2 shadow" onclick="submitOrder('SELL')">卖出 <span class="base-asset">{{ base_symbol }}</span></button>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<!-- Contract Form -->
|
|
<div class="row g-4">
|
|
<div class="col-12 mb-2 d-flex gap-3 align-items-center">
|
|
<div class="btn-group" role="group">
|
|
<input type="radio" class="btn-check" name="c-mode" id="c-limit" checked onchange="toggleMode('contract', 'limit')">
|
|
<label class="btn btn-outline-warning btn-sm px-4" for="c-limit">限价委托</label>
|
|
<input type="radio" class="btn-check" name="c-mode" id="c-market" onchange="toggleMode('contract', 'market')">
|
|
<label class="btn btn-outline-warning btn-sm px-4" for="c-market">市价委托</label>
|
|
</div>
|
|
<div class="dropdown">
|
|
<button class="btn btn-dark btn-sm border-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" id="lev-btn">杠杆: 20x</button>
|
|
<ul class="dropdown-menu dropdown-menu-dark">
|
|
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(10)">10x</a></li>
|
|
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(20)">20x</a></li>
|
|
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(50)">50x</a></li>
|
|
<li><a class="dropdown-item" href="javascript:void(0)" onclick="setLev(100)">100x</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 border-end border-secondary">
|
|
<div class="input-group input-group-sm mb-3">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">价格</span>
|
|
<input type="text" id="c-price" class="form-control bg-transparent text-white border-secondary" placeholder="0.00">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary">USDT</span>
|
|
</div>
|
|
<div class="input-group input-group-sm mb-3">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary" style="width: 70px;">手数</span>
|
|
<input type="number" id="c-amount" class="form-control bg-transparent text-white border-secondary" placeholder="0" oninput="updateSlider('contract')">
|
|
<span class="input-group-text bg-dark text-secondary border-secondary">张</span>
|
|
</div>
|
|
<div class="mb-4 px-1">
|
|
<input type="range" class="form-range custom-range" id="c-slider" min="0" max="100" step="1" value="0" oninput="applySlider('contract')">
|
|
<div class="d-flex justify-content-between x-small text-secondary mt-1">
|
|
<span>0%</span><span>25%</span><span>50%</span><span>75%</span><span>100%</span>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex justify-content-between x-small mb-3 text-secondary">
|
|
<span>所需保证金: <span class="text-white" id="c-margin-display">0.00</span> USDT</span>
|
|
<span>可用: <span class="text-white">{{ account.balance|default:"0.00" }} USDT</span></span>
|
|
</div>
|
|
<div class="row g-2">
|
|
<div class="col-6"><button class="btn btn-success w-100 fw-bold shadow" onclick="submitOrder('BUY')">买入 (做多)</button></div>
|
|
<div class="col-6"><button class="btn btn-danger w-100 fw-bold shadow" onclick="submitOrder('SELL')">卖出 (做空)</button></div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="p-3 bg-dark rounded border border-secondary" style="font-size: 12px;">
|
|
<div class="d-flex justify-content-between mb-2"><span>合约面值</span><span class="text-white">100 USDT / 张</span></div>
|
|
<div class="d-flex justify-content-between mb-2"><span>当前杠杆</span><span class="text-warning" id="lev-val">20x</span></div>
|
|
<div class="d-flex justify-content-between mb-2"><span>预计手续费</span><span class="text-white">0.05%</span></div>
|
|
<div class="d-flex justify-content-between"><span>维持保证金率</span><span class="text-white">0.4%</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Positions / Orders Tabs -->
|
|
<div class="glass-card p-3" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
|
<ul class="nav nav-tabs border-0 mb-3" id="tradeTabs" role="tablist">
|
|
<li class="nav-item">
|
|
<button class="nav-link active bg-transparent text-white border-0 fw-bold border-bottom border-warning border-3" id="positions-tab" data-bs-toggle="tab" data-bs-target="#positions" type="button">当前持仓/订单</button>
|
|
</li>
|
|
</ul>
|
|
<div class="tab-content">
|
|
<div class="tab-pane fade show active" id="positions">
|
|
<div class="table-responsive">
|
|
<table class="table table-dark table-sm align-middle mb-0" style="font-size: 12px;">
|
|
<thead>
|
|
<tr class="text-secondary">
|
|
<th>合约/现货</th>
|
|
<th>方向</th>
|
|
<th>数量/手数</th>
|
|
<th>开仓/委托价</th>
|
|
<th>当前价</th>
|
|
<th>保证金</th>
|
|
<th>盈亏</th>
|
|
<th class="text-end">操作</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% if trade_type == 'CONTRACT' %}
|
|
{% for p in account.positions.all %}
|
|
{% if p.is_active %}
|
|
<tr>
|
|
<td class="fw-bold">{{ p.symbol }} {{ p.leverage }}x</td>
|
|
<td class="{% if p.side == 'LONG' %}text-success{% else %}text-danger{% endif %}">{{ p.get_side_display }}</td>
|
|
<td>{{ p.lots }}</td>
|
|
<td>{{ p.entry_price }}</td>
|
|
<td class="current-p-val" data-symbol="{{ p.symbol }}">--</td>
|
|
<td>{{ p.margin|floatformat:2 }}</td>
|
|
<td class="upl-val" data-entry="{{ p.entry_price }}" data-side="{{ p.side }}" data-lots="{{ p.lots }}">--</td>
|
|
<td class="text-end"><button class="btn btn-outline-danger btn-sm py-0" onclick="closePos({{ p.id }})">平仓</button></td>
|
|
</tr>
|
|
{% endif %}
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% for o in account.orders.all %}
|
|
{% if o.status == 'PENDING' %}
|
|
<tr>
|
|
<td>{{ o.symbol }} ({{ o.get_trade_type_display }})</td>
|
|
<td class="{% if o.side == 'BUY' %}text-success{% else %}text-danger{% endif %}">{{ o.get_side_display }}</td>
|
|
<td>{{ o.amount }}</td>
|
|
<td>{{ o.price|default:"市价" }}</td>
|
|
<td class="current-p-val" data-symbol="{{ o.symbol }}">--</td>
|
|
<td>--</td>
|
|
<td>等待成交</td>
|
|
<td class="text-end"><button class="btn btn-outline-secondary btn-sm py-0">撤单</button></td>
|
|
</tr>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right: Order Book -->
|
|
<div class="col-lg-3">
|
|
<div class="glass-card h-100 p-2" style="border-radius: 4px; border: 1px solid #2b3139; background: #161a1e;">
|
|
<div class="d-flex justify-content-between text-secondary x-small mb-2 px-2">
|
|
<span>价格(USDT)</span>
|
|
<span>数量({{ base_symbol }})</span>
|
|
</div>
|
|
<div id="asks" class="mb-2"></div>
|
|
<div class="py-2 text-center border-top border-bottom border-secondary my-2">
|
|
<span id="current-price-book" class="fs-5 fw-bold text-success">--</span>
|
|
</div>
|
|
<div id="bids"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.x-small { font-size: 11px; }
|
|
.coin-icon-small { width: 18px; height: 18px; margin-right: 8px; vertical-align: middle; border-radius: 50%; }
|
|
.order-book-row { display: flex; justify-content: space-between; font-size: 11px; padding: 2px 4px; position: relative; }
|
|
.order-book-row span { position: relative; z-index: 1; }
|
|
.ask-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(246, 70, 93, 0.15); transition: width 0.3s; }
|
|
.bid-bg { position: absolute; right: 0; top: 0; bottom: 0; background: rgba(14, 203, 129, 0.15); transition: width 0.3s; }
|
|
.custom-range::-webkit-slider-thumb { background: #f0b90b; cursor: pointer; }
|
|
.custom-range-danger::-webkit-slider-thumb { background: #f6465d; cursor: pointer; }
|
|
.coin-list-container::-webkit-scrollbar { width: 4px; }
|
|
.coin-list-container::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 2px; }
|
|
.nav-tabs .nav-link:hover { color: #fcd535 !important; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
|
<script>
|
|
const symbol = '{{ symbol }}';
|
|
const tradeType = '{{ trade_type }}';
|
|
const balance = parseFloat("{{ account.balance|default:0 }}");
|
|
const baseAsset = '{{ base_symbol }}';
|
|
const assetBalance = parseFloat("{{ base_asset_balance|default:0 }}");
|
|
|
|
let currentPrice = 0;
|
|
let leverage = 20;
|
|
let allCoins = [];
|
|
|
|
function initTV() {
|
|
new TradingView.widget({
|
|
"width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol,
|
|
"interval": "15", "theme": "dark", "style": "1", "locale": "zh_CN",
|
|
"container_id": "tradingview_widget", "hide_side_toolbar": false
|
|
});
|
|
}
|
|
|
|
async function getMarkets() {
|
|
try {
|
|
const r = await fetch('https://api.binance.com/api/v3/ticker/24hr');
|
|
const data = await r.json();
|
|
allCoins = data.filter(c => c.symbol.endsWith('USDT')).slice(0, 100);
|
|
renderCoins();
|
|
} catch(e) {}
|
|
}
|
|
|
|
function renderCoins(filter = '') {
|
|
const list = document.getElementById('left-coin-list');
|
|
const mobileList = document.getElementById('mobile-coin-list');
|
|
list.innerHTML = ''; mobileList.innerHTML = '';
|
|
|
|
allCoins.forEach(c => {
|
|
if (filter && !c.symbol.toLowerCase().includes(filter.toLowerCase())) return;
|
|
const base = c.symbol.replace('USDT', '');
|
|
const chg = parseFloat(c.priceChangePercent);
|
|
const iconUrl = `https://static.okx.com/cdn/oksupport/asset/currency/icon/${base.toLowerCase()}.png`;
|
|
const row = `
|
|
<tr style="cursor: pointer;" onclick="location.href='?symbol=${c.symbol}'">
|
|
<td><img src="${iconUrl}" class="coin-icon-small" onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">${base}</td>
|
|
<td class="text-end fw-bold">${parseFloat(c.lastPrice).toLocaleString()}</td>
|
|
<td class="text-end ${chg>=0?'text-success':'text-danger'}">${chg>=0?'+':''}${chg.toFixed(2)}%</td>
|
|
</tr>`;
|
|
list.innerHTML += row;
|
|
mobileList.innerHTML += `<li><a class="dropdown-item d-flex justify-content-between" href="?symbol=${c.symbol}"><span><img src="${iconUrl}" class="coin-icon-small" onerror="this.src='https://static.okx.com/cdn/oksupport/asset/currency/icon/generic.png'">${base}</span><span class="${chg>=0?'text-success':'text-danger'}">${parseFloat(c.lastPrice).toLocaleString()}</span></a></li>`;
|
|
});
|
|
}
|
|
|
|
function filterCoins() {
|
|
renderCoins(document.getElementById('coin-search').value);
|
|
}
|
|
|
|
function toggleMode(side, mode) {
|
|
if (side === 'buy') {
|
|
const pInput = document.getElementById('buy-price');
|
|
const label = document.getElementById('buy-label');
|
|
const unit = document.getElementById('buy-unit');
|
|
if (mode === 'market') {
|
|
pInput.value = '市价价格';
|
|
pInput.disabled = true;
|
|
pInput.style.color = '#f0b90b';
|
|
label.textContent = '成交额';
|
|
unit.textContent = 'USDT';
|
|
} else {
|
|
pInput.value = currentPrice || '';
|
|
pInput.disabled = false;
|
|
pInput.style.color = 'white';
|
|
label.textContent = '数量';
|
|
unit.textContent = baseAsset;
|
|
}
|
|
} else if (side === 'sell') {
|
|
const pInput = document.getElementById('sell-price');
|
|
if (mode === 'market') {
|
|
pInput.value = '市价价格';
|
|
pInput.disabled = true;
|
|
pInput.style.color = '#f0b90b';
|
|
} else {
|
|
pInput.value = currentPrice || '';
|
|
pInput.disabled = false;
|
|
pInput.style.color = 'white';
|
|
}
|
|
} else if (side === 'contract') {
|
|
const pInput = document.getElementById('c-price');
|
|
if (mode === 'market') {
|
|
pInput.value = '市价价格';
|
|
pInput.disabled = true;
|
|
pInput.style.color = '#f0b90b';
|
|
} else {
|
|
pInput.value = currentPrice || '';
|
|
pInput.disabled = false;
|
|
pInput.style.color = 'white';
|
|
}
|
|
}
|
|
}
|
|
|
|
function applySlider(side) {
|
|
const val = document.getElementById(side + '-slider').value;
|
|
if (side === 'buy') {
|
|
const mode = document.getElementById('buy-limit').checked ? 'limit' : 'market';
|
|
if (mode === 'market') {
|
|
document.getElementById('buy-amount').value = (balance * (val / 100)).toFixed(2);
|
|
} else {
|
|
const price = parseFloat(document.getElementById('buy-price').value);
|
|
if (price > 0) document.getElementById('buy-amount').value = (balance * (val / 100) / price).toFixed(4);
|
|
}
|
|
} else if (side === 'sell') {
|
|
document.getElementById('sell-amount').value = (assetBalance * (val / 100)).toFixed(4);
|
|
} else if (side === 'contract') {
|
|
const margin = (balance * (val / 100));
|
|
document.getElementById('c-margin-display').textContent = margin.toFixed(2);
|
|
document.getElementById('c-amount').value = Math.floor((margin * leverage) / 100);
|
|
}
|
|
}
|
|
|
|
function updateSlider(side) {
|
|
if (side === 'buy') {
|
|
const mode = document.getElementById('buy-limit').checked ? 'limit' : 'market';
|
|
const amount = parseFloat(document.getElementById('buy-amount').value) || 0;
|
|
let percent = 0;
|
|
if (mode === 'market') {
|
|
percent = (amount / balance) * 100;
|
|
} else {
|
|
const price = parseFloat(document.getElementById('buy-price').value) || 0;
|
|
percent = (amount * price / balance) * 100;
|
|
}
|
|
document.getElementById('buy-slider').value = Math.min(100, percent);
|
|
} else if (side === 'sell') {
|
|
const amount = parseFloat(document.getElementById('sell-amount').value) || 0;
|
|
document.getElementById('sell-slider').value = Math.min(100, (amount / assetBalance) * 100);
|
|
} else if (side === 'contract') {
|
|
const lots = parseFloat(document.getElementById('c-amount').value) || 0;
|
|
const margin = (100 * lots) / leverage;
|
|
document.getElementById('c-margin-display').textContent = margin.toFixed(2);
|
|
document.getElementById('c-slider').value = Math.min(100, (margin / balance) * 100);
|
|
}
|
|
}
|
|
|
|
function setLev(l) {
|
|
leverage = l;
|
|
document.getElementById('lev-btn').textContent = '杠杆: ' + l + 'x';
|
|
document.getElementById('lev-val').textContent = l + 'x';
|
|
applySlider('contract');
|
|
}
|
|
|
|
async function tick() {
|
|
try {
|
|
const r = await fetch('https://api.binance.com/api/v3/ticker/24hr?symbol=' + symbol);
|
|
const d = await r.json();
|
|
currentPrice = parseFloat(d.lastPrice);
|
|
|
|
document.getElementById('header-price').textContent = currentPrice.toLocaleString();
|
|
document.getElementById('header-price-usd').textContent = currentPrice.toLocaleString();
|
|
document.getElementById('current-price-book').textContent = currentPrice.toLocaleString();
|
|
|
|
const chg = parseFloat(d.priceChangePercent);
|
|
document.getElementById('header-change').textContent = (chg>=0?'+':'') + chg.toFixed(2) + '%';
|
|
document.getElementById('header-change').className = 'fw-bold ' + (chg>=0?'text-success':'text-danger');
|
|
document.getElementById('header-volume').textContent = (parseFloat(d.quoteVolume)/1000000).toFixed(2) + 'M';
|
|
|
|
const askD = document.getElementById('asks'); const bidD = document.getElementById('bids');
|
|
askD.innerHTML = ''; bidD.innerHTML = '';
|
|
for (let i = 0; i < 10; i++) {
|
|
askD.innerHTML = `<div class="order-book-row"><span class="text-danger">${(currentPrice+(10-i)*0.1).toFixed(2)}</span><span>${(Math.random()*1.2).toFixed(4)}</span><div class="ask-bg" style="width:${Math.random()*70}%"></div></div>` + askD.innerHTML;
|
|
bidD.innerHTML += `<div class="order-book-row"><span class="text-success">${(currentPrice-(i+1)*0.1).toFixed(2)}</span><span>${(Math.random()*1.2).toFixed(4)}</span><div class="bid-bg" style="width:${Math.random()*70}%"></div></div>`;
|
|
}
|
|
|
|
if (tradeType === 'SPOT') {
|
|
if (!document.getElementById('buy-price').value && !document.getElementById('buy-price').disabled) document.getElementById('buy-price').value = currentPrice;
|
|
if (!document.getElementById('sell-price').value && !document.getElementById('sell-price').disabled) document.getElementById('sell-price').value = currentPrice;
|
|
} else {
|
|
if (!document.getElementById('c-price').value && !document.getElementById('c-price').disabled) document.getElementById('c-price').value = currentPrice;
|
|
}
|
|
|
|
document.querySelectorAll('.current-p-val').forEach(el => { el.textContent = currentPrice.toLocaleString(); });
|
|
document.querySelectorAll('.upl-val').forEach(el => {
|
|
const entry = parseFloat(el.getAttribute('data-entry'));
|
|
const side = el.getAttribute('data-side');
|
|
const lots = parseFloat(el.getAttribute('data-lots'));
|
|
let upl = 0;
|
|
if (side === 'LONG') upl = (currentPrice - entry) / entry * (lots * 100);
|
|
else upl = (entry - currentPrice) / entry * (lots * 100);
|
|
el.textContent = (upl >= 0 ? '+' : '') + upl.toFixed(2) + ' USDT';
|
|
el.className = 'upl-val ' + (upl >= 0 ? 'text-success' : 'text-danger');
|
|
});
|
|
|
|
} catch(e) {}
|
|
}
|
|
|
|
async function submitOrder(side) {
|
|
const mode = tradeType === 'SPOT'
|
|
? (document.getElementById(side.toLowerCase() + '-limit').checked ? 'LIMIT' : 'MARKET')
|
|
: (document.getElementById('c-limit').checked ? 'LIMIT' : 'MARKET');
|
|
|
|
let price = 0;
|
|
let amount = 0;
|
|
|
|
if (tradeType === 'SPOT') {
|
|
price = document.getElementById(side.toLowerCase() + '-price').value;
|
|
amount = document.getElementById(side.toLowerCase() + '-amount').value;
|
|
} else {
|
|
price = document.getElementById('c-price').value;
|
|
amount = document.getElementById('c-amount').value;
|
|
}
|
|
|
|
try {
|
|
const r = await fetch('{% url "core:submit_order" %}', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': '{{ csrf_token }}' },
|
|
body: JSON.stringify({
|
|
symbol: symbol, side: side, trade_type: tradeType, order_type: mode,
|
|
price: price === '市价价格' ? null : price, amount: amount, leverage: leverage
|
|
})
|
|
});
|
|
const res = await r.json();
|
|
if (res.status === 'success') {
|
|
alert('下单成功!');
|
|
location.reload();
|
|
} else {
|
|
alert('下单失败: ' + res.message);
|
|
}
|
|
} catch(e) { alert('提交出错'); }
|
|
}
|
|
|
|
async function closePos(id) {
|
|
if (confirm('确定要平仓吗?')) {
|
|
const r = await fetch(`/api/close_position/${id}/`, {
|
|
method: 'POST',
|
|
headers: { 'X-CSRFToken': '{{ csrf_token }}' }
|
|
});
|
|
const res = await r.json();
|
|
if (res.status === 'success') location.reload();
|
|
}
|
|
}
|
|
|
|
initTV(); getMarkets(); tick();
|
|
setInterval(tick, 2000);
|
|
</script>
|
|
{% endblock %} |