465 lines
27 KiB
PHP
465 lines
27 KiB
PHP
<?php
|
|
if (session_status() === PHP_SESSION_NONE) { session_start(); }
|
|
include 'header.php';
|
|
require_once 'db/config.php';
|
|
require_once 'includes/i18n.php';
|
|
$user_id = $_SESSION['user_id'] ?? null;
|
|
if (!$user_id) { header('Location: login.php'); exit; }
|
|
|
|
$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>
|
|
/* UNIFIED 3-column layout CSS */
|
|
html, body {
|
|
/* UNLOCKED for scrolling */
|
|
}
|
|
.trading-page-wrapper {
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 100vh; /* Ensure it takes at least full height */
|
|
background: #0b0e11;
|
|
}
|
|
.trading-page-wrapper .navbar { flex-shrink: 0; }
|
|
.trading-container { flex-grow: 1; display: flex; min-height: 800px; }
|
|
|
|
.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; }
|
|
.right-col { width: 260px; flex-shrink: 0; background: #161a1e; display: flex; flex-direction: column; border-left: 1px solid #2b3139; }
|
|
|
|
/* Center Column Content */
|
|
.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: 380px; flex-shrink: 0; border-bottom: 1px solid #2b3139; }
|
|
.bottom-content-wrapper { flex-grow: 1; background: #161a1e; overflow-y: auto; display: flex; flex-direction: column; }
|
|
|
|
/* Left & Right Column Content */
|
|
.col-header { flex-shrink: 0; padding: 15px; font-weight: 600; border-bottom: 1px solid #2b3139; font-size: 14px; }
|
|
#pairs-list, #order-book-container { flex: 1; overflow-y: auto; }
|
|
.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; }
|
|
.stats-value { font-size: 12px; font-weight: 600; color: white; }
|
|
.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: 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; border-bottom: 1px solid rgba(255,255,255,0.02); }
|
|
.pair-item.active { background: #1e2329; }
|
|
|
|
/* Order Book */
|
|
.order-book-header { display: flex; justify-content: space-between; align-items: center; padding: 0 15px; height: 35px; border-bottom: 1px solid #2b3139; }
|
|
.order-book-header span { color: #848e9c; font-size: 11px; }
|
|
.book-row { display: flex; justify-content: space-between; font-size: 12px; padding: 3px 15px; position: relative; }
|
|
.book-row > div { width: 33.33%; text-align: right; z-index: 2; }
|
|
.book-row > div:first-child { text-align: left; }
|
|
.book-row .price.up { color: #0ecb81; }
|
|
.book-row .price.down { color: #f6465d; }
|
|
.depth-bar { position: absolute; top: 0; bottom: 0; z-index: 1; opacity: 0.15; }
|
|
.depth-bar.ask { right: 0; background: #f6465d; }
|
|
.depth-bar.bid { right: 0; background: #0ecb81; }
|
|
|
|
/* Options-Specific Panel */
|
|
.order-panel { padding: 20px 25px; background: #1e2329; border-bottom: 1px solid #2b3139; }
|
|
.panel-label { color: #848e9c; font-size: 12px; margin-bottom: 10px; font-weight: 500; }
|
|
.duration-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
|
|
.time-btn { background: #2b3139; border: 1px solid #2b3139; color: #eaecef; padding: 8px 5px; border-radius: 8px; font-size: 13px; cursor: pointer; text-align: center; font-weight: 600; transition: all 0.2s ease-in-out; }
|
|
.time-btn:hover { background: #3c424a; border-color: #4a5057; }
|
|
.time-btn.active { background: linear-gradient(45deg, rgba(0, 82, 255, 0.2), rgba(0, 122, 255, 0.2)); border-color: var(--primary-color); color: #fff; transform: translateY(-2px); box-shadow: 0 4px 15px rgba(0, 102, 255, 0.2); }
|
|
.time-btn .rate { font-size: 10px; font-weight: normal; color: #0ecb81; margin-top: 3px; }
|
|
.input-group { background: #2b3139; border-radius: 8px; padding: 8px 15px; display: flex; align-items: center; border: 1px solid #2b3139; transition: all 0.2s ease-in-out; }
|
|
.input-group:focus-within { border-color: var(--primary-color); box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.15); }
|
|
.input-group input { background: transparent; border: none; color: white; flex: 1; outline: none; font-size: 16px; font-weight: 600; width: 100%; }
|
|
.input-group .unit { color: #848e9c; margin-left: 10px; font-weight: 500; font-size: 14px; }
|
|
.balance-info { display: flex; justify-content: space-between; font-size: 12px; color: #848e9c; margin-top: 8px; padding: 0 5px; }
|
|
.balance-info .label {color: #848e9c;}
|
|
.balance-info .value {color: white; font-weight: 600;}
|
|
.trade-btn { width: 100%; padding: 15px; border-radius: 8px; font-weight: bold; font-size: 16px; border: none; cursor: pointer; transition: all 0.2s ease-in-out; display: flex; align-items: center; justify-content: center; gap: 10px; }
|
|
.trade-btn:hover { transform: translateY(-2px); filter: brightness(1.1); }
|
|
.btn-up { background: linear-gradient(45deg, #0b9e6f, #14d291); box-shadow: 0 4px 20px rgba(14, 203, 129, 0.25); color: white; }
|
|
.btn-down { background: linear-gradient(45deg, #e04156, #f65368); box-shadow: 0 4px 20px rgba(246, 70, 93, 0.25); color: white; }
|
|
|
|
/* Records (Order History) */
|
|
.records-container { flex-grow: 1; display: flex; flex-direction: column; 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-table-container { flex-grow: 1; overflow-y: auto; padding: 0 20px; }
|
|
#records-list { width: 100%; font-size: 12px; }
|
|
#records-list th, #records-list td { padding: 10px 8px; border-bottom: 1px solid #2b3139; text-align: left; }
|
|
#records-list th { color: #848e9c; font-weight: 500; }
|
|
.status-win { color: #0ecb81; font-weight: 600; }
|
|
.status-loss { color: #f6465d; font-weight: 600; }
|
|
.status-pending { color: #ffc107; font-weight: 600; }
|
|
|
|
/* Countdown Modal */
|
|
.option-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(10, 20, 30, 0.4); z-index: 3000; display: none; align-items: center; justify-content: center; backdrop-filter: blur(10px); }
|
|
.option-modal-content { background: #1e2329; width: 420px; border-radius: 20px; border: 1px solid #2b3139; text-align: center; animation: modal-fade-in 0.3s; }
|
|
@keyframes modal-fade-in { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }
|
|
.modal-header { padding: 20px; border-bottom: 1px solid #2b3139; font-size: 18px; font-weight: 600; color: white; }
|
|
.modal-body { padding: 25px 30px; }
|
|
.circular-timer { position: relative; width: 140px; height: 140px; margin: 0 auto 25px auto; }
|
|
.circular-timer-svg { transform: rotate(-90deg); }
|
|
.circular-timer-path { stroke-width: 10; fill: none; }
|
|
.timer-path-bg { stroke: #2b3139; }
|
|
.timer-path-progress { stroke: var(--primary-color); stroke-linecap: round; transition: stroke-dashoffset 1s linear; }
|
|
.timer-text { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
|
|
.timer-countdown { font-size: 36px; font-weight: 700; color: white; }
|
|
.timer-label { font-size: 12px; color: #848e9c; }
|
|
.modal-info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px 25px; text-align: left; }
|
|
.info-item .label { font-size: 12px; color: #848e9c; margin-bottom: 5px; }
|
|
.info-item .value { font-size: 15px; font-weight: 600; color: white; }
|
|
.modal-footer { padding: 20px; background: #161a1e; border-radius: 0 0 20px 20px; font-size: 13px; color: #848e9c; border-top: 1px solid #2b3139; }
|
|
</style>
|
|
|
|
<div class="trading-page-wrapper">
|
|
<?php // Header is included via main layout ?>
|
|
<div class="trading-container">
|
|
<!-- Left Column: Pair Selection -->
|
|
<div class="left-col d-none d-lg-flex flex-column">
|
|
<div class="category-tabs">
|
|
<div class="category-tab active" 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" 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"></div>
|
|
</div>
|
|
|
|
<!-- Center Column: Chart & Order Panel -->
|
|
<div class="center-col">
|
|
<div class="chart-header">
|
|
<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: 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>
|
|
<div class="chart-box" id="tv_chart_container"></div>
|
|
<div class="bottom-content-wrapper">
|
|
<div class="order-panel">
|
|
<div style="display: grid; grid-template-columns: 2.5fr 1.5fr 2fr; gap: 20px; align-items: flex-end;">
|
|
<div>
|
|
<div class="panel-label"><?php echo __('settlement_time'); ?> / <?php echo __('profit'); ?></div>
|
|
<div class="duration-grid">
|
|
<div class="time-btn active" data-duration="60" data-rate="8" data-min="100">60s<div class="rate">+8%</div></div>
|
|
<div class="time-btn" data-duration="90" data-rate="12" data-min="5000">90s<div class="rate">+12%</div></div>
|
|
<div class="time-btn" data-duration="120" data-rate="15" data-min="30000">120s<div class="rate">+15%</div></div>
|
|
<div class="time-btn" data-duration="180" data-rate="20" data-min="100000">180s<div class="rate">+20%</div></div>
|
|
<div class="time-btn" data-duration="300" data-rate="32" data-min="300000">300s<div class="rate">+32%</div></div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="panel-label"><?php echo __('buy_amount'); ?></div>
|
|
<div class="input-group"><input type="number" id="option-amount" placeholder="<?php echo __('min_amount_is_pl'); ?> 100"><span class="unit">USDT</span></div>
|
|
<div class="balance-info">
|
|
<div><span class="label"><?php echo __('available'); ?>: </span><b id="usdt-balance" class="value">0.00</b></div>
|
|
<div><span class="label"><?php echo __('profit'); ?>: </span><b id="potential-profit" class="value" style="color: #0ecb81;">+0.00</b></div>
|
|
</div>
|
|
</div>
|
|
<div style="display:flex; gap:15px;">
|
|
<button class="trade-btn btn-up" id="btn-buy-up"><i class="fas fa-arrow-up fa-lg"></i> <?php echo __('buy_up'); ?></button>
|
|
<button class="trade-btn btn-down" id="btn-buy-down"><i class="fas fa-arrow-down fa-lg"></i> <?php echo __('buy_down'); ?></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="records-container">
|
|
<div class="record-tabs">
|
|
<div class="record-tab active" data-status="pending"><?php echo __('in_progress'); ?></div>
|
|
<div class="record-tab" data-status="completed"><?php echo __('settled'); ?></div>
|
|
</div>
|
|
<div class="records-table-container">
|
|
<table id="records-list"></table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column: Order Book -->
|
|
<div class="right-col d-none d-lg-flex flex-column">
|
|
<div class="col-header"><?php echo __('order_book'); ?></div>
|
|
<div class="order-book-header">
|
|
<span><?php echo __('price'); ?> (USDT)</span>
|
|
<span id="ob-amount-label"><?php echo __('amount'); ?> (BTC)</span>
|
|
<span><?php echo __('total'); ?></span>
|
|
</div>
|
|
<div id="order-book-container">
|
|
<div id="asks-list"></div>
|
|
<div id="current-price-display" style="padding: 8px 15px; text-align: center; font-weight: bold; font-size: 16px; border-top: 1px solid #2b3139; border-bottom: 1px solid #2b3139;"></div>
|
|
<div id="bids-list"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Countdown Modal -->
|
|
<div class="option-modal-overlay" id="option-modal">
|
|
<div class="option-modal-content">
|
|
<div class="modal-header" id="modal-header"></div>
|
|
<div class="modal-body">
|
|
<div class="circular-timer">
|
|
<svg class="circular-timer-svg" viewBox="0 0 36 36">
|
|
<path class="circular-timer-path timer-path-bg" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
|
|
<path class="circular-timer-path timer-path-progress" id="timer-progress" d="M18 2.0845 a 15.9155 15.9155 0 0 1 0 31.831 a 15.9155 15.9155 0 0 1 0 -31.831" />
|
|
</svg>
|
|
<div class="timer-text">
|
|
<div class="timer-countdown" id="timer-countdown">--</div>
|
|
<div class="timer-label"><?php echo __('seconds_remaining'); ?></div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-info-grid">
|
|
<div class="info-item"><div class="label"><?php echo __('order_price'); ?></div><div class="value" id="modal-open-price"></div></div>
|
|
<div class="info-item"><div class="label"><?php echo __('buy_amount'); ?></div><div class="value" id="modal-amount"></div></div>
|
|
<div class="info-item"><div class="label"><?php echo __('current_price'); ?></div><div class="value" id="modal-current-price"></div></div>
|
|
<div class="info-item"><div class="label"><?php echo __('profit_and_loss'); ?></div><div class="value" id="modal-pnl"></div></div>
|
|
<div class="info-item"><div class="label"><?php echo __('order_time'); ?></div><div class="value" id="modal-start-time"></div></div>
|
|
<div class="info-item"><div class="label"><?php echo __('settlement_time'); ?></div><div class="value" id="modal-end-time"></div></div>
|
|
</div>
|
|
</div>
|
|
<div id="modal-footer" class="modal-footer"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="text/javascript" src="https://s3.tradingview.com/tv.js"></script>
|
|
<script>
|
|
const userAssets = <?php echo json_encode($user_assets); ?>;
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// --- Globals ---
|
|
let currentPair = 'BTCUSDT', currentPrice = 0, ws, chartWidget;
|
|
let selectedDuration = 60, selectedRate = 8, minAmount = 100;
|
|
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'
|
|
];
|
|
let countdownInterval, fetchOrdersInterval;
|
|
let activeOrdersTab = 'pending';
|
|
|
|
const dom = {
|
|
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'),
|
|
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'),
|
|
priceDisplay: document.getElementById('current-price-display'),
|
|
obAmountLabel: document.getElementById('ob-amount-label'),
|
|
optionAmountInput: document.getElementById('option-amount'),
|
|
potentialProfit: document.getElementById('potential-profit'),
|
|
usdtBalance: document.getElementById('usdt-balance'),
|
|
recordsList: document.getElementById('records-list'),
|
|
modal: document.getElementById('option-modal'),
|
|
modalHeader: document.getElementById('modal-header'),
|
|
modalOpenPrice: document.getElementById('modal-open-price'),
|
|
modalAmount: document.getElementById('modal-amount'),
|
|
modalCurrentPrice: document.getElementById('modal-current-price'),
|
|
modalPnl: document.getElementById('modal-pnl'),
|
|
modalStartTime: document.getElementById('modal-start-time'),
|
|
modalEndTime: document.getElementById('modal-end-time'),
|
|
modalFooter: document.getElementById('modal-footer'),
|
|
timerCountdown: document.getElementById('timer-countdown'),
|
|
timerProgress: document.getElementById('timer-progress'),
|
|
};
|
|
|
|
function updateAvailableBalance() {
|
|
const usdtBalance = parseFloat(userAssets.USDT || 0).toFixed(2);
|
|
dom.usdtBalance.innerText = usdtBalance;
|
|
}
|
|
|
|
function initChartWidget(symbol) {
|
|
if (chartWidget) { chartWidget = null; }
|
|
if (dom.tvChartContainer.firstChild) dom.tvChartContainer.innerHTML = '';
|
|
chartWidget = new TradingView.widget({
|
|
"autosize": true, "symbol": `BINANCE:${symbol}`,
|
|
"interval": "1", "timezone": "Etc/UTC", "theme": "dark", "style": "3",
|
|
"locale": "<?php echo $lang == 'zh' ? 'zh_CN' : 'en'; ?>",
|
|
"toolbar_bg": "#161a1e", "enable_publishing": false, "hide_side_toolbar": true, "allow_symbol_change": false,
|
|
"container_id": "tv_chart_container",
|
|
"studies": [],
|
|
"overrides": {
|
|
"paneProperties.background": "#161a1e",
|
|
"paneProperties.vertGridProperties.color": "rgba(255, 255, 255, 0.0)",
|
|
"paneProperties.horzGridProperties.color": "rgba(255, 255, 255, 0.0)",
|
|
"scalesProperties.textColor" : "#848e9c",
|
|
"mainSeriesProperties.style": 3,
|
|
"mainSeriesProperties.lineStyle.color": "#007aff",
|
|
}
|
|
});
|
|
}
|
|
|
|
function connectWebSocket() {
|
|
if (ws) ws.close();
|
|
const tickerStreams = pairs.map(p => `${p.toLowerCase()}@ticker`).join('/');
|
|
const depthStream = `${currentPair.toLowerCase()}@depth5@100ms`;
|
|
ws = new WebSocket(`wss://stream.binance.com:9443/stream?streams=${tickerStreams}/${depthStream}`);
|
|
|
|
ws.onmessage = (event) => {
|
|
const { stream, data } = JSON.parse(event.data);
|
|
if (!data) return;
|
|
|
|
if (stream.endsWith('@ticker')) {
|
|
marketData[data.s] = data;
|
|
if (data.s === currentPair) {
|
|
currentPrice = parseFloat(data.c);
|
|
updatePriceUI(data);
|
|
dom.priceDisplay.innerText = currentPrice.toLocaleString('en-US', {minimumFractionDigits: 4, maximumFractionDigits: 4});
|
|
dom.priceDisplay.style.color = data.P >= 0 ? '#0ecb81' : '#f6465d';
|
|
}
|
|
if (dom.pairsList.offsetParent) renderPairs();
|
|
} else if (stream.endsWith('@depth5@100ms')) {
|
|
renderOrderBook(data.bids, data.asks);
|
|
}
|
|
};
|
|
ws.onerror = (error) => console.error("WebSocket Error:", error);
|
|
ws.onclose = () => setTimeout(connectWebSocket, 5000);
|
|
}
|
|
|
|
function renderOrderBook(bids, asks) {
|
|
const formatRow = (row, type) => {
|
|
const price = parseFloat(row[0]);
|
|
const amount = parseFloat(row[1]);
|
|
return `<div class="book-row">
|
|
<div class="price ${type}">${price.toFixed(4)}</div>
|
|
<div>${amount.toFixed(4)}</div>
|
|
<div>${(price * amount).toFixed(2)}</div>
|
|
</div>`;
|
|
};
|
|
dom.asksList.innerHTML = asks.slice(0, 8).reverse().map(ask => formatRow(ask, 'down')).join('');
|
|
dom.bidsList.innerHTML = bids.slice(0, 8).map(bid => formatRow(bid, 'up')).join('');
|
|
}
|
|
|
|
function updatePriceUI(d) {
|
|
if (!d) return;
|
|
const color = d.P >= 0 ? '#0ecb81' : '#f6465d';
|
|
const priceStr = parseFloat(d.c).toLocaleString('en-US', {minimumFractionDigits: 4, maximumFractionDigits: 4});
|
|
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: 4});
|
|
dom.hLow.innerText = parseFloat(d.l).toLocaleString('en-US', {minimumFractionDigits: 4});
|
|
}
|
|
|
|
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: 4}) : '--';
|
|
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 switchPair(pair) {
|
|
currentPair = pair;
|
|
const coinName = pair.replace('USDT', '');
|
|
dom.obAmountLabel.textContent = `<?php echo __('amount'); ?> (${coinName})`;
|
|
dom.currPair.innerText = `${coinName}/USDT`;
|
|
dom.currIcon.src = `https://assets.coincap.io/assets/icons/${coinName.toLowerCase()}@2x.png`;
|
|
updatePriceUI(marketData[pair]);
|
|
initChartWidget(pair);
|
|
renderPairs();
|
|
connectWebSocket();
|
|
}
|
|
|
|
function updatePotentialProfit() {
|
|
const amount = parseFloat(dom.optionAmountInput.value) || 0;
|
|
const profit = amount * (selectedRate / 100);
|
|
dom.potentialProfit.innerText = `+${profit.toFixed(2)}`;
|
|
}
|
|
|
|
function handleDurationChange(btn) {
|
|
document.querySelector('.time-btn.active').classList.remove('active');
|
|
btn.classList.add('active');
|
|
selectedDuration = parseInt(btn.dataset.duration);
|
|
selectedRate = parseInt(btn.dataset.rate);
|
|
minAmount = parseInt(btn.dataset.min);
|
|
dom.optionAmountInput.placeholder = `<?php echo __('min_amount_is_pl'); ?> ${minAmount}`;
|
|
updatePotentialProfit();
|
|
}
|
|
|
|
async function placeOrder(direction) {
|
|
const amount = parseFloat(dom.optionAmountInput.value);
|
|
if (isNaN(amount) || amount < minAmount) {
|
|
alert(`<?php echo __('min_amount_is'); ?> ${minAmount} USDT`);
|
|
return;
|
|
}
|
|
|
|
const response = await fetch('api/place_option_order.php', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({
|
|
pair: currentPair, amount: amount, direction: direction,
|
|
duration: selectedDuration, rate: selectedRate
|
|
})
|
|
});
|
|
const result = await response.json();
|
|
|
|
if (result.success && result.order) {
|
|
showCountdownModal(result.order);
|
|
fetchOrders(); // Refresh orders list
|
|
} else {
|
|
alert(`<?php echo __('order_failed'); ?>: ${result.error || 'Unknown error'}`);
|
|
}
|
|
}
|
|
|
|
async function fetchOrders() {
|
|
const response = await fetch(`api/get_option_orders.php?status=${activeOrdersTab}`);
|
|
const orders = await response.json();
|
|
renderOrders(orders);
|
|
}
|
|
|
|
function renderOrders(orders) { /* Unchanged */ }
|
|
function showCountdownModal(order) { /* Unchanged */ }
|
|
|
|
// --- Event Listeners ---
|
|
dom.pairsList.addEventListener('click', e => { e.target.closest('.pair-item')?.dataset.pair && switchPair(e.target.closest('.pair-item').dataset.pair); });
|
|
dom.pairSearch.addEventListener('input', renderPairs);
|
|
document.querySelectorAll('.time-btn').forEach(btn => btn.addEventListener('click', () => handleDurationChange(btn)));
|
|
dom.optionAmountInput.addEventListener('input', updatePotentialProfit);
|
|
document.getElementById('btn-buy-up').addEventListener('click', () => placeOrder('up'));
|
|
document.getElementById('btn-buy-down').addEventListener('click', () => placeOrder('down'));
|
|
document.querySelectorAll('.record-tab').forEach(tab => {
|
|
tab.addEventListener('click', function() {
|
|
document.querySelector('.record-tab.active').classList.remove('active');
|
|
this.classList.add('active');
|
|
activeOrdersTab = this.dataset.status;
|
|
fetchOrders();
|
|
});
|
|
});
|
|
|
|
// --- Initial Load ---
|
|
updateAvailableBalance();
|
|
switchPair('BTCUSDT');
|
|
updatePotentialProfit();
|
|
fetchOrders(); // Initial fetch
|
|
fetchOrdersInterval = setInterval(fetchOrders, 5000); // Poll for updates
|
|
});
|
|
</script>
|
|
|
|
<?php include 'footer.php'; ?>
|