38350-vm/futures.php
2026-02-14 05:11:30 +00:00

401 lines
23 KiB
PHP

<?php
include 'header.php';
$user_id = $_SESSION['user_id'] ?? null;
$user_assets = [];
if ($user_id) {
$stmt = $db->prepare("SELECT balance FROM users WHERE id = ?");
$stmt->execute([$user_id]);
$usdt_balance = $stmt->fetchColumn();
$user_assets['USDT'] = (float)($usdt_balance ?: 0);
$stmt = $db->prepare("SELECT symbol, amount FROM user_assets WHERE user_id = ?");
$stmt->execute([$user_id]);
$assets_data = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
if ($assets_data) {
foreach ($assets_data as $symbol => $amount) {
$user_assets[$symbol] = (float)$amount;
}
}
}
?>
<style>
.trading-page-wrapper { display: flex; flex-direction: column; min-height: 100vh; background: #0b0e11; }
.trading-container { flex-grow: 1; display: flex; min-height: 800px; max-width: 1920px; margin: 0 auto; width: 100%; }
.left-col { width: 280px; flex-shrink: 0; background: #161a1e; border-right: 1px solid #2b3139; display: flex; flex-direction: column; }
.center-col { flex: 1; min-width: 600px; display: flex; flex-direction: column; border-right: 1px solid #2b3139; }
.right-col { width: 300px; flex-shrink: 0; background: #161a1e; display: flex; flex-direction: column; }
.chart-header { height: 55px; padding: 0 20px; display: flex; align-items: center; background: #161a1e; border-bottom: 1px solid #2b3139; gap: 20px; flex-shrink: 0; }
.chart-box { height: 450px; border-bottom: 1px solid #2b3139; flex-shrink: 0; }
.bottom-content-wrapper { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.order-box { padding: 20px; background: #161a1e; border-bottom: 1px solid #2b3139; flex-shrink: 0; }
.records-container { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
.record-tabs { display: flex; padding: 0 20px; border-bottom: 1px solid #2b3139; flex-shrink: 0; }
.record-tab { padding: 12px 0; margin-right: 25px; font-size: 13px; font-weight: 600; color: #848e9c; cursor: pointer; position: relative; }
.record-tab.active { color: white; }
.record-tab.active::after { content: ''; position: absolute; bottom: 0; left: 0; width: 100%; height: 2px; background: var(--primary-color); }
#records-list-container { height: 350px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #2b3139 transparent; }
#records-list-container::-webkit-scrollbar { width: 4px; }
#records-list-container::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 10px; }
table { width: 100%; font-size: 12px; border-collapse: collapse; }
th { color: #848e9c; font-weight: 500; text-align: left; padding: 10px 15px; border-bottom: 1px solid #2b3139; background: #161a1e; position: sticky; top: 0; z-index: 10; }
td { padding: 10px 15px; border-bottom: 1px solid rgba(255,255,255,0.02); color: #eaecef; height: 38px; }
.category-tabs { display: flex; padding: 15px 15px 5px; gap: 8px; }
.category-tab { flex: 1; text-align: center; padding: 6px 0; background: #2b3139; border-radius: 4px; font-size: 12px; color: #848e9c; cursor: pointer; border: 1px solid transparent; }
.category-tab.active { background: rgba(0, 82, 255, 0.1); border-color: var(--primary-color); color: var(--primary-color); font-weight: bold; }
.search-box { padding: 10px 15px 15px; position: relative; }
.search-box i { position: absolute; left: 25px; top: 22px; color: #848e9c; font-size: 12px; }
.search-box input { width: 100%; background: #2b3139; border: 1px solid transparent; border-radius: 6px; padding: 8px 10px 8px 35px; color: white; font-size: 13px; outline: none; transition: 0.2s; }
.search-box input:focus { border-color: var(--primary-color); }
#pairs-list { height: 684px; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #2b3139 transparent; }
#pairs-list::-webkit-scrollbar { width: 4px; }
#pairs-list::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 10px; }
.pair-item { display: flex; align-items: center; padding: 10px 15px; cursor: pointer; border-bottom: 1px solid rgba(255,255,255,0.02); height: 38px; }
.pair-item.active { background: #1e2329; border-left: 3px solid var(--primary-color); }
.coin-icon { width: 22px; height: 22px; margin-right: 12px; border-radius: 50%; }
.futures-config { display: flex; gap: 10px; margin-bottom: 12px; }
.config-btn { flex: 1; background: #2b3139; color: white; padding: 8px; border-radius: 4px; text-align: center; font-size: 12px; cursor: pointer; border: 1px solid #3c424a; }
.input-group { background: #2b3139; border-radius: 4px; padding: 8px 12px; display: flex; align-items: center; margin-bottom: 10px; border: 1px solid #3c424a; }
.input-group:focus-within { border-color: var(--primary-color); }
.input-group input { background: transparent; border: none; color: white; flex: 1; outline: none; font-size: 14px; width: 100%; }
.input-group .label { color: #848e9c; font-size: 12px; margin-right: 10px; min-width: 40px; }
.input-group .unit { color: #848e9c; margin-left: 8px; font-size: 12px; }
.slider-container { margin: 15px 0 20px; }
input[type=range] { width: 100%; height: 4px; background: #2b3139; border-radius: 2px; outline: none; }
.trade-btn { width: 100%; padding: 12px; border-radius: 4px; font-weight: bold; font-size: 15px; border: none; cursor: pointer; color: white; transition: 0.2s; }
.btn-buy { background: #0ecb81; }
.btn-sell { background: #f6465d; }
.trade-btn:hover { filter: brightness(1.1); }
.right-col-tabs { display: flex; border-bottom: 1px solid #2b3139; flex-shrink: 0; }
.right-col-tab { flex: 1; text-align: center; padding: 12px 0; font-size: 13px; font-weight: 600; color: #848e9c; cursor: pointer; }
.right-col-tab.active { color: white; background: #1e2329; }
.ob-header { display: flex; justify-content: space-between; padding: 8px 15px; font-size: 11px; color: #848e9c; border-bottom: 1px solid #2b3139; }
.ob-row { display: flex; justify-content: space-between; padding: 3px 15px; font-size: 12px; position: relative; cursor: pointer; }
#ob-panel-content { flex: 1; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #2b3139 transparent; }
#ob-panel-content::-webkit-scrollbar { width: 4px; }
#ob-panel-content::-webkit-scrollbar-thumb { background: #2b3139; border-radius: 10px; }
/* Mobile Specific Styles */
@media (max-width: 991px) {
.trading-container { flex-direction: column; min-height: auto; }
.center-col { min-width: 100%; border-right: none; }
.chart-box { height: 300px; }
.order-box { padding: 15px; }
#records-list-container { height: auto; max-height: 400px; }
.trading-page-wrapper { padding-bottom: 70px; }
.mobile-trade-nav {
display: flex; background: #161a1e; border-bottom: 1px solid #2b3139; padding: 10px 15px; gap: 10px;
}
.mobile-trade-nav a {
flex: 1; text-align: center; padding: 8px 0; background: #2b3139; border-radius: 8px; font-size: 13px; color: #848e9c; text-decoration: none; font-weight: 600; border: 1px solid transparent;
}
.mobile-trade-nav a.active {
background: rgba(0, 82, 255, 0.1); color: var(--primary-color); border-color: var(--primary-color);
}
.mobile-symbol-selector {
display: flex; align-items: center; justify-content: space-between; padding: 12px 15px; background: #161a1e; border-bottom: 1px solid #2b3139;
}
}
@media (min-width: 992px) {
.mobile-trade-nav, .mobile-symbol-selector { display: none; }
}
/* Mobile Drawer */
#mobile-pairs-drawer {
position: fixed; top: 0; left: -100%; width: 100%; height: 100%; background: #0b0e11; z-index: 3000; transition: 0.3s; padding: 20px; display: flex; flex-direction: column;
}
#mobile-pairs-drawer.open { left: 0; }
</style>
<div class="trading-page-wrapper">
<!-- Mobile Navigation Tabs -->
<div class="mobile-trade-nav">
<a href="options.php"><?php echo __('nav_options'); ?></a>
<a href="spot.php"><?php echo __('nav_spot'); ?></a>
<a href="futures.php" class="active"><?php echo __('nav_futures'); ?></a>
</div>
<!-- Mobile Symbol Selector -->
<div class="mobile-symbol-selector" onclick="toggleMobilePairs()">
<div style="display: flex; align-items: center; gap: 10px;">
<img id="curr-icon-mobile" src="" class="coin-icon" style="width: 28px; height: 28px;">
<span id="curr-pair-mobile" style="font-weight: 800; font-size: 18px; color: white;">--/--</span>
<i class="fas fa-caret-down" style="color: #848e9c;"></i>
</div>
<div style="text-align: right;">
<div id="curr-price-mobile" style="font-size: 18px; font-weight: 800; color: #0ecb81;">--</div>
<div id="curr-change-mobile" style="font-size: 12px; font-weight: 600;">--</div>
</div>
</div>
<div class="trading-container">
<!-- Left Column -->
<div class="left-col d-none d-lg-flex">
<div class="category-tabs">
<div class="category-tab" onclick="location.href='options.php'"><?php echo __('nav_options'); ?></div>
<div class="category-tab" onclick="location.href='spot.php'"><?php echo __('nav_spot'); ?></div>
<div class="category-tab active" onclick="location.href='futures.php'"><?php echo __('nav_futures'); ?></div>
</div>
<div class="search-box"><i class="fas fa-search"></i><input type="text" id="pair-search" placeholder="<?php echo __('search_contract'); ?>"></div>
<div id="pairs-list"></div>
</div>
<!-- Center Column -->
<div class="center-col">
<div class="chart-header d-none d-lg-flex">
<div style="display: flex; align-items: center; gap: 10px;">
<img id="curr-icon" src="" class="coin-icon">
<span id="curr-pair" style="font-weight: 800; font-size: 18px; color:white;">--/--</span>
</div>
<div class="stats-item">
<div id="curr-price" style="font-size: 20px; font-weight: 800; color: #0ecb81;">--</div>
<div id="curr-change" style="font-size: 12px; font-weight: 600;">--</div>
</div>
</div>
<div class="chart-box" id="tv_chart_container"></div>
<div class="bottom-content-wrapper">
<div class="order-box">
<div class="futures-config">
<div class="config-btn"><?php echo __('cross'); ?></div>
<div class="config-btn" id="leverage-val">20x</div>
</div>
<div class="input-group"><span class="label"><?php echo __('price'); ?></span><input type="text" placeholder="<?php echo __('market'); ?>" disabled><span class="unit">USDT</span></div>
<div class="input-group"><span class="label"><?php echo __('amount'); ?></span><input type="number" id="futures-amount" step="0.0001" placeholder="0.00"><span class="unit coin-name">--</span></div>
<div class="slider-container"><input type="range" id="futures-slider" min="0" max="100" value="0"></div>
<div style="font-size: 12px; color: #848e9c; margin-bottom: 12px;"><?php echo __('available'); ?>: <span id="avail-usdt" style="color:white; font-weight:600">0.00</span> USDT</div>
<div style="display: flex; gap: 15px;">
<button id="btn-buy" class="trade-btn btn-buy"><?php echo __('open_long'); ?></button>
<button id="btn-sell" class="trade-btn btn-sell"><?php echo __('open_short'); ?></button>
</div>
</div>
<div class="records-container">
<div class="record-tabs">
<div class="record-tab active" data-status="positions"><?php echo __('current_positions'); ?></div>
<div class="record-tab" data-status="history"><?php echo __('history_orders'); ?></div>
</div>
<div id="records-list-container">
<table style="width:100%;" id="records-table"></table>
</div>
</div>
</div>
</div>
<!-- Right Column -->
<div class="right-col d-none d-xl-flex">
<div class="right-col-tabs">
<div class="right-col-tab active" data-tab="ob"><?php echo __('order_book'); ?></div>
<div class="right-col-tab" data-tab="trades"><?php echo __('trades'); ?></div>
</div>
<div id="ob-panel" style="flex: 1; display: flex; flex-direction: column; overflow: hidden;">
<div class="ob-header"><span><?php echo __('price'); ?></span><span><?php echo __('amount'); ?></span></div>
<div id="ob-panel-content">
<div id="asks-list"></div>
<div id="mid-price" style="padding:10px 15px; text-align:center; font-weight:800; font-size:16px; border-top:1px solid #2b3139; border-bottom:1px solid #2b3139;">--</div>
<div id="bids-list"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Mobile Pairs Drawer -->
<div id="mobile-pairs-drawer">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<h3 style="margin: 0; font-weight: 800;"><?php echo __('market'); ?></h3>
<i class="fas fa-times" onclick="toggleMobilePairs()" style="font-size: 20px; color: #848e9c;"></i>
</div>
<div class="search-box" style="padding: 0; margin-bottom: 20px;">
<i class="fas fa-search"></i>
<input type="text" id="pair-search-mobile" placeholder="<?php echo __('search_contract'); ?>">
</div>
<div id="pairs-list-mobile" style="flex: 1; overflow-y: auto;"></div>
</div>
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
<script>
const userAssets = <?php echo json_encode($user_assets); ?>;
function toggleMobilePairs() {
document.getElementById('mobile-pairs-drawer').classList.toggle('open');
}
document.addEventListener('DOMContentLoaded', function() {
let currentPair = 'BTCUSDT', currentPrice = 0, ws, leverage = 20, chartWidget;
const marketData = {}, pairs = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT', 'ADAUSDT', 'DOGEUSDT', 'AVAXUSDT', 'DOTUSDT', 'MATICUSDT', 'LTCUSDT', 'TRXUSDT', 'LINKUSDT', 'UNIUSDT', 'ATOMUSDT', 'ETCUSDT', 'BCHUSDT', 'FILUSDT', 'ICPUSDT', 'NEARUSDT', 'AAVEUSDT', 'ALGOUSDT'];
const dom = {
currIcon: document.getElementById('curr-icon'),
currPair: document.getElementById('curr-pair'),
currPrice: document.getElementById('curr-price'),
currChange: document.getElementById('curr-change'),
currIconMobile: document.getElementById('curr-icon-mobile'),
currPairMobile: document.getElementById('curr-pair-mobile'),
currPriceMobile: document.getElementById('curr-price-mobile'),
currChangeMobile: document.getElementById('curr-change-mobile'),
tvContainer: document.getElementById('tv_chart_container'),
pairsList: document.getElementById('pairs-list'),
pairsListMobile: document.getElementById('pairs-list-mobile'),
pairSearchMobile: document.getElementById('pair-search-mobile'),
asksList: document.getElementById('asks-list'),
bidsList: document.getElementById('bids-list'),
midPrice: document.getElementById('mid-price'),
availUsdt: document.getElementById('avail-usdt'),
coinNames: document.querySelectorAll('.coin-name'),
amountInput: document.getElementById('futures-amount'),
slider: document.getElementById('futures-slider'),
recordsTable: document.getElementById('records-table')
};
function updateBalances() {
dom.availUsdt.innerText = parseFloat(userAssets.USDT || 0).toFixed(2);
dom.coinNames.forEach(el => el.innerText = currentPair.replace('USDT', ''));
}
function initChart(symbol) {
dom.tvContainer.innerHTML = '';
new TradingView.widget({
"autosize": true, "symbol": `BINANCE:${symbol}`, "interval": "15", "theme": "dark", "style": "1",
"locale": "<?php echo $lang == 'zh' ? 'zh_CN' : 'en'; ?>", "container_id": "tv_chart_container",
"hide_side_toolbar": true, "allow_symbol_change": false, "studies": ["Volume@tv-basicstudies"],
"overrides": { "paneProperties.background": "#161a1e" }
});
}
function connectWS() {
if (ws) ws.close();
const streams = pairs.map(p => `${p.toLowerCase()}@ticker`).concat([`${currentPair.toLowerCase()}@depth20@100ms`]);
ws = new WebSocket(`wss://stream.binance.com:9443/stream?streams=${streams.join('/')}`);
ws.onmessage = (e) => {
const { stream, data } = JSON.parse(e.data);
if (!data) return;
if (stream.endsWith('@ticker')) {
marketData[data.s] = data;
if (data.s === currentPair) {
currentPrice = parseFloat(data.c);
const color = data.P >= 0 ? '#0ecb81' : '#f6465d';
const priceStr = currentPrice.toLocaleString('en-US', {minimumFractionDigits:2});
const changeStr = (data.P >= 0 ? '+' : '') + parseFloat(data.P).toFixed(2) + '%';
if(dom.currPrice) dom.currPrice.innerText = priceStr;
if(dom.currChange) dom.currChange.innerText = changeStr;
if(dom.currPrice) dom.currPrice.style.color = dom.currChange.style.color = color;
dom.currPriceMobile.innerText = priceStr;
dom.currPriceMobile.style.color = color;
dom.currChangeMobile.innerText = changeStr;
dom.currChangeMobile.style.color = color;
if(dom.midPrice) dom.midPrice.innerText = currentPrice.toFixed(2);
}
if (dom.pairsList.offsetParent || dom.pairsListMobile.offsetParent) renderPairs();
} else if (stream.endsWith('@depth20@100ms')) {
if(dom.asksList) {
dom.asksList.innerHTML = data.asks.slice(0, 20).reverse().map(a => `<div class="ob-row" onclick="document.getElementById('futures-amount').value=${parseFloat(a[1]).toFixed(4)};"><span style="color:#f6465d">${parseFloat(a[0]).toFixed(2)}</span><span>${parseFloat(a[1]).toFixed(4)}</span></div>`).join('');
dom.bidsList.innerHTML = data.bids.slice(0, 20).map(b => `<div class="ob-row" onclick="document.getElementById('futures-amount').value=${parseFloat(b[1]).toFixed(4)};"><span style="color:#0ecb81">${parseFloat(b[0]).toFixed(2)}</span><span>${parseFloat(b[1]).toFixed(4)}</span></div>`).join('');
}
}
};
ws.onclose = () => setTimeout(connectWS, 5000);
}
function renderPairs() {
const query = document.getElementById('pair-search').value.toUpperCase();
const queryMobile = dom.pairSearchMobile.value.toUpperCase();
const generateHtml = (q) => pairs.filter(p => p.includes(q)).map(p => {
const d = marketData[p] || {};
return `<div class="pair-item ${p === currentPair ? 'active' : ''}" onclick="switchPair('${p}')">
<img src="https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png" class="coin-icon" onerror="this.style.opacity=0">
<div style="flex:1; color:white; font-weight:600">${p.replace('USDT','/USDT')}</div>
<div style="text-align:right"><div style="color:white; font-weight:600">${d.c ? parseFloat(d.c).toFixed(2) : '--'}</div><div style="color:${d.P>=0?'#0ecb81':'#f6465d'}; font-size:11px">${d.P?(d.P>=0?'+':'')+parseFloat(d.P).toFixed(2)+'%':'--'}</div></div>
</div>`;
}).join('');
dom.pairsList.innerHTML = generateHtml(query);
dom.pairsListMobile.innerHTML = generateHtml(queryMobile);
}
window.switchPair = (p) => {
currentPair = p;
if(dom.currPair) dom.currPair.innerText = p.replace('USDT', '/USDT') + ' Perp';
dom.currPairMobile.innerText = p.replace('USDT', '/USDT') + ' Perp';
if(dom.currIcon) dom.currIcon.src = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
dom.currIconMobile.src = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
initChart(p);
updateBalances();
connectWS();
if(document.getElementById('mobile-pairs-drawer').classList.contains('open')) toggleMobilePairs();
};
dom.pairSearchMobile.addEventListener('input', renderPairs);
async function placeOrder(side) {
const amount = parseFloat(dom.amountInput.value);
if (!amount || amount <= 0) { alert("Amount > 0"); return; }
const price = currentPrice;
const total = amount * price;
try {
const r = await fetch('api/place_order.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
symbol: currentPair, type: 'futures', side: side, order_type: 'market',
price: price, amount: amount, total: total, leverage: leverage
})
});
const res = await r.json();
if (res.success) { alert("<?php echo __('order_placed'); ?>"); fetchOrders(); }
else { alert(res.error || "Error"); }
} catch(e) { alert("Network Error"); }
}
document.getElementById('btn-buy').onclick = () => placeOrder('buy');
document.getElementById('btn-sell').onclick = () => placeOrder('sell');
let activeTab = 'positions';
async function fetchOrders() {
try {
const r = await fetch(`api/get_orders.php?type=futures&status=${activeTab}`);
const res = await r.json();
if (res.success) renderOrders(res.data);
} catch(e) {}
}
function renderOrders(orders) {
if (!orders || orders.length === 0) { dom.recordsTable.innerHTML = '<tr><td colspan="6" style="text-align:center; padding:30px; color:#848e9c"><?php echo __('no_records'); ?></td></tr>'; return; }
let h = `<thead><tr><th><?php echo __('pair'); ?></th><th><?php echo __('direction'); ?></th><th><?php echo __('price'); ?></th><th><?php echo __('amount'); ?></th><th><?php echo __('leverage'); ?></th><th><?php echo __('time'); ?></th></tr></thead><tbody>`;
orders.forEach(o => {
h += `<tr><td>${o.symbol}</td><td style="color:${o.side==='buy'?'#0ecb81':'#f6465d'}">${o.side==='buy'?'<?php echo __('buy_long'); ?>':'<?php echo __('sell_short'); ?>'}</td><td>${parseFloat(o.price).toFixed(2)}</td><td>${parseFloat(o.amount).toFixed(4)}</td><td>${o.leverage}x</td><td style="color:#848e9c">${o.created_at}</td></tr>`;
});
dom.recordsTable.innerHTML = h + '</tbody>';
}
document.querySelectorAll('.record-tab').forEach(t => t.onclick = () => {
if (!t.dataset.status) return;
document.querySelector('.record-tabs .active').classList.remove('active');
t.classList.add('active');
activeTab = t.dataset.status;
fetchOrders();
});
switchPair('BTCUSDT');
fetchOrders();
setInterval(fetchOrders, 5000);
});
</script>
<?php include 'footer.php'; ?>