Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
@ -1,77 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../db/config.php';
|
|
||||||
require_once __DIR__ . '/analysis.php';
|
|
||||||
|
|
||||||
// Example: /api/alerts.php?symbol=BTCUSDT
|
|
||||||
$symbol = $_GET['symbol'] ?? 'BTCUSDT';
|
|
||||||
|
|
||||||
function check_bearish_alerts($symbol) {
|
|
||||||
$alerts = [];
|
|
||||||
|
|
||||||
// Fetch latest price
|
|
||||||
$pdo = db();
|
|
||||||
$stmt = $pdo->prepare("SELECT close_price FROM candlestick_data WHERE symbol = :symbol ORDER BY open_time DESC LIMIT 1");
|
|
||||||
$stmt->bindParam(':symbol', $symbol, PDO::PARAM_STR);
|
|
||||||
$stmt->execute();
|
|
||||||
$current_price = $stmt->fetchColumn();
|
|
||||||
|
|
||||||
if (!$current_price) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch analysis data
|
|
||||||
$sma = calculate_sma($symbol, 20);
|
|
||||||
$rsi = calculate_rsi($symbol, 14);
|
|
||||||
$macd_data = calculate_macd($symbol);
|
|
||||||
$patterns = calculate_patterns($symbol);
|
|
||||||
|
|
||||||
// Condition 1: Price vs. SMA
|
|
||||||
$price_below_sma = $current_price < $sma;
|
|
||||||
|
|
||||||
// Condition 2: RSI indicates weakening momentum (e.g., dropping from overbought)
|
|
||||||
// For simplicity, we'll check if RSI is above a certain threshold and not rising.
|
|
||||||
// A more complex implementation would track RSI changes over time.
|
|
||||||
$rsi_weakening = $rsi > 50; // Simplified: RSI is in the upper range, potential for reversal
|
|
||||||
|
|
||||||
// Condition 3: MACD Bearish Crossover
|
|
||||||
$macd_bearish_crossover = false;
|
|
||||||
if ($macd_data && $macd_data['macd'] < $macd_data['signal']) {
|
|
||||||
// Check if it just crossed
|
|
||||||
// This requires historical data, for now, we check the current state
|
|
||||||
$macd_bearish_crossover = true; // Simplified: MACD is below signal line
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check original combo
|
|
||||||
if ($price_below_sma && $rsi_weakening && $macd_bearish_crossover) {
|
|
||||||
$alerts[] = [
|
|
||||||
'type' => 'Strong Bearish Signal',
|
|
||||||
'indicator' => 'SMA, RSI, MACD Combination',
|
|
||||||
'message' => 'Price dropped below 20-period SMA, RSI is high, and MACD shows a bearish state.'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for candlestick patterns
|
|
||||||
if (!empty($patterns)) {
|
|
||||||
foreach ($patterns as $pattern_name => $detected) {
|
|
||||||
if ($detected) {
|
|
||||||
$alerts[] = [
|
|
||||||
'type' => 'Strong Bearish Signal',
|
|
||||||
'indicator' => 'Candlestick Pattern',
|
|
||||||
'message' => 'Detected bearish pattern: ' . ucwords(str_replace('_', ' ', $pattern_name))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $alerts;
|
|
||||||
}
|
|
||||||
|
|
||||||
$alerts = check_bearish_alerts($symbol);
|
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
'symbol' => $symbol,
|
|
||||||
'alerts' => $alerts,
|
|
||||||
'timestamp' => time()
|
|
||||||
]);
|
|
||||||
234
api/analysis.php
234
api/analysis.php
@ -1,234 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
require_once __DIR__ . '/../db/config.php';
|
|
||||||
|
|
||||||
// Example: /api/analysis.php?symbol=BTCUSDT&indicators=sma,rsi,macd,patterns&period=20
|
|
||||||
$symbol = $_GET['symbol'] ?? 'BTCUSDT';
|
|
||||||
$indicators_str = $_GET['indicators'] ?? 'sma';
|
|
||||||
$indicators = array_map('trim', explode(',', strtolower($indicators_str)));
|
|
||||||
$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();
|
|
||||||
$stmt = $pdo->prepare("SELECT close_price FROM candlestick_data WHERE symbol = :symbol ORDER BY open_time 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;
|
|
||||||
}
|
|
||||||
return array_sum($closes) / $period;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculate_rsi($symbol, $period = 14) {
|
|
||||||
$pdo = db();
|
|
||||||
$limit = $period + 1;
|
|
||||||
$stmt = $pdo->prepare("SELECT close_price FROM candlestick_data WHERE symbol = :symbol ORDER BY open_time DESC LIMIT :limit");
|
|
||||||
$stmt->bindParam(':symbol', $symbol, PDO::PARAM_STR);
|
|
||||||
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
|
|
||||||
$stmt->execute();
|
|
||||||
$closes = array_reverse($stmt->fetchAll(PDO::FETCH_COLUMN));
|
|
||||||
if (count($closes) < $limit) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$gains = 0;
|
|
||||||
$losses = 0;
|
|
||||||
for ($i = 1; $i < count($closes); $i++) {
|
|
||||||
$change = $closes[$i] - $closes[$i - 1];
|
|
||||||
if ($change > 0) {
|
|
||||||
$gains += $change;
|
|
||||||
} else {
|
|
||||||
$losses += abs($change);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($period == 0) return 100;
|
|
||||||
$avg_gain = $gains / $period;
|
|
||||||
$avg_loss = $losses / $period;
|
|
||||||
if ($avg_loss == 0) {
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
$rs = $avg_gain / $avg_loss;
|
|
||||||
return 100 - (100 / (1 + $rs));
|
|
||||||
}
|
|
||||||
|
|
||||||
function _calculate_ema_series($prices, $period) {
|
|
||||||
if (count($prices) < $period) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
$smoothing_factor = 2 / ($period + 1);
|
|
||||||
$emas = [];
|
|
||||||
$initial_prices = array_slice($prices, 0, $period);
|
|
||||||
$sma = array_sum($initial_prices) / count($initial_prices);
|
|
||||||
$emas[] = $sma;
|
|
||||||
$remaining_prices = array_slice($prices, $period);
|
|
||||||
$last_ema = $sma;
|
|
||||||
foreach ($remaining_prices as $price) {
|
|
||||||
$current_ema = ($price - $last_ema) * $smoothing_factor + $last_ema;
|
|
||||||
$emas[] = $current_ema;
|
|
||||||
$last_ema = $current_ema;
|
|
||||||
}
|
|
||||||
return $emas;
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculate_macd($symbol, $fast_period = 12, $slow_period = 26, $signal_period = 9) {
|
|
||||||
$pdo = db();
|
|
||||||
$limit = $slow_period + $signal_period + 50; // Fetch extra data for stability
|
|
||||||
$stmt = $pdo->prepare("SELECT close_price FROM candlestick_data WHERE symbol = :symbol ORDER BY open_time ASC LIMIT :limit");
|
|
||||||
$stmt->bindParam(':symbol', $symbol, PDO::PARAM_STR);
|
|
||||||
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
|
|
||||||
$stmt->execute();
|
|
||||||
$closes = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
|
||||||
|
|
||||||
if (count($closes) < $slow_period + $signal_period) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ema_fast_all = _calculate_ema_series($closes, $fast_period);
|
|
||||||
$ema_slow_all = _calculate_ema_series($closes, $slow_period);
|
|
||||||
|
|
||||||
$ema_fast_aligned = array_slice($ema_fast_all, count($ema_fast_all) - count($ema_slow_all));
|
|
||||||
|
|
||||||
$macd_line_series = [];
|
|
||||||
for ($i = 0; $i < count($ema_slow_all); $i++) {
|
|
||||||
$macd_line_series[] = $ema_fast_aligned[$i] - $ema_slow_all[$i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($macd_line_series) < $signal_period) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$signal_line_series = _calculate_ema_series($macd_line_series, $signal_period);
|
|
||||||
|
|
||||||
$macd_line = end($macd_line_series);
|
|
||||||
$signal_line = end($signal_line_series);
|
|
||||||
$histogram = $macd_line - $signal_line;
|
|
||||||
|
|
||||||
return [
|
|
||||||
'macd' => $macd_line,
|
|
||||||
'signal' => $signal_line,
|
|
||||||
'histogram' => $histogram
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function calculate_patterns($symbol) {
|
|
||||||
$pdo = db();
|
|
||||||
// Fetch last 30 candles for pattern recognition
|
|
||||||
$stmt = $pdo->prepare("SELECT open_price, high_price, low_price, close_price, open_time FROM candlestick_data WHERE symbol = :symbol ORDER BY open_time DESC LIMIT 30");
|
|
||||||
$stmt->bindParam(':symbol', $symbol, PDO::PARAM_STR);
|
|
||||||
$stmt->execute();
|
|
||||||
$candles = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (count($candles) < 5) { // Need at least a few candles for any pattern
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$patterns = [];
|
|
||||||
$latest_candle = $candles[0];
|
|
||||||
$prev_candle = $candles[1];
|
|
||||||
|
|
||||||
// 1. Shooting Star
|
|
||||||
$body_size = abs($latest_candle['open_price'] - $latest_candle['close_price']);
|
|
||||||
$upper_wick = $latest_candle['high_price'] - max($latest_candle['open_price'], $latest_candle['close_price']);
|
|
||||||
$lower_wick = min($latest_candle['open_price'], $latest_candle['close_price']) - $latest_candle['low_price'];
|
|
||||||
if ($upper_wick > $body_size * 2 && $lower_wick < $body_size) {
|
|
||||||
$patterns['shooting_star'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Bearish Engulfing
|
|
||||||
if ($prev_candle['close_price'] > $prev_candle['open_price'] && // Previous candle is bullish
|
|
||||||
$latest_candle['open_price'] > $prev_candle['close_price'] && // Current candle opens above previous close
|
|
||||||
$latest_candle['close_price'] < $prev_candle['open_price']) { // Current candle closes below previous open
|
|
||||||
$patterns['bearish_engulfing'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Gravestone Doji
|
|
||||||
$is_doji = $body_size <= ($latest_candle['high_price'] - $latest_candle['low_price']) * 0.1;
|
|
||||||
if ($is_doji && $lower_wick < $body_size && $upper_wick > $body_size * 3) {
|
|
||||||
$patterns['gravestone_doji'] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Evening Star (3-candle pattern)
|
|
||||||
if (count($candles) >= 3) {
|
|
||||||
$c1 = $candles[2]; // Large bullish candle
|
|
||||||
$c2 = $candles[1]; // Small body candle (star)
|
|
||||||
$c3 = $candles[0]; // Bearish candle
|
|
||||||
if (($c1['close_price'] > $c1['open_price']) && (abs($c1['close_price'] - $c1['open_price']) > ($c1['high_price'] - $c1['low_price']) * 0.7) && // c1 is strong bullish
|
|
||||||
($c2['open_price'] > $c1['close_price']) && (abs($c2['close_price'] - $c2['open_price']) < ($c2['high_price'] - $c2['low_price']) * 0.3) && // c2 is small body, gapped up
|
|
||||||
($c3['close_price'] < $c3['open_price']) && ($c3['open_price'] < $c2['open_price']) && ($c3['close_price'] < $c1['close_price'])) { // c3 is bearish, closes below midpoint of c1
|
|
||||||
$patterns['evening_star'] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Double Top (Structural)
|
|
||||||
// This is a more complex pattern requiring finding two distinct peaks.
|
|
||||||
// We'll look for two highs that are close in price, separated by a valley.
|
|
||||||
if(count($candles) >= 15) {
|
|
||||||
$recent_highs = array_map(function($c) { return $c['high_price']; }, array_slice($candles, 0, 15));
|
|
||||||
$max_high = max($recent_highs);
|
|
||||||
$peaks = [];
|
|
||||||
foreach($candles as $i => $c) {
|
|
||||||
if ($i > 1 && $i < count($candles) -1 && $c['high_price'] >= $max_high * 0.98) {
|
|
||||||
if($candles[$i-1]['high_price'] < $c['high_price'] && $candles[$i+1]['high_price'] < $c['high_price']) {
|
|
||||||
$peaks[] = ['index' => $i, 'price' => $c['high_price']];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count($peaks) >= 2) {
|
|
||||||
// Check if the two most recent peaks form a double top
|
|
||||||
$peak1 = $peaks[1]; // More recent peak in time, but second in array
|
|
||||||
$peak2 = $peaks[0]; // Older peak
|
|
||||||
|
|
||||||
$price_diff = abs($peak1['price'] - $peak2['price']) / $peak2['price'];
|
|
||||||
$time_diff = $peak1['index'] - $peak2['index'];
|
|
||||||
|
|
||||||
// Peaks should be close in price, but not too close in time
|
|
||||||
if ($price_diff < 0.02 && $time_diff > 5) {
|
|
||||||
// Find the low point (valley) between the peaks
|
|
||||||
$valley_slice = array_slice($candles, $peak2['index'] + 1, $time_diff -1);
|
|
||||||
$valley_low = min(array_map(function($c){ return $c['low_price']; }, $valley_slice));
|
|
||||||
|
|
||||||
// Check if current price has broken below the valley low (neckline)
|
|
||||||
if($latest_candle['close_price'] < $valley_low) {
|
|
||||||
$patterns['double_top'] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$results = [];
|
|
||||||
foreach ($indicators as $indicator) {
|
|
||||||
switch ($indicator) {
|
|
||||||
case 'sma':
|
|
||||||
$results['sma'] = calculate_sma($symbol, $period);
|
|
||||||
break;
|
|
||||||
case 'rsi':
|
|
||||||
$results['rsi'] = calculate_rsi($symbol, 14);
|
|
||||||
break;
|
|
||||||
case 'macd':
|
|
||||||
$results['macd'] = calculate_macd($symbol);
|
|
||||||
break;
|
|
||||||
case 'patterns':
|
|
||||||
$results['patterns'] = calculate_patterns($symbol);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = [
|
|
||||||
'symbol' => $symbol,
|
|
||||||
'values' => $results,
|
|
||||||
'timestamp' => time()
|
|
||||||
];
|
|
||||||
|
|
||||||
echo json_encode($response);
|
|
||||||
116
api/ticker.php
116
api/ticker.php
@ -1,116 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
require_once __DIR__ . '/../db/config.php';
|
|
||||||
|
|
||||||
// --- Configuration ---
|
|
||||||
$symbols = ['bitcoin', 'ethereum']; // CoinGecko IDs
|
|
||||||
$exchange = 'CoinGecko';
|
|
||||||
$interval = '1d'; // CoinGecko provides daily data
|
|
||||||
|
|
||||||
// --- Data Fetching ---
|
|
||||||
function fetch_candlestick_data($symbol, $interval) {
|
|
||||||
$api_url = sprintf(
|
|
||||||
"https://api.coingecko.com/api/v3/coins/%s/ohlc?vs_currency=usd&days=1",
|
|
||||||
$symbol
|
|
||||||
);
|
|
||||||
|
|
||||||
$ch = curl_init();
|
|
||||||
curl_setopt_array($ch, [
|
|
||||||
CURLOPT_URL => $api_url,
|
|
||||||
CURLOPT_RETURNTRANSFER => true,
|
|
||||||
CURLOPT_TIMEOUT => 10,
|
|
||||||
CURLOPT_USERAGENT => 'FlatlogicMarketDetector/1.0'
|
|
||||||
]);
|
|
||||||
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
if ($http_code !== 200) {
|
|
||||||
error_log("CoinGecko API request failed with HTTP code {$http_code}: {$response}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = json_decode($response, true);
|
|
||||||
|
|
||||||
if (isset($data['error'])) {
|
|
||||||
error_log("CoinGecko API error: " . $data['error']);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 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, 0, 0, 0, 0, 0, 0)
|
|
||||||
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],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Main Execution ---
|
|
||||||
$latest_tickers = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
$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' => strtoupper($symbol),
|
|
||||||
'price' => $latest_kline[4], // Close price
|
|
||||||
'change_24h_percent' => 0, // Placeholder
|
|
||||||
'signal' => '-'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Output for Frontend ---
|
|
||||||
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.']);
|
|
||||||
}
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
/* Import Google Fonts */
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
color: #212529;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table thead th {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #6c757d;
|
|
||||||
border-bottom-width: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table tbody tr:hover {
|
|
||||||
background-color: #f1f3f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.symbol-logo {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fw-medium {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Price flash animations */
|
|
||||||
@keyframes flash-success-anim {
|
|
||||||
from { background-color: rgba(25, 135, 84, 0.2); }
|
|
||||||
to { background-color: transparent; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.flash-success {
|
|
||||||
animation: flash-success-anim 0.75s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes flash-danger-anim {
|
|
||||||
from { background-color: rgba(220, 53, 69, 0.2); }
|
|
||||||
to { background-color: transparent; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.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,173 +0,0 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
const POLLING_INTERVAL = 3000; // 3 seconds for price/analysis
|
|
||||||
const ALERT_POLLING_INTERVAL = 10000; // 10 seconds for alerts
|
|
||||||
|
|
||||||
const liveTickers = [
|
|
||||||
{ symbol: 'BITCOIN', elementId: 'live-crypto-row-btc', lastPrice: 0 },
|
|
||||||
{ symbol: 'ETHEREUM', elementId: 'live-crypto-row-eth', lastPrice: 0 }
|
|
||||||
];
|
|
||||||
|
|
||||||
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}`);
|
|
||||||
const data = await response.json();
|
|
||||||
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);
|
|
||||||
const priceCell = rowElement.querySelector('.price');
|
|
||||||
if (priceCell) priceCell.textContent = 'Error';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateRow(data, ticker, rowElement) {
|
|
||||||
const priceCell = rowElement.querySelector('.price');
|
|
||||||
const symbolCell = rowElement.querySelector('.symbol');
|
|
||||||
const exchangeCell = rowElement.querySelector('.exchange');
|
|
||||||
|
|
||||||
if (!priceCell || !symbolCell || !exchangeCell) {
|
|
||||||
console.error(`One or more required elements not found in row for ${ticker.symbol}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentPrice = parseFloat(data.price);
|
|
||||||
priceCell.textContent = `${currentPrice.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
||||||
symbolCell.textContent = data.symbol.replace('_SPBL', '');
|
|
||||||
exchangeCell.textContent = data.exchange;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchAnalysis(ticker, rowElement) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(`api/analysis.php?symbol=${ticker.symbol}&indicators=sma,rsi,macd&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}`);
|
|
||||||
updateAnalysisData(data, rowElement, ticker.lastPrice);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to fetch analysis for ${ticker.symbol}:`, error);
|
|
||||||
rowElement.querySelector('.sma').textContent = 'Error';
|
|
||||||
rowElement.querySelector('.rsi').textContent = 'Error';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateAnalysisData(data, rowElement, currentPrice) {
|
|
||||||
const smaCell = rowElement.querySelector('.sma');
|
|
||||||
const signalCell = rowElement.querySelector('.signal');
|
|
||||||
const rsiCell = rowElement.querySelector('.rsi');
|
|
||||||
const macdCell = rowElement.querySelector('.macd');
|
|
||||||
const macdSignalCell = rowElement.querySelector('.macd-signal');
|
|
||||||
if (!smaCell || !signalCell || !rsiCell || !macdCell || !macdSignalCell) return;
|
|
||||||
|
|
||||||
const sma = parseFloat(data.values.sma);
|
|
||||||
const rsi = parseFloat(data.values.rsi);
|
|
||||||
const macdData = data.values.macd;
|
|
||||||
|
|
||||||
if (!isNaN(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>';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
smaCell.textContent = '-';
|
|
||||||
signalCell.textContent = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNaN(rsi)) {
|
|
||||||
rsiCell.textContent = rsi.toFixed(2);
|
|
||||||
} else {
|
|
||||||
rsiCell.textContent = '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (macdData && typeof macdData.macd !== 'undefined' && typeof macdData.signal !== 'undefined') {
|
|
||||||
const macd = parseFloat(macdData.macd);
|
|
||||||
const macdSignal = parseFloat(macdData.signal);
|
|
||||||
if (!isNaN(macd)) {
|
|
||||||
macdCell.textContent = macd.toFixed(4);
|
|
||||||
}
|
|
||||||
if (!isNaN(macdSignal)) {
|
|
||||||
macdSignalCell.textContent = macdSignal.toFixed(4);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
macdCell.textContent = '-';
|
|
||||||
macdSignalCell.textContent = '-';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchAndDisplayAlerts() {
|
|
||||||
const alertsContainer = document.getElementById('alerts-container');
|
|
||||||
if (!alertsContainer) return;
|
|
||||||
|
|
||||||
let allAlerts = [];
|
|
||||||
|
|
||||||
for (const ticker of liveTickers) {
|
|
||||||
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();
|
|
||||||
if (data.alerts && data.alerts.length > 0) {
|
|
||||||
data.alerts.forEach(alert => {
|
|
||||||
allAlerts.push({ symbol: data.symbol, ...alert });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Failed to fetch alerts for ${ticker.symbol}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allAlerts.length > 0) {
|
|
||||||
alertsContainer.innerHTML = ''; // Clear previous alerts
|
|
||||||
allAlerts.forEach(alert => {
|
|
||||||
const alertEl = document.createElement('div');
|
|
||||||
alertEl.className = 'alert alert-danger d-flex align-items-center';
|
|
||||||
alertEl.innerHTML = `
|
|
||||||
<i class="bi bi-exclamation-triangle-fill me-3"></i>
|
|
||||||
<div>
|
|
||||||
<strong class="d-block">${alert.type} on ${alert.symbol}</strong>
|
|
||||||
${alert.message}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
alertsContainer.appendChild(alertEl);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
alertsContainer.innerHTML = '<p class="text-muted">No alerts triggered yet. The system is monitoring the market.</p>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchAll = () => {
|
|
||||||
fetchAndupdate(ticker, rowElement).then(() => {
|
|
||||||
fetchAnalysis(ticker, rowElement);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchAll();
|
|
||||||
setInterval(fetchAll, POLLING_INTERVAL);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Setup alert polling
|
|
||||||
fetchAndDisplayAlerts();
|
|
||||||
setInterval(fetchAndDisplayAlerts, ALERT_POLLING_INTERVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
initializeLiveTickers();
|
|
||||||
});
|
|
||||||
@ -1,9 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
// Generated by setup_mariadb_project.sh — edit as needed.
|
// Generated by setup_mariadb_project.sh — edit as needed.
|
||||||
define('DB_HOST', '127.0.0.1');
|
define('DB_HOST', '127.0.0.1');
|
||||||
define('DB_NAME', 'app_35761');
|
define('DB_NAME', 'app_35705');
|
||||||
define('DB_USER', 'app_35761');
|
define('DB_USER', 'app_35705');
|
||||||
define('DB_PASS', '119dd8a1-185f-46cf-b3d1-54072cb42357');
|
define('DB_PASS', 'cdc156d8-b1ec-4426-86fb-b1e546c1a442');
|
||||||
|
|
||||||
function db() {
|
function db() {
|
||||||
static $pdo;
|
static $pdo;
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once __DIR__ . '/../config.php';
|
|
||||||
|
|
||||||
try {
|
|
||||||
$pdo = db();
|
|
||||||
$sql = "
|
|
||||||
CREATE TABLE IF NOT EXISTS `price_history` (
|
|
||||||
`id` INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
`symbol` VARCHAR(20) NOT NULL,
|
|
||||||
`price` DECIMAL(18, 8) NOT NULL,
|
|
||||||
`timestamp` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
INDEX `symbol_timestamp_idx` (`symbol`, `timestamp`)
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
";
|
|
||||||
$pdo->exec($sql);
|
|
||||||
echo "Table 'price_history' created successfully or already exists." . PHP_EOL;
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
die("DB ERROR: " . $e->getMessage());
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
<?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);
|
|
||||||
}
|
|
||||||
260
index.php
260
index.php
@ -1,120 +1,150 @@
|
|||||||
<!DOCTYPE html>
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
@ini_set('display_errors', '1');
|
||||||
|
@error_reporting(E_ALL);
|
||||||
|
@date_default_timezone_set('UTC');
|
||||||
|
|
||||||
|
$phpVersion = PHP_VERSION;
|
||||||
|
$now = date('Y-m-d H:i:s');
|
||||||
|
?>
|
||||||
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>New Style</title>
|
||||||
<title>Crypto Market Alert System</title>
|
<?php
|
||||||
<meta name="description" content="A real-time crypto market crash and pump detector. Built with Flatlogic Generator.">
|
// Read project preview data from environment
|
||||||
<meta name="keywords" content="crypto market, pump detector, crash alert, futures trading, perpetuals, RSI, MACD, trading signals, Bitget, KuCoin, real-time crypto, Built with Flatlogic Generator">
|
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||||
|
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||||
<!-- Open Graph / Facebook -->
|
?>
|
||||||
<meta property="og:type" content="website">
|
<?php if ($projectDescription): ?>
|
||||||
<meta property="og:title" content="Crypto Market Alert System">
|
<!-- Meta description -->
|
||||||
<meta property="og:description" content="A real-time crypto market crash and pump detector. Built with Flatlogic Generator.">
|
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||||
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
<!-- Open Graph meta tags -->
|
||||||
|
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||||
<!-- Twitter -->
|
<!-- Twitter meta tags -->
|
||||||
<meta property="twitter:card" content="summary_large_image">
|
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||||
<meta property="twitter:title" content="Crypto Market Alert System">
|
<?php endif; ?>
|
||||||
<meta property="twitter:description" content="A real-time crypto market crash and pump detector. Built with Flatlogic Generator.">
|
<?php if ($projectImageUrl): ?>
|
||||||
<meta property="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
<!-- Open Graph image -->
|
||||||
|
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||||
<!-- Styles -->
|
<!-- Twitter image -->
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
|
<?php endif; ?>
|
||||||
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg-color-start: #6a11cb;
|
||||||
|
--bg-color-end: #2575fc;
|
||||||
|
--text-color: #ffffff;
|
||||||
|
--card-bg-color: rgba(255, 255, 255, 0.01);
|
||||||
|
--card-border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
||||||
|
color: var(--text-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
||||||
|
animation: bg-pan 20s linear infinite;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
@keyframes bg-pan {
|
||||||
|
0% { background-position: 0% 0%; }
|
||||||
|
100% { background-position: 100% 100%; }
|
||||||
|
}
|
||||||
|
main {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
background: var(--card-bg-color);
|
||||||
|
border: 1px solid var(--card-border-color);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
-webkit-backdrop-filter: blur(20px);
|
||||||
|
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.loader {
|
||||||
|
margin: 1.25rem auto 1.25rem;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border: 3px solid rgba(255, 255, 255, 0.25);
|
||||||
|
border-top-color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
.hint {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px; height: 1px;
|
||||||
|
padding: 0; margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap; border: 0;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
letter-spacing: -1px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background: rgba(0,0,0,0.2);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 1rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<main>
|
||||||
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
|
<div class="card">
|
||||||
<div class="container-fluid">
|
<h1>Analyzing your requirements and generating your website…</h1>
|
||||||
<a class="navbar-brand" href="#">
|
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
||||||
<i class="bi bi-graph-up-arrow text-primary"></i>
|
<span class="sr-only">Loading…</span>
|
||||||
Crypto Market Detector
|
</div>
|
||||||
</a>
|
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
||||||
</div>
|
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
||||||
</nav>
|
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
||||||
|
</div>
|
||||||
<main class="container my-5">
|
</main>
|
||||||
<div class="card">
|
<footer>
|
||||||
<div class="card-header bg-white border-bottom-0 pt-4 pb-0">
|
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||||
<h4 class="card-title fw-bold">Live Market Signals</h4>
|
</footer>
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-borderless">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">EXCHANGE</th>
|
|
||||||
<th scope="col">SYMBOL</th>
|
|
||||||
<th scope="col">PRICE</th>
|
|
||||||
<th scope="col">SMA (20m)</th>
|
|
||||||
<th scope="col">SIGNAL</th>
|
|
||||||
<th scope="col">RSI (14m)</th>
|
|
||||||
<th scope="col">MACD</th>
|
|
||||||
<th scope="col">MACD Signal</th>
|
|
||||||
<th scope="col" class="text-end">STATUS</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<!-- BTC Row (Updated by JavaScript) -->
|
|
||||||
<tr id="live-crypto-row-btc">
|
|
||||||
<td class="fw-medium text-muted exchange">Binance</td>
|
|
||||||
<td>
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<img src="https://via.placeholder.com/24/f0b90b/000000?Text=B" class="symbol-logo rounded-circle" alt="BTC">
|
|
||||||
<span class="fw-bold symbol">BITCOIN</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="fw-bold fs-5 price">$0.00</td>
|
|
||||||
<td class="fw-medium sma">-</td>
|
|
||||||
<td class="fw-medium signal">-</td>
|
|
||||||
<td class="fw-medium rsi">-</td>
|
|
||||||
<td class="fw-medium macd">-</td>
|
|
||||||
<td class="fw-medium macd-signal">-</td>
|
|
||||||
<td class="text-end status">-</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<!-- ETH Row (Updated by JavaScript) -->
|
|
||||||
<tr id="live-crypto-row-eth">
|
|
||||||
<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">
|
|
||||||
<span class="fw-bold symbol">ETHEREUM</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td class="fw-bold fs-5 price">$0.00</td>
|
|
||||||
<td class="fw-medium sma">-</td>
|
|
||||||
<td class="fw-medium signal">-</td>
|
|
||||||
<td class="fw-medium rsi">-</td>
|
|
||||||
<td class="fw-medium macd">-</td>
|
|
||||||
<td class="fw-medium macd-signal">-</td>
|
|
||||||
<td class="text-end status">-</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card mt-4">
|
|
||||||
<div class="card-header bg-white border-bottom-0 pt-4 pb-0">
|
|
||||||
<h4 class="card-title fw-bold">Triggered Alerts</h4>
|
|
||||||
</div>
|
|
||||||
<div class="card-body" id="alerts-container">
|
|
||||||
<p class="text-muted">No alerts triggered yet. The system is monitoring the market.</p>
|
|
||||||
<!-- Alerts will be dynamically inserted here -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<footer class="text-center text-muted py-4">
|
|
||||||
<small>Built with Flatlogic</small>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user