477 lines
25 KiB
PHP
477 lines
25 KiB
PHP
<?php
|
|
include 'header.php';
|
|
require_once 'db/config.php';
|
|
require_once 'includes/i18n.php';
|
|
|
|
$user_id = $_SESSION['user_id'] ?? null;
|
|
$user_assets = [];
|
|
if ($user_id) {
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("SELECT currency, balance FROM assets WHERE user_id = ?");
|
|
$stmt->execute([$user_id]);
|
|
$assets_data = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
|
|
if ($assets_data) {
|
|
$user_assets = $assets_data;
|
|
}
|
|
}
|
|
?>
|
|
|
|
<style>
|
|
/* New Robust Layout */
|
|
html, body {
|
|
/* height: 100%; */
|
|
/* overflow: hidden; */ /* UNLOCKED for scrolling */
|
|
}
|
|
.trading-page-wrapper {
|
|
/* height: 100vh; */ /* UNLOCKED for scrolling */
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 100vh; /* Ensure it takes at least full height */
|
|
}
|
|
.trading-page-wrapper .navbar {
|
|
flex-shrink: 0;
|
|
}
|
|
.trading-container {
|
|
flex-grow: 1;
|
|
display: flex;
|
|
min-height: 800px; /* Give a minimum height for content */
|
|
background: #0b0e11;
|
|
}
|
|
|
|
/* Columns */
|
|
.left-col { width: 260px; flex-shrink: 0; background: #161a1e; display: flex; flex-direction: column; border-right: 1px solid #2b3139; }
|
|
.center-col { flex: 1; min-width: 600px; display: flex; flex-direction: column; border-right: 1px solid #2b3139; min-height: 0; }
|
|
.right-col { width: 320px; flex-shrink: 0; background: #161a1e; display: flex; flex-direction: column; }
|
|
|
|
/* Center Column Content Layout */
|
|
.chart-header { flex-shrink: 0; height: 50px; padding: 8px 20px; display: flex; align-items: center; background: #161a1e; border-bottom: 1px solid #2b3139; gap: 20px; }
|
|
.chart-box { height: 350px; flex-shrink: 0; border-bottom: 1px solid #2b3139; }
|
|
.bottom-content-wrapper { flex-grow: 1; overflow-y: auto; min-height: 0; }
|
|
|
|
.order-box { padding: 12px 20px; background: #161a1e; border-bottom: 1px solid #2b3139; }
|
|
.records-container { display: flex; flex-direction: column; background: #161a1e; flex-grow: 1; min-height: 400px; }
|
|
.record-tabs { display: flex; padding: 0 20px; flex-shrink: 0; border-bottom: 1px solid #2b3139; }
|
|
.record-tab { padding: 12px 0; margin-right: 25px; font-size: 14px; font-weight: 600; color: #848e9c; cursor: pointer; border-bottom: 2px solid transparent; }
|
|
.record-tab.active { color: white; border-bottom-color: var(--primary-color); }
|
|
#records-list { padding: 10px 0; flex-grow: 1; overflow-y: auto; }
|
|
|
|
.right-col { display: flex; flex-direction: column;}
|
|
.ob-panel, .trades-panel { flex-grow: 1; min-height: 0; display: flex; flex-direction: column;}
|
|
.ob-content { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
#asks-list { flex: 1 1 50%; display: flex; flex-direction: column; overflow: hidden; justify-content: flex-end; }
|
|
#bids-list { flex: 1 1 50%; display: flex; flex-direction: column; overflow: hidden; justify-content: flex-start; }
|
|
#trades-content { flex: 1; overflow-y: auto; min-height: 0; }
|
|
|
|
.left-col { overflow-y: auto; }
|
|
#pairs-list { flex: 1; overflow-y: auto; }
|
|
.ob-header, .trades-header { padding: 10px 15px; font-size: 12px; color: #848e9c; display: flex; justify-content: space-between; border-bottom: 1px solid #2b3139; background: #161a1e; flex-shrink: 0;}
|
|
.ob-row, .trade-row { display: flex; justify-content: space-between; padding: 3px 15px; font-size: 12px; position: relative; cursor: pointer; height: 20px; align-items: center; flex-shrink: 0; }
|
|
.ob-bar { position: absolute; right: 0; top: 0; bottom: 0; opacity: 0.1; z-index: 0; transition: width 0.1s linear; }
|
|
#mid-price { padding: 8px 15px; font-size: 16px; font-weight: 800; text-align: center; border-top: 1px solid #2b3139; border-bottom: 1px solid #2b3139; background: #161a1e; flex-shrink: 0;}
|
|
.coin-icon { width: 22px; height: 22px; margin-right: 12px; border-radius: 50%; }
|
|
.stats-item { display: flex; flex-direction: column; justify-content: center; }
|
|
.stats-label { font-size: 10px; color: #848e9c; margin-bottom: 1px; }
|
|
.stats-value { font-size: 12px; font-weight: 600; color: white; }
|
|
#websocket-status { display: none; /* Hiding this element as requested */ padding: 8px 15px; font-size: 12px; color: #fff; text-align: center; position: absolute; top: 0; left: 0; right: 0; z-index: 1000; }
|
|
|
|
.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; transition: all 0.2s; 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: 5px 15px 10px; position: relative; }
|
|
.search-box i { position: absolute; left: 25px; top: 50%; transform: translateY(-50%); color: #848e9c; font-size: 12px; }
|
|
.search-box input { width: 100%; background: #2b3139; border: 1px solid transparent; border-radius: 4px; padding: 8px 10px 8px 30px; color: white; font-size: 13px; outline: none; }
|
|
.pair-item { display: flex; align-items: center; padding: 10px 15px; cursor: pointer; transition: background 0.2s; border-bottom: 1px solid rgba(255,255,255,0.02); }
|
|
.pair-item.active { background: #1e2329; }
|
|
.order-type-tabs { display: flex; gap: 20px; margin-bottom: 10px; }
|
|
.order-type-tab { font-size: 13px; color: #848e9c; cursor: pointer; padding-bottom: 4px; border-bottom: 2px solid transparent; }
|
|
.order-type-tab.active { color: white; border-bottom-color: var(--primary-color); font-weight: bold; }
|
|
.input-group { background: #2b3139; border-radius: 4px; padding: 6px 12px; display: flex; align-items: center; margin-bottom: 6px; border: 1px solid transparent; }
|
|
.input-group:focus-within { border-color: var(--primary-color); }
|
|
.input-group input { background: transparent; border: none; color: white; flex: 1; outline: none; font-size: 13px; width: 100%; }
|
|
.input-group .label { color: #848e9c; font-size: 11px; margin-right: 10px; }
|
|
.input-group .unit { color: #848e9c; margin-left: 8px; font-size: 11px; }
|
|
.slider-container { margin: 8px 0 15px; padding: 0 5px; }
|
|
input[type=range] { -webkit-appearance: none; width: 100%; height: 3px; background: #2b3139; border-radius: 2px; outline: none; }
|
|
input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 14px; height: 14px; background: var(--primary-color); border-radius: 50%; cursor: pointer; }
|
|
.trade-btn { width: 100%; padding: 10px; border-radius: 4px; font-weight: bold; font-size: 14px; border: none; cursor: pointer; margin-top: 5px; }
|
|
.btn-buy { background: #0ecb81; color: white; }
|
|
.btn-sell { background: #f6465d; color: white; }
|
|
.right-col-tabs { display: flex; border-bottom: 1px solid #2b3139; flex-shrink: 0;}
|
|
.right-col-tab { flex: 1; text-align: center; padding: 10px 0; font-size: 13px; font-weight: 600; color: #848e9c; cursor: pointer; background: #161a1e; }
|
|
.right-col-tab.active { color: white; background: #1e2329;}
|
|
</style>
|
|
|
|
<div class="trading-page-wrapper">
|
|
<?php // The real header is included in the file, this is a placeholder for the structure. We will inject the actual header.php content. ?>
|
|
<div class="trading-container">
|
|
<div class="left-col d-none d-lg-flex flex-column">
|
|
<div class="category-tabs">
|
|
<div class="category-tab" onclick="location.href='options.php'"><?php echo __('nav_options'); ?></div>
|
|
<div class="category-tab active" onclick="location.href='spot.php'"><?php echo __('nav_spot'); ?></div>
|
|
<div class="category-tab" 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_currency'); ?>">
|
|
</div>
|
|
<div id="pairs-list" class="flex-grow-1" style="overflow-y: auto;"></div>
|
|
</div>
|
|
|
|
<div class="center-col">
|
|
<div id="websocket-status"></div>
|
|
<div class="chart-header">
|
|
<div style="display: flex; align-items: center; gap: 10px; padding-right: 15px; border-right: 1px solid #2b3139;">
|
|
<img id="curr-icon" src="" class="coin-icon" style="margin:0; width:28px; height:28px;">
|
|
<span id="curr-pair" style="font-weight: 800; font-size: 16px;">--/--</span>
|
|
</div>
|
|
<div class="stats-item">
|
|
<div id="curr-price" style="font-size: 18px; font-weight: 800; color: #0ecb81; line-height: 1.2;">--</div>
|
|
<div id="curr-change" style="font-size: 11px; font-weight: 600;">--</div>
|
|
</div>
|
|
<div class="stats-item"><span class="stats-label"><?php echo __('24h_high'); ?></span><span class="stats-value" id="h-high">--</span></div>
|
|
<div class="stats-item"><span class="stats-label"><?php echo __('24h_low'); ?></span><span class="stats-value" id="h-low">--</span></div>
|
|
<div class="stats-item"><span class="stats-label"><?php echo __('24h_vol'); ?></span><span class="stats-value" id="h-vol">--</span></div>
|
|
</div>
|
|
|
|
<div class="chart-box" id="tv_chart_container"></div>
|
|
|
|
<div class="bottom-content-wrapper">
|
|
<div class="order-box">
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 25px;">
|
|
<div>
|
|
<div class="order-type-tabs" data-side="buy">
|
|
<div class="order-type-tab active" data-type="limit"><?php echo __('limit'); ?></div>
|
|
<div class="order-type-tab" data-type="market"><?php echo __('market'); ?></div>
|
|
</div>
|
|
<div class="input-group"><span class="label"><?php echo __('price'); ?></span><input type="number" id="buy-price" placeholder="0.00"><span class="unit">USDT</span></div>
|
|
<div class="input-group"><span class="label"><?php echo __('amount'); ?></span><input type="number" id="buy-amount" placeholder="0.00"><span class="unit coin-name">--</span></div>
|
|
<div class="slider-container"><input type="range" id="buy-slider" min="0" max="100" value="0" step="1"></div>
|
|
<div style="font-size:11px; color:#848e9c; margin-bottom:8px; display:flex; justify-content:space-between"><span><?php echo __('available'); ?>:</span><span id="avail-usdt" style="font-weight:bold; color:white;">0.00 USDT</span></div>
|
|
<button id="btn-buy" class="trade-btn btn-buy"><?php echo __('buy'); ?> <span class="coin-name">--</span></button>
|
|
</div>
|
|
<div>
|
|
<div class="order-type-tabs" data-side="sell">
|
|
<div class="order-type-tab active" data-type="limit"><?php echo __('limit'); ?></div>
|
|
<div class="order-type-tab" data-type="market"><?php echo __('market'); ?></div>
|
|
</div>
|
|
<div class="input-group"><span class="label"><?php echo __('price'); ?></span><input type="number" id="sell-price" placeholder="0.00"><span class="unit">USDT</span></div>
|
|
<div class="input-group"><span class="label"><?php echo __('amount'); ?></span><input type="number" id="sell-amount" placeholder="0.00"><span class="unit coin-name">--</span></div>
|
|
<div class="slider-container"><input type="range" id="sell-slider" min="0" max="100" value="0" step="1"></div>
|
|
<div style="font-size:11px; color:#848e9c; margin-bottom:8px; display:flex; justify-content:space-between"><span><?php echo __('available'); ?>:</span><span id="avail-coin" style="font-weight:bold; color:white;">0.00 --</span></div>
|
|
<button id="btn-sell" class="trade-btn btn-sell"><?php echo __('sell'); ?> <span class="coin-name">--</span></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="records-container">
|
|
<div class="record-tabs">
|
|
<div class="record-tab active" data-status="open"><?php echo __('current_orders'); ?></div>
|
|
<div class="record-tab" data-status="history"><?php echo __('history_orders'); ?></div>
|
|
</div>
|
|
<div id="records-list"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="right-col d-none d-lg-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" class="ob-panel">
|
|
<div class="ob-header">
|
|
<span><?php echo __('price'); ?>(USDT)</span>
|
|
<span><?php echo __('amount'); ?>(<span class="coin-name">--</span>)</span>
|
|
<span><?php echo __('total'); ?></span>
|
|
</div>
|
|
<div class="ob-content">
|
|
<div id="asks-list"></div>
|
|
<div id="mid-price">--</div>
|
|
<div id="bids-list"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="trades-panel" class="trades-panel" style="display: none;">
|
|
<div class="trades-header">
|
|
<span><?php echo __('price'); ?>(USDT)</span>
|
|
<span><?php echo __('amount'); ?>(<span class="coin-name">--</span>)</span>
|
|
<span><?php echo __('time'); ?></span>
|
|
</div>
|
|
<div id="trades-content"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const userAssets = <?php echo json_encode($user_assets); ?>;
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// --- Globals ---
|
|
let currentPair = 'BTCUSDT';
|
|
let currentPrice = 0;
|
|
let ws;
|
|
const marketData = {};
|
|
const pairs = [
|
|
'BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'XRPUSDT', 'ADAUSDT', 'DOGEUSDT', 'AVAXUSDT',
|
|
'DOTUSDT', 'MATICUSDT', 'LTCUSDT', 'TRXUSDT', 'LINKUSDT', 'UNIUSDT', 'ATOMUSDT', 'ETCUSDT',
|
|
'BCHUSDT', 'FILUSDT', 'ICPUSDT', 'NEARUSDT', 'AAVEUSDT', 'ALGOUSDT'
|
|
];
|
|
|
|
// --- DOM Elements ---
|
|
const dom = {
|
|
websocketStatus: document.getElementById('websocket-status'),
|
|
currIcon: document.getElementById('curr-icon'),
|
|
currPair: document.getElementById('curr-pair'),
|
|
currPrice: document.getElementById('curr-price'),
|
|
currChange: document.getElementById('curr-change'),
|
|
hHigh: document.getElementById('h-high'),
|
|
hLow: document.getElementById('h-low'),
|
|
hVol: document.getElementById('h-vol'),
|
|
tvChartContainer: document.getElementById('tv_chart_container'),
|
|
pairsList: document.getElementById('pairs-list'),
|
|
pairSearch: document.getElementById('pair-search'),
|
|
asksList: document.getElementById('asks-list'),
|
|
bidsList: document.getElementById('bids-list'),
|
|
midPrice: document.getElementById('mid-price'),
|
|
tradesContent: document.getElementById('trades-content'),
|
|
coinNameSpans: document.querySelectorAll('.coin-name'),
|
|
buyPriceInput: document.getElementById('buy-price'),
|
|
sellPriceInput: document.getElementById('sell-price'),
|
|
availUsdtSpan: document.getElementById('avail-usdt'),
|
|
availCoinSpan: document.getElementById('avail-coin')
|
|
};
|
|
|
|
// --- Balance Management ---
|
|
function updateAvailableBalances() {
|
|
const baseCurrency = currentPair.replace('USDT', '');
|
|
const usdtBalance = parseFloat(userAssets.USDT || 0).toFixed(2);
|
|
const coinBalance = parseFloat(userAssets[baseCurrency] || 0).toFixed(6);
|
|
|
|
if (dom.availUsdtSpan) {
|
|
dom.availUsdtSpan.innerText = `${usdtBalance} USDT`;
|
|
}
|
|
if (dom.availCoinSpan) {
|
|
dom.availCoinSpan.innerText = `${coinBalance} ${baseCurrency}`;
|
|
}
|
|
}
|
|
|
|
// --- TradingView Chart ---
|
|
function initChartWidget(symbol) {
|
|
if (dom.tvChartContainer.firstChild) {
|
|
dom.tvChartContainer.innerHTML = '';
|
|
}
|
|
const widget = new TradingView.widget({
|
|
"autosize": true,
|
|
"symbol": `BINANCE:${symbol}`,
|
|
"interval": "15",
|
|
"timezone": "Etc/UTC",
|
|
"theme": "dark",
|
|
"style": "1",
|
|
"locale": "<?php echo $lang == 'zh' ? 'zh_CN' : 'en'; ?>",
|
|
"toolbar_bg": "#f1f3f6",
|
|
"enable_publishing": false,
|
|
"withdateranges": true,
|
|
"hide_side_toolbar": true,
|
|
"allow_symbol_change": false,
|
|
"container_id": "tv_chart_container",
|
|
"studies": [
|
|
"Volume@tv-basicstudies"
|
|
],
|
|
"overrides": {
|
|
"paneProperties.background": "#161a1e",
|
|
"paneProperties.vertGridProperties.color": "rgba(255, 255, 255, 0.05)",
|
|
"paneProperties.horzGridProperties.color": "rgba(255, 255, 255, 0.05)",
|
|
"scalesProperties.textColor" : "#848e9c",
|
|
}
|
|
});
|
|
}
|
|
|
|
// --- WebSocket Connection ---
|
|
function connectWebSocket() {
|
|
if (ws) ws.close();
|
|
|
|
// setWebsocketStatus('<?php echo __('websocket_connecting'); ?>', '#ffc107'); // Hidden
|
|
|
|
const streams = pairs.map(p => `${p.toLowerCase()}@ticker`).concat([`${currentPair.toLowerCase()}@depth20@100ms`, `${currentPair.toLowerCase()}@trade`]);
|
|
ws = new WebSocket(`wss://stream.binance.com:9443/stream?streams=${streams.join('/')}`);
|
|
|
|
ws.onopen = () => {
|
|
// setWebsocketStatus('<?php echo __('websocket_connected'); ?>', '#0ecb81', 2000); // Hidden
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
const { stream, data } = JSON.parse(event.data);
|
|
if (!data) return;
|
|
|
|
const type = stream.split('@')[1];
|
|
|
|
if (type === 'ticker') {
|
|
marketData[data.s] = data;
|
|
if (data.s === currentPair) {
|
|
currentPrice = parseFloat(data.c);
|
|
updatePriceUI(data);
|
|
}
|
|
if (dom.pairsList.offsetParent) renderPairs();
|
|
} else if (type.startsWith('depth')) {
|
|
renderOrderBook(data.bids, data.asks);
|
|
} else if (type === 'trade') {
|
|
renderTrades(data);
|
|
}
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error('WebSocket Error:', error);
|
|
// setWebsocketStatus('<?php echo __('websocket_error'); ?>', '#f6465d'); // Hidden
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
// setWebsocketStatus('<?php echo __('websocket_disconnected'); ?>', '#f6465d', 5000); // Hidden
|
|
setTimeout(connectWebSocket, 5000);
|
|
};
|
|
}
|
|
|
|
function setWebsocketStatus(message, color, hideAfter = 0) {
|
|
// This function is kept for logic continuity but does nothing visually.
|
|
return;
|
|
}
|
|
|
|
// --- UI Rendering ---
|
|
function updatePriceUI(d) {
|
|
if (!d) return;
|
|
const color = d.P >= 0 ? '#0ecb81' : '#f6465d';
|
|
const priceStr = parseFloat(d.c).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
|
|
|
document.title = `${priceStr} | ${d.s.replace('USDT', '/USDT')}`;
|
|
dom.currPrice.innerText = priceStr;
|
|
dom.currPrice.style.color = color;
|
|
dom.currChange.innerText = `${d.P >= 0 ? '+' : ''}${parseFloat(d.P).toFixed(2)}%`;
|
|
dom.currChange.style.color = color;
|
|
dom.hHigh.innerText = parseFloat(d.h).toLocaleString('en-US', {minimumFractionDigits: 2});
|
|
dom.hLow.innerText = parseFloat(d.l).toLocaleString('en-US', {minimumFractionDigits: 2});
|
|
dom.hVol.innerText = `${(parseFloat(d.v) / 1000).toFixed(1)}K`;
|
|
dom.midPrice.innerText = priceStr;
|
|
dom.midPrice.style.color = color;
|
|
}
|
|
|
|
function renderPairs() {
|
|
const query = dom.pairSearch.value.toUpperCase();
|
|
const filteredPairs = pairs.filter(p => p.includes(query));
|
|
|
|
dom.pairsList.innerHTML = filteredPairs.map(p => {
|
|
const d = marketData[p] || {};
|
|
const icon = `https://assets.coincap.io/assets/icons/${p.replace('USDT','').toLowerCase()}@2x.png`;
|
|
const price = d.c ? parseFloat(d.c).toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2}) : '--';
|
|
const change = d.P ? `${parseFloat(d.P) >= 0 ? '+' : ''}${parseFloat(d.P).toFixed(2)}%` : '--';
|
|
const color = (d.P && parseFloat(d.P) >= 0) ? '#0ecb81' : '#f6465d';
|
|
|
|
return `
|
|
<div class="pair-item ${p === currentPair ? 'active' : ''}" data-pair="${p}">
|
|
<img src="${icon}" class="coin-icon" onerror="this.style.display='none'">
|
|
<div style="flex:1"><div style="font-weight:600; font-size:13px">${p.replace('USDT', '/USDT')}</div></div>
|
|
<div style="text-align:right">
|
|
<div style="font-size:13px; font-weight:600">${price}</div>
|
|
<div style="font-size:11px; color:${color}">${change}</div>
|
|
</div>
|
|
</div>`;
|
|
}).join('');
|
|
}
|
|
|
|
function renderOrderBook(bids, asks) {
|
|
const renderList = (element, data, isAsks) => {
|
|
if (!element) return;
|
|
const maxRows = Math.floor(element.offsetHeight / 20);
|
|
if (maxRows <= 0) return;
|
|
|
|
const relevantData = isAsks ? data.slice(0, maxRows).reverse() : data.slice(0, maxRows);
|
|
const maxVol = Math.max(...relevantData.map(([, q]) => parseFloat(q))) || 1;
|
|
|
|
element.innerHTML = relevantData.map(([price, quantity]) => {
|
|
const p = parseFloat(price), q = parseFloat(quantity);
|
|
const barWidth = (q / maxVol) * 100;
|
|
const color = isAsks ? '#f6465d' : '#0ecb81';
|
|
const bgColor = isAsks ? 'rgba(246, 70, 93, 0.2)' : 'rgba(14, 203, 129, 0.2)';
|
|
return `
|
|
<div class="ob-row" onclick="setPrice(${p})">
|
|
<div class="ob-bar" style="width:${barWidth}%; background-color:${bgColor};"></div>
|
|
<span style="color:${color}; z-index:1;">${p.toFixed(2)}</span>
|
|
<span style="z-index:1;">${q.toFixed(4)}</span>
|
|
<span style="z-index:1;">${(p * q).toFixed(2)}</span>
|
|
</div>`;
|
|
}).join('');
|
|
};
|
|
renderList(dom.asksList, asks, true);
|
|
renderList(dom.bidsList, bids, false);
|
|
}
|
|
|
|
function renderTrades(trade) {
|
|
if (!dom.tradesContent) return;
|
|
const color = trade.m ? '#f6465d' : '#0ecb81'; // m: is maker (seller is maker)
|
|
const time = new Date(trade.T).toLocaleTimeString('en-GB');
|
|
const row = `
|
|
<div class="trade-row">
|
|
<span style="color:${color}">${parseFloat(trade.p).toFixed(2)}</span>
|
|
<span>${parseFloat(trade.q).toFixed(4)}</span>
|
|
<span>${time}</span>
|
|
</div>`;
|
|
dom.tradesContent.insertAdjacentHTML('afterbegin', row);
|
|
if (dom.tradesContent.children.length > 50) {
|
|
dom.tradesContent.removeChild(dom.tradesContent.lastChild);
|
|
}
|
|
}
|
|
|
|
function switchPair(pair) {
|
|
currentPair = pair;
|
|
const coinName = pair.replace('USDT', '');
|
|
|
|
dom.currPair.innerText = `${coinName}/USDT`;
|
|
dom.currIcon.src = `https://assets.coincap.io/assets/icons/${coinName.toLowerCase()}@2x.png`;
|
|
dom.coinNameSpans.forEach(el => el.innerText = coinName);
|
|
|
|
updatePriceUI(marketData[pair]);
|
|
updateAvailableBalances();
|
|
initChartWidget(pair);
|
|
renderPairs();
|
|
|
|
// Clear old data and resubscribe
|
|
dom.asksList.innerHTML = '';
|
|
dom.bidsList.innerHTML = '';
|
|
dom.tradesContent.innerHTML = '';
|
|
connectWebSocket();
|
|
}
|
|
|
|
window.setPrice = (price) => {
|
|
dom.buyPriceInput.value = price;
|
|
dom.sellPriceInput.value = price;
|
|
}
|
|
|
|
// --- Event Listeners ---
|
|
dom.pairsList.addEventListener('click', (e) => {
|
|
const pairItem = e.target.closest('.pair-item');
|
|
if (pairItem && pairItem.dataset.pair) {
|
|
switchPair(pairItem.dataset.pair);
|
|
}
|
|
});
|
|
|
|
dom.pairSearch.addEventListener('input', renderPairs);
|
|
|
|
document.querySelectorAll('.right-col-tab').forEach(tab => {
|
|
tab.addEventListener('click', () => {
|
|
document.querySelector('.right-col-tab.active').classList.remove('active');
|
|
tab.classList.add('active');
|
|
const isOb = tab.dataset.tab === 'ob';
|
|
document.getElementById('ob-panel').style.display = isOb ? 'flex' : 'none';
|
|
document.getElementById('trades-panel').style.display = isOb ? 'none' : 'flex';
|
|
});
|
|
});
|
|
|
|
// --- Initial Load ---
|
|
switchPair('BTCUSDT');
|
|
});
|
|
</script>
|
|
<!-- This script is for the TradingView widget -->
|
|
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
|
<script type="text/javascript" src="https://s3.tradingview.com/external-embedding/embed-widget-advanced-chart.js"></script>
|
|
|
|
|
|
<?php include 'footer.php'; ?>
|