V.1.14
This commit is contained in:
parent
a824cabd7e
commit
ffa84da26a
61
api/analysis.php
Normal file
61
api/analysis.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// --- Parameters ---
|
||||
// Example: /api/analysis.php?symbol=BTCUSDT&indicator=sma&period=20
|
||||
$symbol = $_GET['symbol'] ?? 'BTCUSDT';
|
||||
$indicator = $_GET['indicator'] ?? 'sma';
|
||||
$period = (int)($_GET['period'] ?? 20);
|
||||
|
||||
if ($period <= 0) {
|
||||
echo json_encode(['error' => 'Period must be a positive integer.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
function calculate_sma($symbol, $period) {
|
||||
$pdo = db();
|
||||
|
||||
// Fetch the last 'period' number of closing prices for the given symbol, most recent first.
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT close FROM candlestick_data
|
||||
WHERE symbol = :symbol
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT :period"
|
||||
);
|
||||
|
||||
$stmt->bindParam(':symbol', $symbol, PDO::PARAM_STR);
|
||||
$stmt->bindParam(':period', $period, PDO::PARAM_INT);
|
||||
|
||||
$stmt->execute();
|
||||
|
||||
$closes = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
if (count($closes) < $period) {
|
||||
return null; // Not enough data to calculate SMA
|
||||
}
|
||||
|
||||
$sum = array_sum($closes);
|
||||
$sma = $sum / $period;
|
||||
|
||||
return $sma;
|
||||
}
|
||||
|
||||
$result = null;
|
||||
if (strtolower($indicator) === 'sma') {
|
||||
$result = calculate_sma($symbol, $period);
|
||||
} else {
|
||||
echo json_encode(['error' => 'Invalid indicator specified.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$response = [
|
||||
'symbol' => $symbol,
|
||||
'indicator' => 'SMA',
|
||||
'period' => $period,
|
||||
'value' => $result,
|
||||
'timestamp' => time()
|
||||
];
|
||||
|
||||
echo json_encode($response);
|
||||
160
api/ticker.php
160
api/ticker.php
@ -1,85 +1,109 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
|
||||
// --- Configuration ---
|
||||
// Default to BTCUSDT if no symbol is provided in the URL query string.
|
||||
$symbol = isset($_GET['symbol']) ? strtoupper($_GET['symbol']) : 'BTCUSDT';
|
||||
$api_url = "https://api.bitget.com/api/v2/spot/market/tickers?symbol=" . $symbol;
|
||||
$symbols = ['BTCUSDT', 'ETHUSDT'];
|
||||
$exchange = 'Binance';
|
||||
$interval = '1m';
|
||||
|
||||
// --- cURL Execution ---
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $api_url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json'
|
||||
],
|
||||
// It's good practice to set a user-agent
|
||||
CURLOPT_USERAGENT => 'FlatlogicMarketDetector/1.0'
|
||||
]);
|
||||
// --- Data Fetching ---
|
||||
function fetch_candlestick_data($symbol, $interval) {
|
||||
$api_url = sprintf(
|
||||
"https://api.binance.com/api/v3/klines?symbol=%s&interval=%s&limit=5",
|
||||
$symbol, $interval
|
||||
);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $api_url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 10,
|
||||
CURLOPT_USERAGENT => 'FlatlogicMarketDetector/1.0'
|
||||
]);
|
||||
|
||||
// --- Response Handling ---
|
||||
if ($error) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'cURL Error: ' . $error]);
|
||||
exit;
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
if ($http_code !== 200) {
|
||||
http_response_code($http_code);
|
||||
echo json_encode(['error' => 'Bitget API returned non-200 status', 'details' => json_decode($response)]);
|
||||
exit;
|
||||
// --- Database Logging ---
|
||||
function log_candlestick_data($pdo, $symbol, $exchange, $interval, $kline) {
|
||||
$sql = <<<SQL
|
||||
INSERT INTO candlestick_data (symbol, exchange, interval_time, open_time, open_price, high_price, low_price, close_price, volume, close_time, quote_asset_volume, number_of_trades, taker_buy_base_asset_volume, taker_buy_quote_asset_volume)
|
||||
VALUES (:symbol, :exchange, :interval_time, :open_time, :open_price, :high_price, :low_price, :close_price, :volume, :close_time, :quote_asset_volume, :number_of_trades, :taker_buy_base_asset_volume, :taker_buy_quote_asset_volume)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
close_price = VALUES(close_price), high_price = VALUES(high_price), low_price = VALUES(low_price), volume = VALUES(volume);
|
||||
SQL;
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
$stmt->execute([
|
||||
':symbol' => $symbol,
|
||||
':exchange' => $exchange,
|
||||
':interval_time' => $interval,
|
||||
':open_time' => $kline[0],
|
||||
':open_price' => $kline[1],
|
||||
':high_price' => $kline[2],
|
||||
':low_price' => $kline[3],
|
||||
':close_price' => $kline[4],
|
||||
':volume' => $kline[5],
|
||||
':close_time' => $kline[6],
|
||||
':quote_asset_volume' => $kline[7],
|
||||
':number_of_trades' => $kline[8],
|
||||
':taker_buy_base_asset_volume' => $kline[9],
|
||||
':taker_buy_quote_asset_volume' => $kline[10],
|
||||
]);
|
||||
}
|
||||
|
||||
$data = json_decode($response, true);
|
||||
// --- Main Execution ---
|
||||
$latest_tickers = [];
|
||||
|
||||
// --- Data Validation & Output ---
|
||||
if (json_last_error() !== JSON_ERROR_NONE || empty($data['data'])) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => 'Failed to parse JSON response or data is empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Extract the first ticker object from the response data array
|
||||
$ticker_data = $data['data'][0] ?? null;
|
||||
|
||||
if (!$ticker_data) {
|
||||
http_response_code(404);
|
||||
echo json_encode(['error' => 'Ticker data for symbol not found in API response']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Log to Database ---
|
||||
try {
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
$price_to_log = $ticker_data['lastPr'] ?? null;
|
||||
if ($price_to_log) {
|
||||
$pdo = db();
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO price_history (symbol, price) VALUES (:symbol, :price)"
|
||||
);
|
||||
$stmt->execute(['symbol' => $symbol, 'price' => $price_to_log]);
|
||||
$pdo = db();
|
||||
foreach ($symbols as $symbol) {
|
||||
$klines = fetch_candlestick_data($symbol, $interval);
|
||||
|
||||
if (empty($klines) || !is_array($klines)) {
|
||||
continue; // Skip this symbol if data fetching fails
|
||||
}
|
||||
|
||||
foreach ($klines as $kline) {
|
||||
log_candlestick_data($pdo, $symbol, $exchange, $interval, $kline);
|
||||
}
|
||||
|
||||
// For the frontend, provide the most recent ticker data
|
||||
$latest_kline = end($klines);
|
||||
$latest_tickers[] = [
|
||||
'exchange' => $exchange,
|
||||
'symbol' => $symbol,
|
||||
'price' => $latest_kline[4], // Close price
|
||||
'change_24h_percent' => 0, // Placeholder, as kline API doesn't provide this directly
|
||||
'signal' => '-'
|
||||
];
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// If DB fails, log error but don't crash the API endpoint
|
||||
error_log('DB logging failed in ticker.php: ' . $e->getMessage());
|
||||
http_response_code(500);
|
||||
// Log error and exit gracefully
|
||||
error_log('Ticker script failed: ' . $e->getMessage());
|
||||
echo json_encode(['error' => 'An internal error occurred.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// --- Sanitize and Structure Output ---
|
||||
// We select and sanitize the fields we want to send to the frontend.
|
||||
$output = [
|
||||
'exchange' => 'Bitget',
|
||||
'symbol' => $ticker_data['symbol'] ?? 'N/A',
|
||||
'price' => $ticker_data['lastPr'] ?? '0.00',
|
||||
// Bitget provides the 24h change as a decimal, e.g., 0.025 for +2.5%
|
||||
'change_24h_percent' => isset($ticker_data['priceChangePercent']) ? (float)$ticker_data['priceChangePercent'] * 100 : 0,
|
||||
'signal' => '-' // Placeholder for future signal detection
|
||||
];
|
||||
|
||||
echo json_encode($output);
|
||||
// --- Output for Frontend ---
|
||||
// This part is for the main dashboard display, not the alerts API.
|
||||
if (isset($_GET['symbol'])) {
|
||||
$symbol_to_find = strtoupper($_GET['symbol']);
|
||||
foreach ($latest_tickers as $ticker) {
|
||||
if ($ticker['symbol'] === $symbol_to_find) {
|
||||
echo json_encode($ticker);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
// Fallback if the requested symbol wasn't processed
|
||||
echo json_encode(['error' => 'Data for symbol not found.']);
|
||||
} else {
|
||||
// If no symbol is specified, do not return anything for now.
|
||||
// The frontend will request each symbol individually.
|
||||
echo json_encode(['status' => 'OK', 'message' => 'Data processed.']);
|
||||
}
|
||||
@ -63,3 +63,13 @@ body {
|
||||
.flash-danger {
|
||||
animation: flash-danger-anim 0.75s ease-out;
|
||||
}
|
||||
|
||||
/* Alert flash animation */
|
||||
@keyframes flash-alert-anim {
|
||||
0%, 100% { background-color: transparent; }
|
||||
50% { background-color: rgba(220, 53, 69, 0.25); }
|
||||
}
|
||||
|
||||
.flash-alert {
|
||||
animation: flash-alert-anim 1.5s infinite;
|
||||
}
|
||||
|
||||
@ -1,100 +1,124 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const POLLING_INTERVAL = 3000; // 3 seconds
|
||||
|
||||
// --- Configuration for Live Tickers ---
|
||||
const liveTickers = [
|
||||
{ symbol: 'BTCUSDT', elementId: 'live-crypto-row-btc', lastPrice: 0 },
|
||||
{ symbol: 'ETHUSDT', elementId: 'live-crypto-row-eth', lastPrice: 0 }
|
||||
];
|
||||
|
||||
// --- Generic Fetch and Update Functions ---
|
||||
|
||||
/**
|
||||
* Fetches the latest price for a given symbol and updates its corresponding row.
|
||||
* @param {object} ticker - The ticker object from the liveTickers array.
|
||||
* @param {HTMLElement} rowElement - The table row element to update.
|
||||
*/
|
||||
async function fetchAndupdate(ticker, rowElement) {
|
||||
try {
|
||||
const response = await fetch(`api/ticker.php?symbol=${ticker.symbol}`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
}
|
||||
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.error) {
|
||||
throw new Error(`API Error: ${data.error}`);
|
||||
}
|
||||
|
||||
if (data.error) throw new Error(`API Error: ${data.error}`);
|
||||
updateRow(data, ticker, rowElement);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch live price for ${ticker.symbol}:`, error);
|
||||
// Optionally, display an error state in the specific row
|
||||
const priceCell = rowElement.querySelector('.price');
|
||||
if (priceCell) priceCell.textContent = 'Error';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the DOM of a specific row with new data.
|
||||
* @param {object} data - The data object from the API.
|
||||
* @param {object} ticker - The ticker object being updated.
|
||||
* @param {HTMLElement} rowElement - The table row element.
|
||||
*/
|
||||
function updateRow(data, ticker, rowElement) {
|
||||
const priceCell = rowElement.querySelector('.price');
|
||||
const changeCell = rowElement.querySelector('.change');
|
||||
const symbolCell = rowElement.querySelector('.symbol');
|
||||
const exchangeCell = rowElement.querySelector('.exchange');
|
||||
|
||||
if (!priceCell || !changeCell || !symbolCell || !exchangeCell) {
|
||||
if (!priceCell || !symbolCell || !exchangeCell) {
|
||||
console.error(`One or more required elements not found in row for ${ticker.symbol}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPrice = parseFloat(data.price);
|
||||
const priceChange = currentPrice - ticker.lastPrice;
|
||||
|
||||
// Update text content
|
||||
priceCell.textContent = `$${currentPrice.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
||||
priceCell.textContent = `${currentPrice.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
||||
symbolCell.textContent = data.symbol.replace('_SPBL', '');
|
||||
exchangeCell.textContent = data.exchange;
|
||||
|
||||
// Update 24h change
|
||||
const changePercent = parseFloat(data.change_24h_percent);
|
||||
changeCell.textContent = `${changePercent.toFixed(2)}%`;
|
||||
changeCell.classList.toggle('text-success', changePercent >= 0);
|
||||
changeCell.classList.toggle('text-danger', changePercent < 0);
|
||||
|
||||
// Visual flash on price change
|
||||
if (ticker.lastPrice !== 0 && priceChange !== 0) {
|
||||
const flashClass = priceChange > 0 ? 'flash-success' : 'flash-danger';
|
||||
if (ticker.lastPrice !== 0 && currentPrice !== ticker.lastPrice) {
|
||||
const flashClass = currentPrice > ticker.lastPrice ? 'flash-success' : 'flash-danger';
|
||||
priceCell.classList.add(flashClass);
|
||||
setTimeout(() => priceCell.classList.remove(flashClass), 750);
|
||||
}
|
||||
|
||||
ticker.lastPrice = currentPrice; // Update the last price for this specific ticker
|
||||
ticker.lastPrice = currentPrice;
|
||||
}
|
||||
|
||||
// --- Initialization ---
|
||||
async function fetchAnalysis(ticker, rowElement) {
|
||||
try {
|
||||
const response = await fetch(`api/analysis.php?symbol=${ticker.symbol}&period=20`);
|
||||
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
const data = await response.json();
|
||||
if (data.error) throw new Error(`API Error: ${data.error}`);
|
||||
updateAnalysis(data, rowElement, ticker.lastPrice);
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch analysis for ${ticker.symbol}:`, error);
|
||||
const smaCell = rowElement.querySelector('.sma');
|
||||
if (smaCell) smaCell.textContent = 'Error';
|
||||
}
|
||||
}
|
||||
|
||||
function updateAnalysis(data, rowElement, currentPrice) {
|
||||
const smaCell = rowElement.querySelector('.sma');
|
||||
const signalCell = rowElement.querySelector('.signal');
|
||||
if (!smaCell || !signalCell) return;
|
||||
|
||||
const sma = parseFloat(data.sma);
|
||||
smaCell.textContent = sma.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
|
||||
if (currentPrice > sma) {
|
||||
signalCell.innerHTML = '<span class="badge bg-success-subtle text-success-emphasis">Above SMA</span>';
|
||||
} else if (currentPrice < sma) {
|
||||
signalCell.innerHTML = '<span class="badge bg-danger-subtle text-danger-emphasis">Below SMA</span>';
|
||||
} else {
|
||||
signalCell.innerHTML = '<span class="badge bg-secondary-subtle text-secondary-emphasis">At SMA</span>';
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAlert(ticker, rowElement) {
|
||||
try {
|
||||
const response = await fetch(`api/alerts.php?symbol=${ticker.symbol}`);
|
||||
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
const data = await response.json();
|
||||
updateAlertStatus(data.status, rowElement);
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch alert for ${ticker.symbol}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
function updateAlertStatus(status, rowElement) {
|
||||
const statusCell = rowElement.querySelector('.status');
|
||||
if (!statusCell) return;
|
||||
|
||||
rowElement.classList.remove('flash-alert');
|
||||
statusCell.innerHTML = '-';
|
||||
|
||||
if (status === 'Crash Alert') {
|
||||
statusCell.innerHTML = '<span class="badge bg-danger-subtle text-danger-emphasis">Crash Alert</span>';
|
||||
rowElement.classList.add('flash-alert');
|
||||
} else if (status === 'Pump Alert') {
|
||||
statusCell.innerHTML = '<span class="badge bg-success-subtle text-success-emphasis">Pump Alert</span>';
|
||||
rowElement.classList.add('flash-alert');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the polling for all configured tickers.
|
||||
*/
|
||||
function initializeLiveTickers() {
|
||||
liveTickers.forEach(ticker => {
|
||||
const rowElement = document.getElementById(ticker.elementId);
|
||||
if (!rowElement) {
|
||||
console.error(`Element with ID #${ticker.elementId} not found for symbol ${ticker.symbol}.`);
|
||||
return; // Skip this ticker if its row doesn't exist
|
||||
return;
|
||||
}
|
||||
|
||||
// Initial fetch
|
||||
fetchAndupdate(ticker, rowElement);
|
||||
const fetchAll = () => {
|
||||
fetchAndupdate(ticker, rowElement).then(() => {
|
||||
fetchAnalysis(ticker, rowElement);
|
||||
});
|
||||
fetchAlert(ticker, rowElement);
|
||||
};
|
||||
|
||||
// Start polling every X seconds
|
||||
setInterval(() => fetchAndupdate(ticker, rowElement), POLLING_INTERVAL);
|
||||
fetchAll();
|
||||
setInterval(fetchAll, POLLING_INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
31
db/migrations/002_create_candlestick_data_table.php
Normal file
31
db/migrations/002_create_candlestick_data_table.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../config.php';
|
||||
|
||||
try {
|
||||
$db = db();
|
||||
$sql = <<<SQL
|
||||
CREATE TABLE IF NOT EXISTS candlestick_data (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
symbol VARCHAR(20) NOT NULL,
|
||||
exchange VARCHAR(50) NOT NULL,
|
||||
interval_time VARCHAR(10) NOT NULL,
|
||||
open_time BIGINT NOT NULL,
|
||||
open_price DECIMAL(20, 8) NOT NULL,
|
||||
high_price DECIMAL(20, 8) NOT NULL,
|
||||
low_price DECIMAL(20, 8) NOT NULL,
|
||||
close_price DECIMAL(20, 8) NOT NULL,
|
||||
volume DECIMAL(20, 8) NOT NULL,
|
||||
close_time BIGINT NOT NULL,
|
||||
quote_asset_volume DECIMAL(20, 8),
|
||||
number_of_trades INT,
|
||||
taker_buy_base_asset_volume DECIMAL(20, 8),
|
||||
taker_buy_quote_asset_volume DECIMAL(20, 8),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY `symbol_exchange_interval_time_open_time` (`symbol`, `exchange`, `interval_time`, `open_time`)
|
||||
);
|
||||
SQL;
|
||||
$db->exec($sql);
|
||||
echo "Migration 002 successful: candlestick_data table created or already exists." . PHP_EOL;
|
||||
} catch (PDOException $e) {
|
||||
die("Migration 002 failed: " . $e->getMessage() . PHP_EOL);
|
||||
}
|
||||
70
index.php
70
index.php
@ -49,30 +49,30 @@
|
||||
<th scope="col">EXCHANGE</th>
|
||||
<th scope="col">SYMBOL</th>
|
||||
<th scope="col">PRICE</th>
|
||||
<th scope="col" class="text-center">24H %</th>
|
||||
<th scope="col" class="text-end">SIGNAL</th>
|
||||
<th scope="col">SMA (20m)</th>
|
||||
<th scope="col">SIGNAL</th>
|
||||
<th scope="col" class="text-end">STATUS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Live Row 1 (Updated by JavaScript) -->
|
||||
<!-- BTC Row (Updated by JavaScript) -->
|
||||
<tr id="live-crypto-row-btc">
|
||||
<td class="fw-medium text-muted exchange">Bitget</td>
|
||||
<td class="fw-medium text-muted exchange">Binance</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://via.placeholder.com/24/0d6efd/FFFFFF?Text=B" class="symbol-logo rounded-circle" alt="BTC">
|
||||
<img src="https://via.placeholder.com/24/f0b90b/000000?Text=B" class="symbol-logo rounded-circle" alt="BTC">
|
||||
<span class="fw-bold symbol">BTC/USDT</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="fw-bold fs-5 price">$0.00</td>
|
||||
<td class="text-center">
|
||||
<span class="fw-bold change">--%</span>
|
||||
</td>
|
||||
<td class="text-end signal">-</td>
|
||||
<td class="fw-medium sma">-</td>
|
||||
<td class="fw-medium signal">-</td>
|
||||
<td class="text-end status">-</td>
|
||||
</tr>
|
||||
|
||||
<!-- Live Row 2 (Updated by JavaScript) -->
|
||||
<!-- ETH Row (Updated by JavaScript) -->
|
||||
<tr id="live-crypto-row-eth">
|
||||
<td class="fw-medium text-muted exchange">Bitget</td>
|
||||
<td class="fw-medium text-muted exchange">Binance</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://via.placeholder.com/24/6f42c1/FFFFFF?Text=E" class="symbol-logo rounded-circle" alt="ETH">
|
||||
@ -80,52 +80,10 @@
|
||||
</div>
|
||||
</td>
|
||||
<td class="fw-bold fs-5 price">$0.00</td>
|
||||
<td class="text-center">
|
||||
<span class="fw-bold change">--%</span>
|
||||
</td>
|
||||
<td class="text-end signal">-</td>
|
||||
<td class="fw-medium sma">-</td>
|
||||
<td class="fw-medium signal">-</td>
|
||||
<td class="text-end status">-</td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
// Static mock data for UI variety
|
||||
$mock_data = [
|
||||
['exchange' => 'WEEX', 'symbol' => 'PEPE/USDT', 'price' => '0.00001234', 'change' => 255.1, 'signal' => 'Pump Alert'],
|
||||
['exchange' => 'KuCoin', 'symbol' => 'SOL/USDT', 'price' => '165.80', 'change' => -1.2, 'signal' => 'Bearish Reversal'],
|
||||
['exchange' => 'KCEX', 'symbol' => 'DOGE/USDT', 'price' => '0.1588', 'change' => 5.6, 'signal' => null],
|
||||
['exchange' => 'Bitget', 'symbol' => 'XRP/USDT', 'price' => '0.5210', 'change' => -0.5, 'signal' => null],
|
||||
];
|
||||
|
||||
foreach ($mock_data as $row):
|
||||
$change_color = $row['change'] >= 0 ? 'success' : 'danger';
|
||||
$change_icon = $row['change'] >= 0 ? 'bi-arrow-up-right' : 'bi-arrow-down-left';
|
||||
|
||||
$signal_badge = '';
|
||||
if ($row['signal'] === 'Crash Alert') {
|
||||
$signal_badge = '<span class="badge bg-danger-subtle text-danger-emphasis">Crash Alert</span>';
|
||||
} elseif ($row['signal'] === 'Pump Alert') {
|
||||
$signal_badge = '<span class="badge bg-success-subtle text-success-emphasis">Pump Alert</span>';
|
||||
} elseif ($row['signal'] === 'Bearish Reversal') {
|
||||
$signal_badge = '<span class="badge bg-warning-subtle text-warning-emphasis">Bearish Reversal</span>';
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td class="fw-medium text-muted"><?php echo htmlspecialchars($row['exchange']); ?></td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://via.placeholder.com/24/0d6efd/FFFFFF?Text=<?php echo substr($row['symbol'], 0, 1); ?>" class="symbol-logo rounded-circle" alt="">
|
||||
<span class="fw-bold"><?php echo htmlspecialchars($row['symbol']); ?></span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="fw-bold fs-5">$<?php echo htmlspecialchars($row['price']); ?></td>
|
||||
<td class="text-center">
|
||||
<span class="fw-bold text-<?php echo $change_color; ?>">
|
||||
<i class="bi <?php echo $change_icon; ?>"></i>
|
||||
<?php echo htmlspecialchars(abs($row['change'])); ?>%
|
||||
</span>
|
||||
</td>
|
||||
<td class="text-end"><?php echo $signal_badge; ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user