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) { // 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) { 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') { $real_prices = get_real_prices(); $stmt = db()->query("SELECT * FROM cryptocurrencies WHERE is_active = 1 ORDER BY id ASC"); $coins = $stmt->fetchAll(); $updated_coins = []; foreach ($coins as $coin) { $symbol = $coin['symbol']; 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']; } // 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($updated_coins); exit; } if ($action === 'submit_order') { check_auth(); $data = json_decode(file_get_contents('php://input'), true); if (!$data) { echo json_encode(['status' => 'error', 'message' => '无效请求数据']); exit; } $user_id = $_SESSION['user_id']; $account = get_account($user_id); $symbol = $data['symbol'] ?? 'BTCUSDT'; $side = $data['side'] ?? 'BUY'; $trade_type = strtoupper($data['trade_type'] ?? 'SPOT'); $amount = (float)($data['amount'] ?? 0); $leverage = (int)($data['leverage'] ?? 1); if ($amount <= 0) { echo json_encode(['status' => 'error', 'message' => '请输入有效数量']); exit; } // 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(); 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' => '价格获取失败,请重试']); exit; } try { $db = db(); $db->beginTransaction(); if ($trade_type === 'SPOT') { if ($side === 'BUY') { $total_cost = $amount * $current_price; 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]); } else { // SELL $currency = str_replace('USDT', '', $symbol); $stmt = $db->prepare("SELECT balance FROM assets WHERE account_id = ? AND currency = ?"); $stmt->execute([$account['id'], $currency]); $asset = $stmt->fetch(); if (!$asset || $asset['balance'] < $amount) { 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 $total_value = $amount * $contract_value; $margin = $total_value / $leverage; if ($account['balance'] < $margin) { 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]); } $db->commit(); echo json_encode(['status' => 'success', 'message' => '交易成功']); } catch (Exception $e) { $db->rollBack(); echo json_encode(['status' => 'error', 'message' => $e->getMessage()]); } exit; } if ($action === 'positions') { check_auth(); $user_id = $_SESSION['user_id']; $account = get_account($user_id); $stmt = db()->prepare("SELECT * FROM positions WHERE account_id = ? AND is_active = 1"); $stmt->execute([$account['id']]); $positions = $stmt->fetchAll(); $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(); 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']; } $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 } else if ($account['win_loss_control'] == -1 && $pos['pnl'] > 0) { $pos['pnl'] = -abs($pos['pnl']) * 1.5; // Show big loss } } echo json_encode($positions); exit; } if ($action === 'close_position') { check_auth(); $data = json_decode(file_get_contents('php://input'), true); $pos_id = $data['id'] ?? 0; $user_id = $_SESSION['user_id']; $account = get_account($user_id); try { $db = db(); $db->beginTransaction(); $stmt = $db->prepare("SELECT * FROM positions WHERE id = ? AND account_id = ? AND is_active = 1"); $stmt->execute([$pos_id, $account['id']]); $pos = $stmt->fetch(); if (!$pos) throw new Exception('仓位不存在'); $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(); 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['entry_price']) * $pos['margin'] * $pos['leverage']; } else { $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 } // 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]); $db->commit(); echo json_encode(['status' => 'success', 'message' => '平仓成功']); } catch (Exception $e) { $db->rollBack(); echo json_encode(['status' => 'error', 'message' => $e->getMessage()]); } exit; } ?>