232 lines
14 KiB
PHP
232 lines
14 KiB
PHP
<?php
|
|
session_start();
|
|
include 'header.php';
|
|
require_once 'db/config.php';
|
|
|
|
$user_id = $_SESSION['user_id'] ?? null;
|
|
$balance = 0;
|
|
if ($user_id) {
|
|
$stmt = db()->prepare("SELECT balance FROM users WHERE id = ?");
|
|
$stmt->execute([$user_id]);
|
|
$user = $stmt->fetch();
|
|
$balance = $user['balance'] ?? 0;
|
|
}
|
|
?>
|
|
|
|
<div class="spot-trading-layout" style="display: grid; grid-template-columns: 280px 1fr 300px 300px; height: calc(100vh - 64px); background: #0b0e11; overflow: hidden; color: #EAECEF;">
|
|
|
|
<!-- Column 1: Pairs List -->
|
|
<div style="border-right: 1px solid #1e2329; display: flex; flex-direction: column; background: #161a1e;">
|
|
<div style="padding: 15px; border-bottom: 1px solid #1e2329;">
|
|
<input type="text" id="pair-search" placeholder="Search Markets" style="width: 100%; background: #0b0e11; border: 1px solid #2b3139; color: white; padding: 8px 12px; border-radius: 4px; font-size: 13px;">
|
|
</div>
|
|
<div id="pairs-list" style="flex: 1; overflow-y: auto;">
|
|
<!-- JS Filled -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Column 2: K-Line & Orders -->
|
|
<div style="display: flex; flex-direction: column; border-right: 1px solid #1e2329; overflow: hidden;">
|
|
<!-- Top Info Bar -->
|
|
<div style="height: 60px; border-bottom: 1px solid #1e2329; display: flex; align-items: center; padding: 0 20px; gap: 30px; background: #161a1e;">
|
|
<div style="display: flex; align-items: center; gap: 10px;">
|
|
<img id="current-icon" src="https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/btc.png" width="24">
|
|
<span style="font-weight: bold; font-size: 1.1rem;" id="current-pair">BTC/USDT</span>
|
|
</div>
|
|
<div style="display: flex; gap: 20px;">
|
|
<div class="stat-box"><div id="top-price" style="font-weight: bold; color: #00c087;">--.---</div><div style="font-size: 10px; color: #848e9c;">Price</div></div>
|
|
<div class="stat-box"><div id="top-change" style="font-weight: bold;">--%</div><div style="font-size: 10px; color: #848e9c;">24h Change</div></div>
|
|
<div class="stat-box"><div id="top-high" style="color: white;">--.---</div><div style="font-size: 10px; color: #848e9c;">24h High</div></div>
|
|
<div class="stat-box"><div id="top-low" style="color: white;">--.---</div><div style="font-size: 10px; color: #848e9c;">24h Low</div></div>
|
|
</div>
|
|
</div>
|
|
<!-- K-Line -->
|
|
<div id="tradingview_chart" style="flex: 1;"></div>
|
|
<!-- Orders Tabs -->
|
|
<div style="height: 300px; border-top: 1px solid #1e2329; display: flex; flex-direction: column; background: #161a1e;">
|
|
<div style="display: flex; border-bottom: 1px solid #1e2329;">
|
|
<div class="order-tab active" onclick="switchOrderTab(this, 'open')"><?php echo __('open_orders'); ?></div>
|
|
<div class="order-tab" onclick="switchOrderTab(this, 'history')"><?php echo __('order_history'); ?></div>
|
|
<div class="order-tab" onclick="switchOrderTab(this, 'trades')"><?php echo __('trade_history'); ?></div>
|
|
<div class="order-tab" onclick="switchOrderTab(this, 'funds')"><?php echo __('funds_flow'); ?></div>
|
|
</div>
|
|
<div style="flex: 1; overflow-y: auto; padding: 15px;">
|
|
<table style="width: 100%; font-size: 12px; color: #848e9c; text-align: left;">
|
|
<thead>
|
|
<tr style="border-bottom: 1px solid #1e2329;">
|
|
<th style="padding-bottom: 8px;">Time</th>
|
|
<th style="padding-bottom: 8px;">Type</th>
|
|
<th style="padding-bottom: 8px;">Side</th>
|
|
<th style="padding-bottom: 8px;">Price</th>
|
|
<th style="padding-bottom: 8px;">Amount</th>
|
|
<th style="padding-bottom: 8px;">Filled</th>
|
|
<th style="padding-bottom: 8px;">Total</th>
|
|
<th style="padding-bottom: 8px;">Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="orders-list-body">
|
|
<tr><td colspan="8" style="text-align: center; padding: 30px; opacity: 0.5;">No active orders</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Column 3: Order Panel -->
|
|
<div style="border-right: 1px solid #1e2329; background: #161a1e; display: flex; flex-direction: column; padding: 20px;">
|
|
<div style="font-size: 14px; font-weight: bold; margin-bottom: 20px;"><?php echo __('trade_panel'); ?></div>
|
|
|
|
<div style="display: flex; background: #2b3139; border-radius: 4px; margin-bottom: 20px;">
|
|
<button class="trade-type-btn active" style="flex: 1; padding: 8px; border: none; background: transparent; color: white; cursor: pointer;">Limit</button>
|
|
<button class="trade-type-btn" style="flex: 1; padding: 8px; border: none; background: transparent; color: #848e9c; cursor: pointer;">Market</button>
|
|
</div>
|
|
|
|
<div style="margin-bottom: 10px; font-size: 12px; color: #848e9c; display: flex; justify-content: space-between;">
|
|
<span>Balance</span>
|
|
<span style="color: white;"><?php echo number_format($balance, 2); ?> USDT</span>
|
|
</div>
|
|
|
|
<!-- Buy -->
|
|
<div style="margin-bottom: 25px;">
|
|
<div class="input-wrap"><span>Price</span><input type="number" id="buy-price" placeholder="0.00"><span>USDT</span></div>
|
|
<div class="input-wrap"><span>Amount</span><input type="number" id="buy-amount" placeholder="0.00"><span id="buy-token">BTC</span></div>
|
|
<div style="margin: 15px 0;"><input type="range" class="slider" style="width: 100%;"></div>
|
|
<button class="trade-btn buy" onclick="placeOrder('buy')">Buy <span class="buy-symbol">BTC</span></button>
|
|
</div>
|
|
|
|
<!-- Sell -->
|
|
<div>
|
|
<div class="input-wrap"><span>Price</span><input type="number" id="sell-price" placeholder="0.00"><span>USDT</span></div>
|
|
<div class="input-wrap"><span>Amount</span><input type="number" id="sell-amount" placeholder="0.00"><span id="sell-token">BTC</span></div>
|
|
<div style="margin: 15px 0;"><input type="range" class="slider" style="width: 100%;"></div>
|
|
<button class="trade-btn sell" onclick="placeOrder('sell')">Sell <span class="sell-symbol">BTC</span></button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Column 4: Order Book -->
|
|
<div style="background: #161a1e; display: flex; flex-direction: column;">
|
|
<div style="padding: 15px; border-bottom: 1px solid #1e2329; font-size: 14px; font-weight: bold;">Order Book</div>
|
|
<div style="padding: 10px 15px; font-size: 11px; color: #848e9c; display: flex; justify-content: space-between;">
|
|
<span>Price(USDT)</span>
|
|
<span>Amount(BTC)</span>
|
|
</div>
|
|
<div style="flex: 1; overflow: hidden; display: flex; flex-direction: column;">
|
|
<div id="asks" style="flex: 1; display: flex; flex-direction: column-reverse; padding: 0 15px; overflow: hidden; font-family: monospace;"></div>
|
|
<div id="mid-price" style="padding: 12px; text-align: center; font-size: 1.1rem; font-weight: bold; color: #00c087; background: rgba(255,255,255,0.03);">--.---</div>
|
|
<div id="bids" style="flex: 1; display: flex; flex-direction: column; padding: 0 15px; overflow: hidden; font-family: monospace;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.stat-box { border-left: 1px solid #2b3139; padding-left: 15px; display: flex; flex-direction: column; }
|
|
.order-tab { padding: 10px 15px; font-size: 12px; color: #848e9c; cursor: pointer; border-bottom: 2px solid transparent; }
|
|
.order-tab.active { color: var(--primary-color); border-bottom-color: var(--primary-color); }
|
|
.input-wrap { background: #0b0e11; border: 1px solid #2b3139; border-radius: 4px; padding: 8px 12px; display: flex; align-items: center; margin-bottom: 12px; gap: 10px; }
|
|
.input-wrap span { font-size: 12px; color: #848e9c; white-space: nowrap; width: 45px; }
|
|
.input-wrap input { background: transparent; border: none; color: white; flex: 1; text-align: right; outline: none; font-weight: bold; }
|
|
.trade-btn { width: 100%; padding: 12px; border: none; border-radius: 4px; font-weight: bold; cursor: pointer; color: white; }
|
|
.trade-btn.buy { background: #00c087; }
|
|
.trade-btn.sell { background: #f6465d; }
|
|
.slider { -webkit-appearance: none; height: 3px; background: #2b3139; border-radius: 2px; }
|
|
.slider::-webkit-slider-thumb { -webkit-appearance: none; width: 12px; height: 12px; background: white; border-radius: 50%; }
|
|
</style>
|
|
|
|
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
|
<script>
|
|
let currentSymbol = 'BTCUSDT';
|
|
let currentPrice = 0;
|
|
const pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT', 'ADAUSDT', 'DOGEUSDT', 'DOTUSDT', 'LINKUSDT', 'LTCUSDT', 'MATICUSDT'];
|
|
|
|
function initChart(symbol) {
|
|
new TradingView.widget({
|
|
"width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol, "interval": "15", "timezone": "Etc/UTC", "theme": "dark", "style": "1", "locale": "<?php echo $lang == 'zh' ? 'zh_CN' : 'en'; ?>", "container_id": "tradingview_chart", "backgroundColor": "#0b0e11", "gridColor": "rgba(42, 46, 57, 0.05)", "hide_side_toolbar": false, "allow_symbol_change": false, "save_image": false
|
|
});
|
|
}
|
|
initChart(currentSymbol);
|
|
|
|
const ws = new WebSocket('wss://stream.binance.com:9443/ws/' + pairs.map(p => p.toLowerCase() + '@ticker').join('/'));
|
|
const marketData = {};
|
|
|
|
ws.onmessage = (event) => {
|
|
const data = JSON.parse(event.data);
|
|
const s = data.s;
|
|
marketData[s] = data;
|
|
renderPairsList();
|
|
|
|
if (s === currentSymbol) {
|
|
currentPrice = parseFloat(data.c);
|
|
document.getElementById('top-price').innerText = currentPrice.toLocaleString();
|
|
document.getElementById('top-price').style.color = data.P >= 0 ? '#00c087' : '#f6465d';
|
|
document.getElementById('top-change').innerText = (data.P >= 0 ? '+' : '') + data.P + '%';
|
|
document.getElementById('top-change').style.color = data.P >= 0 ? '#00c087' : '#f6465d';
|
|
document.getElementById('top-high').innerText = parseFloat(data.h).toLocaleString();
|
|
document.getElementById('top-low').innerText = parseFloat(data.l).toLocaleString();
|
|
document.getElementById('mid-price').innerText = currentPrice.toLocaleString();
|
|
updateOrderBook(currentPrice);
|
|
}
|
|
};
|
|
|
|
function renderPairsList() {
|
|
const list = document.getElementById('pairs-list');
|
|
let html = '';
|
|
pairs.forEach(p => {
|
|
const d = marketData[p] || {c: 0, P: 0};
|
|
const color = d.P >= 0 ? '#00c087' : '#f6465d';
|
|
const name = p.replace('USDT', '');
|
|
html += `
|
|
<div onclick="changePair('${p}')" style="display: flex; justify-content: space-between; padding: 12px 15px; cursor: pointer; border-bottom: 1px solid #1e2329; ${currentSymbol === p ? 'background: #2b3139;' : ''}">
|
|
<div style="display: flex; gap: 8px; align-items: center;">
|
|
<img src="https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/${name.toLowerCase()}.png" width="18" onerror="this.src='https://cdn-icons-png.flaticon.com/512/2585/2585274.png'">
|
|
<span style="font-weight: bold; font-size: 13px;">${name}</span>
|
|
</div>
|
|
<div style="text-align: right;">
|
|
<div style="font-size: 13px;">${parseFloat(d.c).toLocaleString()}</div>
|
|
<div style="color: ${color}; font-size: 11px;">${d.P >= 0 ? '+' : ''}${d.P}%</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
list.innerHTML = html;
|
|
}
|
|
|
|
function changePair(pair) {
|
|
currentSymbol = pair;
|
|
const name = pair.replace('USDT', '');
|
|
document.getElementById('current-pair').innerText = name + '/USDT';
|
|
document.getElementById('current-icon').src = `https://raw.githubusercontent.com/spothq/cryptocurrency-icons/master/128/color/${name.toLowerCase()}.png`;
|
|
document.getElementById('buy-token').innerText = name;
|
|
document.getElementById('sell-token').innerText = name;
|
|
document.querySelectorAll('.buy-symbol, .sell-symbol').forEach(el => el.innerText = name);
|
|
initChart(pair);
|
|
renderPairsList();
|
|
}
|
|
|
|
function updateOrderBook(price) {
|
|
let asksHtml = ''; let bidsHtml = '';
|
|
for(let i=1; i<=20; i++) {
|
|
const askPrice = (price + (i * 0.01 * price / 100)).toFixed(2);
|
|
const bidPrice = (price - (i * 0.01 * price / 100)).toFixed(2);
|
|
asksHtml += `<div style="display: flex; justify-content: space-between; font-size: 11px; padding: 2px 0;"><span style="color: #f6465d;">${askPrice}</span><span style="color: #848e9c;">${(Math.random()*1).toFixed(4)}</span></div>`;
|
|
bidsHtml += `<div style="display: flex; justify-content: space-between; font-size: 11px; padding: 2px 0;"><span style="color: #00c087;">${bidPrice}</span><span style="color: #848e9c;">${(Math.random()*1).toFixed(4)}</span></div>`;
|
|
}
|
|
document.getElementById('asks').innerHTML = asksHtml;
|
|
document.getElementById('bids').innerHTML = bidsHtml;
|
|
}
|
|
|
|
async function placeOrder(side) {
|
|
const price = parseFloat(document.getElementById(side + '-price').value) || currentPrice;
|
|
const amount = parseFloat(document.getElementById(side + '-amount').value);
|
|
if (!amount) return alert('Please enter amount');
|
|
const response = await fetch('api/place_order.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ symbol: currentSymbol, type: 'spot', side: side, order_type: 'limit', price: price, amount: amount, total: price * amount })
|
|
});
|
|
const result = await response.json();
|
|
if (result.success) { alert('Order placed!'); location.reload(); } else { alert('Error: ' + result.error); }
|
|
}
|
|
</script>
|
|
|
|
<?php include 'footer.php'; ?>
|