258 lines
9.8 KiB
PHP
258 lines
9.8 KiB
PHP
<?php
|
|
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";
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
|
$response = curl_exec($ch);
|
|
curl_close($ch);
|
|
|
|
if (!$response) return [];
|
|
|
|
$data = json_decode($response, true);
|
|
$prices = [];
|
|
if (is_array($data)) {
|
|
foreach ($data as $item) {
|
|
$prices[$item['symbol']] = [
|
|
'price' => $item['lastPrice'],
|
|
'change' => $item['priceChangePercent']
|
|
];
|
|
}
|
|
}
|
|
return $prices;
|
|
}
|
|
|
|
if ($action === 'market_data') {
|
|
$binance_prices = fetch_binance_prices();
|
|
$stmt = db()->query("SELECT * FROM cryptocurrencies WHERE is_active = 1");
|
|
$coins = $stmt->fetchAll();
|
|
|
|
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'];
|
|
|
|
// Apply manual price if set
|
|
if ($coin['manual_price'] > 0) {
|
|
$coin['price'] = (float)$coin['manual_price'];
|
|
}
|
|
|
|
// Update DB with latest price
|
|
$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'];
|
|
}
|
|
}
|
|
|
|
header('Content-Type: application/json');
|
|
echo json_encode($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;
|
|
}
|
|
|
|
// Get current price
|
|
$stmt = db()->prepare("SELECT * FROM cryptocurrencies WHERE symbol = ?");
|
|
$stmt->execute([$symbol]);
|
|
$coin = $stmt->fetch();
|
|
$current_price = $coin ? (float)$coin['current_price'] : 0;
|
|
|
|
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('余额不足 (需要 ' . $total_cost . ' USDT)');
|
|
}
|
|
|
|
// Deduct USDT
|
|
$stmt = $db->prepare("UPDATE accounts SET balance = balance - ? WHERE id = ?");
|
|
$stmt->execute([$total_cost, $account['id']]);
|
|
|
|
// Add Asset
|
|
$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]);
|
|
|
|
} 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') {
|
|
// Simple Contract Logic: Deduct Margin
|
|
$margin = ($amount * $current_price) / $leverage;
|
|
if ($account['balance'] < $margin) {
|
|
throw new Exception('保证金不足 (需要 ' . $margin . ' 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();
|
|
|
|
// 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'];
|
|
|
|
if ($pos['side'] === 'LONG') {
|
|
$pos['pnl'] = ($current_price - $pos['entry_price']) * $pos['lots'];
|
|
} else {
|
|
$pos['pnl'] = ($pos['entry_price'] - $current_price) * $pos['lots'];
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
$pos['current_price'] = $current_price;
|
|
}
|
|
|
|
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('仓位不存在');
|
|
|
|
$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'];
|
|
|
|
if ($pos['side'] === 'LONG') {
|
|
$pnl = ($current_price - $pos['entry_price']) * $pos['lots'];
|
|
} else {
|
|
$pnl = ($pos['entry_price'] - $current_price) * $pos['lots'];
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
?>
|