diff --git a/api.php b/api.php index 35102ed..63f73d6 100644 --- a/api.php +++ b/api.php @@ -3,57 +3,100 @@ include_once 'config.php'; $action = $_GET['action'] ?? ''; -// Function to fetch prices with caching +/** + * Fetch prices from Binance with caching and high precision + */ function get_real_prices() { $cache_file = __DIR__ . '/db/price_cache.json'; $cache_time = 2; // Cache for 2 seconds + // Check cache if (file_exists($cache_file) && (time() - filemtime($cache_file) < $cache_time)) { - return json_decode(file_get_contents($cache_file), true); + $cache_data = json_decode(file_get_contents($cache_file), true); + if (!empty($cache_data)) return $cache_data; } - // 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); + // Fetch active coins from DB + try { + $stmt = db()->query("SELECT symbol FROM cryptocurrencies WHERE is_active = 1"); + $symbols = $stmt->fetchAll(PDO::FETCH_COLUMN); + } catch (Exception $e) { + $symbols = ['BTCUSDT', 'ETHUSDT', 'BNBUSDT', 'SOLUSDT', 'DOGEUSDT']; + } - if (empty($symbols)) return []; + if (empty($symbols)) $symbols = ['BTCUSDT']; - // 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; + // Use Binance 24hr ticker for comprehensive data + $symbols_json = json_encode($symbols); + $url = "https://api.binance.com/api/v3/ticker/24hr?symbols=" . urlencode($symbols_json); $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'); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'); + // Disable SSL verification if needed for some environments, but prefer keeping it + // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + $response = curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); - 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 []; + $prices = []; + + if ($http_code == 200 && $response) { + $data = json_decode($response, true); + if (is_array($data)) { + foreach ($data as $item) { + if (isset($item['symbol'])) { + $prices[$item['symbol']] = [ + 'price' => $item['lastPrice'], + 'change' => $item['priceChangePercent'], + 'high' => $item['highPrice'], + 'low' => $item['lowPrice'], + 'volume' => $item['quoteVolume'], + 'ts' => time() + ]; + } + } + } } - $data = json_decode($response, true); - $prices = []; - if (is_array($data)) { - foreach ($data as $item) { - if (isset($item['symbol'])) { - $prices[$item['symbol']] = [ - 'price' => $item['lastPrice'], - 'change' => $item['priceChangePercent'], - 'high' => $item['highPrice'], - 'low' => $item['lowPrice'], - 'volume' => $item['quoteVolume'] - ]; + // Fallback: If 24hr fails, try simpler price-only endpoint + if (empty($prices)) { + $url_simple = "https://api.binance.com/api/v3/ticker/price"; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url_simple); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + $resp_simple = curl_exec($ch); + curl_close($ch); + + if ($resp_simple) { + $data_simple = json_decode($resp_simple, true); + if (is_array($data_simple)) { + foreach ($data_simple as $item) { + if (in_array($item['symbol'], $symbols)) { + $prices[$item['symbol']] = [ + 'price' => $item['price'], + 'change' => '0.00', + 'high' => $item['price'], + 'low' => $item['price'], + 'volume' => '0', + 'ts' => time() + ]; + } + } } } } if (!empty($prices)) { - file_put_contents($cache_file, json_encode($prices)); + // Only update file if we have new data + @file_put_contents($cache_file, json_encode($prices)); + } else if (file_exists($cache_file)) { + // Last resort: return expired cache + return json_decode(file_get_contents($cache_file), true); } return $prices; @@ -61,34 +104,36 @@ function get_real_prices() { if ($action === 'market_data') { $real_prices = get_real_prices(); - $stmt = db()->query("SELECT * FROM cryptocurrencies WHERE is_active = 1 ORDER BY id ASC"); - $coins = $stmt->fetchAll(); + try { + $stmt = db()->query("SELECT * FROM cryptocurrencies WHERE is_active = 1 ORDER BY id ASC"); + $coins = $stmt->fetchAll(); + } catch (Exception $e) { + $coins = []; + } $updated_coins = []; foreach ($coins as $coin) { $symbol = $coin['symbol']; if (isset($real_prices[$symbol])) { - $coin['price'] = (float)$real_prices[$symbol]['price']; + $coin['price'] = (string)$real_prices[$symbol]['price']; // Keep as string for precision $coin['change'] = (float)$real_prices[$symbol]['change']; - $coin['high'] = (float)$real_prices[$symbol]['high']; - $coin['low'] = (float)$real_prices[$symbol]['low']; + $coin['high'] = (string)$real_prices[$symbol]['high']; + $coin['low'] = (string)$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']; + $coin['price'] = (string)$coin['manual_price']; } - // Periodically update DB (every few seconds to avoid overhead) - // We'll update the database to keep it relatively fresh for order submission + // Sync to DB occasionally (logic can be improved, but this is current) $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['price'] = (string)$coin['current_price']; $coin['change'] = (float)$coin['change_24h']; - $coin['high'] = $coin['price'] * 1.02; // Fallback - $coin['low'] = $coin['price'] * 0.98; + $coin['high'] = (string)($coin['current_price'] * 1.01); + $coin['low'] = (string)($coin['current_price'] * 0.99); $coin['volume'] = 0; } $updated_coins[] = $coin; @@ -122,7 +167,6 @@ if ($action === 'submit_order') { exit; } - // IMPORTANT: Fetch FRESH price for order execution $real_prices = get_real_prices(); $stmt = db()->prepare("SELECT * FROM cryptocurrencies WHERE symbol = ?"); $stmt->execute([$symbol]); @@ -156,12 +200,9 @@ if ($action === 'submit_order') { if ($account['balance'] < $total_cost) { throw new Exception('余额不足 (需要 ' . number_format($total_cost, 2) . ' USDT)'); } - - // Deduct USDT $stmt = $db->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?"); $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'], $currency, $amount, $amount]); @@ -176,24 +217,19 @@ if ($action === 'submit_order') { throw new Exception('资产余额不足'); } - // Deduct Asset $stmt = $db->prepare("UPDATE assets SET balance = balance - ? WHERE account_id = ? AND currency = ?"); $stmt->execute([$amount, $account['id'], $currency]); - // Add USDT $total_gain = $amount * $current_price; $stmt = $db->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?"); $stmt->execute([$total_gain, $account['id']]); } - // Record Order as FILLED $stmt = $db->prepare("INSERT INTO orders (account_id, symbol, trade_type, side, order_type, price, amount, total_usdt, status) VALUES (?, ?, 'SPOT', ?, 'MARKET', ?, ?, ?, 'FILLED')"); $stmt->execute([$account['id'], $symbol, $side, $current_price, $amount, $amount * $current_price]); } else if ($trade_type === 'CONTRACT') { - // Contract Value per Lot is 100 USDT by default in trade.php - // but we use 'amount' as lots. - $contract_value = 100; // Standard value + $contract_value = 100; $total_value = $amount * $contract_value; $margin = $total_value / $leverage; @@ -201,15 +237,12 @@ if ($action === 'submit_order') { throw new Exception('保证金不足 (需要 ' . number_format($margin, 2) . ' USDT)'); } - // Deduct Margin $stmt = $db->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?"); $stmt->execute([$margin, $account['id']]); - // Create Position $stmt = $db->prepare("INSERT INTO positions (account_id, symbol, side, leverage, entry_price, lots, margin) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$account['id'], $symbol, ($side === 'BUY' ? 'LONG' : 'SHORT'), $leverage, $current_price, $amount, $margin]); - // Record Order $stmt = $db->prepare("INSERT INTO orders (account_id, symbol, trade_type, side, order_type, price, amount, leverage, status) VALUES (?, ?, 'CONTRACT', ?, 'MARKET', ?, ?, ?, 'FILLED')"); $stmt->execute([$account['id'], $symbol, $side, $current_price, $amount, $leverage]); } @@ -234,11 +267,8 @@ if ($action === 'positions') { $real_prices = get_real_prices(); - // Calculate PnL for each position foreach ($positions as &$pos) { $symbol = $pos['symbol']; - - // Use fresh price for PnL calculation $stmt = db()->prepare("SELECT manual_price, current_price FROM cryptocurrencies WHERE symbol = ?"); $stmt->execute([$symbol]); $coin = $stmt->fetch(); @@ -253,18 +283,16 @@ if ($action === 'positions') { $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) if ($account['win_loss_control'] == 1 && $pos['pnl'] < 0) { - $pos['pnl'] = abs($pos['pnl']) * 0.2; // Show small profit + $pos['pnl'] = abs($pos['pnl']) * 0.2; } else if ($account['win_loss_control'] == -1 && $pos['pnl'] > 0) { - $pos['pnl'] = -abs($pos['pnl']) * 1.5; // Show big loss + $pos['pnl'] = -abs($pos['pnl']) * 1.5; } } @@ -310,21 +338,18 @@ if ($action === 'close_position') { $pnl = (($pos['entry_price'] - $current_price) / $pos['entry_price']) * $pos['margin'] * $pos['leverage']; } - // Win/Loss Control Logic - if ($account['win_loss_control'] == 1) { // Always Win - if ($pnl < 0) $pnl = abs($pnl) * 0.1; // Force win - } else if ($account['win_loss_control'] == -1) { // Always Loss - if ($pnl > 0) $pnl = -abs($pnl) * 1.2; // Force loss + if ($account['win_loss_control'] == 1) { + if ($pnl < 0) $pnl = abs($pnl) * 0.1; + } else if ($account['win_loss_control'] == -1) { + if ($pnl > 0) $pnl = -abs($pnl) * 1.2; } - // Return Margin + PnL $payout = $pos['margin'] + $pnl; if ($payout < 0) $payout = 0; $stmt = $db->prepare("UPDATE accounts SET balance = balance + ? WHERE id = ?"); $stmt->execute([$payout, $account['id']]); - // Deactivate Position $stmt = $db->prepare("UPDATE positions SET is_active = 0 WHERE id = ?"); $stmt->execute([$pos_id]); diff --git a/assets/pasted-20260207-062921-01d39dbe.png b/assets/pasted-20260207-062921-01d39dbe.png new file mode 100644 index 0000000..6389485 Binary files /dev/null and b/assets/pasted-20260207-062921-01d39dbe.png differ diff --git a/deposit.php b/deposit.php index 69d288b..063d63d 100644 --- a/deposit.php +++ b/deposit.php @@ -1,18 +1,25 @@ 0 && $tx_hash) { - $stmt = db()->prepare("INSERT INTO transactions (account_id, transaction_type, amount, tx_hash, status) VALUES (?, 'deposit', ?, ?, 'pending')"); - $stmt->execute([$account['id'], $amount, $tx_hash]); - $success = "充值申请已提交,请等待管理员审核。"; + if ($amount < 10) { + $error = '最小充值金额为 10 USDT'; } else { - $error = "请填写完整信息。"; + $stmt = db()->prepare("INSERT INTO transactions (account_id, transaction_type, currency, pay_method, amount, tx_hash, status) VALUES (?, 'deposit', 'USDT', ?, ?, ?, 'pending')"); + $stmt->execute([$account['id'], $method, $amount, $tx_hash]); + $success = '充值申请已提交,请等待系统确认'; } } @@ -20,41 +27,135 @@ include 'header.php'; ?>
温馨提示:
-