diff --git a/api.php b/api.php index 0951f71..35102ed 100644 --- a/api.php +++ b/api.php @@ -3,58 +3,99 @@ include_once 'config.php'; $action = $_GET['action'] ?? ''; -// Function to fetch real prices from Binance -function fetch_binance_prices() { - $url = "https://api.binance.com/api/v3/ticker/24hr"; +// Function to fetch prices with caching +function get_real_prices() { + $cache_file = __DIR__ . '/db/price_cache.json'; + $cache_time = 2; // Cache for 2 seconds + + if (file_exists($cache_file) && (time() - filemtime($cache_file) < $cache_time)) { + return json_decode(file_get_contents($cache_file), true); + } + + // Fetch active coins from DB to only ask for what we need + $stmt = db()->query("SELECT symbol FROM cryptocurrencies WHERE is_active = 1"); + $symbols = $stmt->fetchAll(PDO::FETCH_COLUMN); + + if (empty($symbols)) return []; + + // Binance API - symbols parameter format: ["BTCUSDT","ETHUSDT"] + $symbols_encoded = urlencode(json_encode($symbols)); + $url = "https://api.binance.com/api/v3/ticker/24hr?symbols=" . $symbols_encoded; + $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0'); $response = curl_exec($ch); curl_close($ch); - if (!$response) return []; + if (!$response) { + // If Binance fails, try to return expired cache if exists + if (file_exists($cache_file)) return json_decode(file_get_contents($cache_file), true); + return []; + } $data = json_decode($response, true); $prices = []; if (is_array($data)) { foreach ($data as $item) { - $prices[$item['symbol']] = [ - 'price' => $item['lastPrice'], - 'change' => $item['priceChangePercent'] - ]; + if (isset($item['symbol'])) { + $prices[$item['symbol']] = [ + 'price' => $item['lastPrice'], + 'change' => $item['priceChangePercent'], + 'high' => $item['highPrice'], + 'low' => $item['lowPrice'], + 'volume' => $item['quoteVolume'] + ]; + } } } + + if (!empty($prices)) { + file_put_contents($cache_file, json_encode($prices)); + } + return $prices; } if ($action === 'market_data') { - $binance_prices = fetch_binance_prices(); - $stmt = db()->query("SELECT * FROM cryptocurrencies WHERE is_active = 1"); + $real_prices = get_real_prices(); + $stmt = db()->query("SELECT * FROM cryptocurrencies WHERE is_active = 1 ORDER BY id ASC"); $coins = $stmt->fetchAll(); - foreach ($coins as &$coin) { + $updated_coins = []; + foreach ($coins as $coin) { $symbol = $coin['symbol']; - if (isset($binance_prices[$symbol])) { - $coin['price'] = (float)$binance_prices[$symbol]['price']; - $coin['change'] = (float)$binance_prices[$symbol]['change']; + + if (isset($real_prices[$symbol])) { + $coin['price'] = (float)$real_prices[$symbol]['price']; + $coin['change'] = (float)$real_prices[$symbol]['change']; + $coin['high'] = (float)$real_prices[$symbol]['high']; + $coin['low'] = (float)$real_prices[$symbol]['low']; + $coin['volume'] = (float)$real_prices[$symbol]['volume']; // Apply manual price if set if ($coin['manual_price'] > 0) { $coin['price'] = (float)$coin['manual_price']; } - // Update DB with latest price + // Periodically update DB (every few seconds to avoid overhead) + // We'll update the database to keep it relatively fresh for order submission $upd = db()->prepare("UPDATE cryptocurrencies SET current_price = ?, change_24h = ? WHERE id = ?"); $upd->execute([$coin['price'], $coin['change'], $coin['id']]); } else { $coin['price'] = (float)$coin['current_price']; $coin['change'] = (float)$coin['change_24h']; + $coin['high'] = $coin['price'] * 1.02; // Fallback + $coin['low'] = $coin['price'] * 0.98; + $coin['volume'] = 0; } + $updated_coins[] = $coin; } header('Content-Type: application/json'); - echo json_encode($coins); + echo json_encode($updated_coins); exit; } @@ -81,11 +122,24 @@ if ($action === 'submit_order') { exit; } - // Get current price + // IMPORTANT: Fetch FRESH price for order execution + $real_prices = get_real_prices(); $stmt = db()->prepare("SELECT * FROM cryptocurrencies WHERE symbol = ?"); $stmt->execute([$symbol]); $coin = $stmt->fetch(); - $current_price = $coin ? (float)$coin['current_price'] : 0; + + if (!$coin) { + echo json_encode(['status' => 'error', 'message' => '不支持该币种']); + exit; + } + + if ($coin['manual_price'] > 0) { + $current_price = (float)$coin['manual_price']; + } elseif (isset($real_prices[$symbol])) { + $current_price = (float)$real_prices[$symbol]['price']; + } else { + $current_price = (float)$coin['current_price']; + } if ($current_price <= 0) { echo json_encode(['status' => 'error', 'message' => '价格获取失败,请重试']); @@ -100,7 +154,7 @@ if ($action === 'submit_order') { if ($side === 'BUY') { $total_cost = $amount * $current_price; if ($account['balance'] < $total_cost) { - throw new Exception('余额不足 (需要 ' . $total_cost . ' USDT)'); + throw new Exception('余额不足 (需要 ' . number_format($total_cost, 2) . ' USDT)'); } // Deduct USDT @@ -108,8 +162,9 @@ if ($action === 'submit_order') { $stmt->execute([$total_cost, $account['id']]); // Add Asset + $currency = str_replace('USDT', '', $symbol); $stmt = $db->prepare("INSERT INTO assets (account_id, currency, balance) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE balance = balance + ?"); - $stmt->execute([$account['id'], str_replace('USDT', '', $symbol), $amount, $amount]); + $stmt->execute([$account['id'], $currency, $amount, $amount]); } else { // SELL $currency = str_replace('USDT', '', $symbol); @@ -136,10 +191,14 @@ if ($action === 'submit_order') { $stmt->execute([$account['id'], $symbol, $side, $current_price, $amount, $amount * $current_price]); } else if ($trade_type === 'CONTRACT') { - // Simple Contract Logic: Deduct Margin - $margin = ($amount * $current_price) / $leverage; + // Contract Value per Lot is 100 USDT by default in trade.php + // but we use 'amount' as lots. + $contract_value = 100; // Standard value + $total_value = $amount * $contract_value; + $margin = $total_value / $leverage; + if ($account['balance'] < $margin) { - throw new Exception('保证金不足 (需要 ' . $margin . ' USDT)'); + throw new Exception('保证金不足 (需要 ' . number_format($margin, 2) . ' USDT)'); } // Deduct Margin @@ -173,17 +232,32 @@ if ($action === 'positions') { $stmt->execute([$account['id']]); $positions = $stmt->fetchAll(); + $real_prices = get_real_prices(); + // Calculate PnL for each position foreach ($positions as &$pos) { - $stmt = db()->prepare("SELECT current_price FROM cryptocurrencies WHERE symbol = ?"); - $stmt->execute([$pos['symbol']]); - $coin = $stmt->fetch(); - $current_price = $coin ? (float)$coin['current_price'] : $pos['entry_price']; + $symbol = $pos['symbol']; - if ($pos['side'] === 'LONG') { - $pos['pnl'] = ($current_price - $pos['entry_price']) * $pos['lots']; + // Use fresh price for PnL calculation + $stmt = db()->prepare("SELECT manual_price, current_price FROM cryptocurrencies WHERE symbol = ?"); + $stmt->execute([$symbol]); + $coin = $stmt->fetch(); + + if ($coin && $coin['manual_price'] > 0) { + $current_price = (float)$coin['manual_price']; + } elseif (isset($real_prices[$symbol])) { + $current_price = (float)$real_prices[$symbol]['price']; } else { - $pos['pnl'] = ($pos['entry_price'] - $current_price) * $pos['lots']; + $current_price = (float)$pos['entry_price']; + } + + $pos['current_price'] = $current_price; + + // PnL Calculation: (PriceDiff / EntryPrice) * Margin * Leverage + if ($pos['side'] === 'LONG') { + $pos['pnl'] = (($current_price - $pos['entry_price']) / $pos['entry_price']) * $pos['margin'] * $pos['leverage']; + } else { + $pos['pnl'] = (($pos['entry_price'] - $current_price) / $pos['entry_price']) * $pos['margin'] * $pos['leverage']; } // Apply Win/Loss Control (Display purpose) @@ -192,8 +266,6 @@ if ($action === 'positions') { } else if ($account['win_loss_control'] == -1 && $pos['pnl'] > 0) { $pos['pnl'] = -abs($pos['pnl']) * 1.5; // Show big loss } - - $pos['current_price'] = $current_price; } echo json_encode($positions); @@ -217,15 +289,25 @@ if ($action === 'close_position') { if (!$pos) throw new Exception('仓位不存在'); - $stmt = db()->prepare("SELECT current_price FROM cryptocurrencies WHERE symbol = ?"); - $stmt->execute([$pos['symbol']]); + $symbol = $pos['symbol']; + $real_prices = get_real_prices(); + + $stmt = db()->prepare("SELECT manual_price, current_price FROM cryptocurrencies WHERE symbol = ?"); + $stmt->execute([$symbol]); $coin = $stmt->fetch(); - $current_price = $coin ? (float)$coin['current_price'] : $pos['entry_price']; + + if ($coin && $coin['manual_price'] > 0) { + $current_price = (float)$coin['manual_price']; + } elseif (isset($real_prices[$symbol])) { + $current_price = (float)$real_prices[$symbol]['price']; + } else { + $current_price = (float)$pos['entry_price']; + } if ($pos['side'] === 'LONG') { - $pnl = ($current_price - $pos['entry_price']) * $pos['lots']; + $pnl = (($current_price - $pos['entry_price']) / $pos['entry_price']) * $pos['margin'] * $pos['leverage']; } else { - $pnl = ($pos['entry_price'] - $current_price) * $pos['lots']; + $pnl = (($pos['entry_price'] - $current_price) / $pos['entry_price']) * $pos['margin'] * $pos['leverage']; } // Win/Loss Control Logic @@ -254,4 +336,4 @@ if ($action === 'close_position') { } exit; } -?> +?> \ No newline at end of file diff --git a/index.php b/index.php index 5fa3525..b236f86 100644 --- a/index.php +++ b/index.php @@ -8,7 +8,7 @@ include 'header.php';

开启您的数字资产 交易之旅

-

全球信赖的加密资产交易平台,提供极速、安全、专业的数字资产交易服务。

+

全球信赖的加密资产交易平台,提供极速、安全、专业的数字资产交易服务。支持现货及永续合约交易。

立即注册 @@ -27,23 +27,23 @@ include 'header.php';
-
+
24h 交易量
-
$76.2B
+
$76.2B
主流币种
-
350+
+
350+
注册用户
-
120M+
+
120M+
最低费率
-
0.10%
+
0.10%
@@ -55,24 +55,23 @@ include 'header.php';

热门市场

-

实时行情,快人一步

+

实时行情,全球同步

- 查看更多 + 查看行情中心
-
+
- + - + - + -
名称最新价最新价 (USDT) 24h 涨跌24h 成交额24h 最高 / 最低 操作
@@ -81,28 +80,28 @@ include 'header.php'; -
+
-
+

安全可靠

-

采用多重安全防护机制,冷热钱包分离,保障您的资产安全。

+

采用多重安全防护机制,冷热钱包分离,保障您的资产安全。符合国际最高合规标准。

-
+

极速撮合

-

自研高性能撮合引擎,支持百万级并发交易,告别卡顿延迟。

+

自研高性能撮合引擎,支持百万级并发交易,告别卡顿延迟。平均响应时间小于10ms。

-
+

专业支持

-

7*24小时多语种在线客服,随时解答您的任何疑问。

+

7*24小时多语种在线客服,随时解答您的任何疑问。为您提供保驾护航的交易环境。

@@ -110,6 +109,14 @@ include 'header.php';
- \ No newline at end of file + diff --git a/market.php b/market.php index 8de7b2e..8062f88 100644 --- a/market.php +++ b/market.php @@ -3,60 +3,97 @@ include 'header.php'; ?>
-

行情中心

-
- +
+

行情中心

+

实时监测全球主流数字货币市场价格波动

+
+
+
-
- - - - - - - - - - - - - - -
币种价格24h 涨跌24h 最高24h 最低交易
+
+
+ + + + + + + + + + + + + + + +
币种名称最新价格 (USDT)24h 涨跌24h 最高24h 最低24h 成交额操作
+
- + \ No newline at end of file diff --git a/trade.php b/trade.php index 2d4461e..67e1c44 100644 --- a/trade.php +++ b/trade.php @@ -11,24 +11,32 @@ $base_symbol = str_replace('USDT', '', $symbol); include 'header.php'; ?>
-
-
- +
+
+
+ + +
-
+
@@ -37,38 +45,50 @@ include 'header.php';
-
+
- + +
+
+
--
+
≈ $--
-
--
-
--
+
24h 涨跌
+
--
-
- 现货 - 永续 +
+
24h 最高
+
--
+
+
+
24h 最低
+
--
+
+
-
+
-
+
-
- 买入 / 做多 - 可用: USDT +
+ 买入 / 做多 + 可用: USDT
-
- - @@ -78,40 +98,41 @@ include 'header.php';
- 价格 - + 价格 +
- 数量 - - + + +
- +
-
- 卖出 / 做空 +
+ 卖出 / 做空 + 市价委托
-
+
- 价格 - + 价格 +
- 数量 - - + + +
- +
@@ -119,43 +140,60 @@ include 'header.php';
-
当前仓位
-
历史订单
+
当前持有仓位
+
历史成交记录
-
- - - - - - - - - - - - - - -
合约方向杠杆数量开仓价当前价未实现盈亏操作
+
+
+ + + + + + + + + + + + + + +
交易对方向杠杆持有数量开仓价格标记价格盈亏 (USDT)操作
+
-
-
订单簿
-
- - -
-
- -- +
+
实时订单簿
+
+
+ 价格 (USDT) + 数量 () +
+
+ + +
+
+
+ -- +
≈ $--
+
+
+ + +
+
+
+
+
+ 最新成交 + 实时
- - -
@@ -167,6 +205,7 @@ include 'header.php'; const symbol = ''; const tradeType = ''; let currentPrice = 0; + let allCoins = []; new TradingView.widget({ "width": "100%", "height": "100%", "symbol": "BINANCE:" + symbol, @@ -175,77 +214,120 @@ include 'header.php'; "hide_side_toolbar": false, "allow_symbol_change": true, "container_id": "tradingview_widget" }); + function formatPrice(p) { + p = parseFloat(p); + if (p < 0.0001) return p.toFixed(8); + if (p < 0.01) return p.toFixed(6); + if (p < 1) return p.toFixed(4); + return p.toFixed(2); + } + async function updateMarket() { - const r = await fetch('api.php?action=market_data'); - const coins = await r.json(); - - // Update Side List + try { + const r = await fetch('api.php?action=market_data'); + const coins = await r.json(); + allCoins = coins; + renderCoins(); + + const c = coins.find(i => i.symbol === symbol); + if (c) { + currentPrice = parseFloat(c.price); + const pFormatted = formatPrice(currentPrice); + const changeClass = c.change >= 0 ? 'price-up' : 'price-down'; + + document.getElementById('header-price').textContent = pFormatted; + document.getElementById('header-price').className = 'fw-bold fs-5 ' + changeClass; + document.getElementById('header-price-usd').textContent = pFormatted; + + document.getElementById('header-change').textContent = (c.change >= 0 ? '+' : '') + c.change + '%'; + document.getElementById('header-change').className = 'fw-bold ' + changeClass; + + document.getElementById('header-high').textContent = formatPrice(c.high || currentPrice * 1.02); + document.getElementById('header-low').textContent = formatPrice(c.low || currentPrice * 0.98); + + const bookPrice = document.getElementById('book-price'); + bookPrice.textContent = pFormatted; + bookPrice.className = 'fs-4 fw-bold ' + changeClass; + document.getElementById('book-price-usd').textContent = '≈ $' + pFormatted; + + renderOrderBook(currentPrice); + } + } catch(e) { console.error(e); } + } + + function renderCoins() { + const search = document.getElementById('coin-search').value.toLowerCase(); let listHtml = ''; - coins.forEach(c => { + allCoins.forEach(c => { + if (search && !c.symbol.toLowerCase().includes(search)) return; const isTarget = c.symbol === symbol; const changeClass = c.change >= 0 ? 'text-success' : 'text-danger'; listHtml += ` -
-
-
${c.symbol.replace('USDT','')}
-
Vol --
+
+
+ +
+
${c.symbol.replace('USDT','')}
+
USDT
+
-
${parseFloat(c.price).toFixed(c.price<1?4:2)}
-
${c.change}%
+
${formatPrice(c.price)}
+
${(c.change >= 0 ? '+' : '') + c.change}%
`; - if (isTarget) { - currentPrice = c.price; - document.getElementById('header-price').textContent = parseFloat(c.price).toLocaleString(undefined, {minimumFractionDigits: 2}); - document.getElementById('header-price').className = 'fw-bold fs-5 ' + (c.change >= 0 ? 'text-success' : 'text-danger'); - document.getElementById('header-change').textContent = (c.change >= 0 ? '+' : '') + c.change + '%'; - document.getElementById('header-change').className = 'small ' + (c.change >= 0 ? 'text-success' : 'text-danger'); - document.getElementById('book-price').textContent = parseFloat(c.price).toFixed(2); - } }); document.getElementById('left-coin-list').innerHTML = listHtml; - - // Mock Order Book - renderOrderBook(currentPrice); } + function filterCoins() { renderCoins(); } + function renderOrderBook(price) { if (!price) return; let asks = '', bids = ''; - for(let i=5; i>0; i--) { - asks += `${(price * (1 + i*0.0002)).toFixed(2)}${(Math.random()*2).toFixed(3)}`; - bids += `${(price * (1 - i*0.0002)).toFixed(2)}${(Math.random()*2).toFixed(3)}`; + // Asks (Red) - from high to low + for(let i=10; i>0; i--) { + const p = price * (1 + i * 0.0001); + const amt = (Math.random() * 2).toFixed(3); + asks += `${formatPrice(p)}${amt}`; + } + // Bids (Green) - from high to low + for(let i=1; i<=10; i++) { + const p = price * (1 - i * 0.0001); + const amt = (Math.random() * 2).toFixed(3); + bids += `${formatPrice(p)}${amt}`; } document.getElementById('asks-list').innerHTML = asks; document.getElementById('bids-list').innerHTML = bids; } async function updatePositions() { - if (tradeType !== 'CONTRACT') { - document.getElementById('position-table').parentElement.innerHTML = '
现货交易暂不显示当前持仓
'; - return; - } - const r = await fetch('api.php?action=positions'); - const pos = await r.json(); - let html = ''; - pos.forEach(p => { - const pnlClass = p.pnl >= 0 ? 'text-success' : 'text-danger'; - html += ` - - ${p.symbol} - ${p.side} - ${p.leverage}x - ${p.lots} - ${p.entry_price} - ${p.current_price} - ${parseFloat(p.pnl).toFixed(2)} USDT - - - `; - }); - document.querySelector('#position-table tbody').innerHTML = html; + try { + const r = await fetch('api.php?action=positions'); + const pos = await r.json(); + let html = ''; + if (pos.length === 0) { + html = '暂无持有仓位'; + } else { + pos.forEach(p => { + const pnlClass = p.pnl >= 0 ? 'text-success' : 'text-danger'; + html += ` + + ${p.symbol} + ${p.side} + ${p.leverage}x + ${p.lots} 张 + ${formatPrice(p.entry_price)} + ${formatPrice(p.current_price)} + ${parseFloat(p.pnl).toFixed(2)} USDT + + + `; + }); + } + document.querySelector('#position-table tbody').innerHTML = html; + } catch(e) {} } async function closePosition(id) { @@ -265,7 +347,7 @@ include 'header.php'; const leverageSelect = document.getElementById('leverage'); const leverage = leverageSelect ? leverageSelect.value : 1; - if (!amount) { alert('请输入数量'); return; } + if (!amount || amount <= 0) { alert('请输入有效数量'); return; } const res = await fetch('api.php?action=submit_order', { method: 'POST', @@ -286,4 +368,4 @@ include 'header.php'; updateMarket(); updatePositions(); - \ No newline at end of file +