Compare commits

..

No commits in common. "ai-dev" and "master" have entirely different histories.

9 changed files with 148 additions and 843 deletions

View File

@ -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()
]);

View File

@ -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);

View File

@ -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.']);
}

View File

@ -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;
}

View File

@ -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();
});

View File

@ -1,9 +1,9 @@
<?php
// Generated by setup_mariadb_project.sh — edit as needed.
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'app_35761');
define('DB_USER', 'app_35761');
define('DB_PASS', '119dd8a1-185f-46cf-b3d1-54072cb42357');
define('DB_NAME', 'app_35705');
define('DB_USER', 'app_35705');
define('DB_PASS', 'cdc156d8-b1ec-4426-86fb-b1e546c1a442');
function db() {
static $pdo;

View File

@ -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());
}

View File

@ -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);
}

248
index.php
View File

@ -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">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Crypto Market Alert System</title>
<meta name="description" content="A real-time crypto market crash and pump detector. Built with Flatlogic Generator.">
<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">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="Crypto Market Alert System">
<meta property="og:description" content="A real-time crypto market crash and pump detector. Built with Flatlogic Generator.">
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:title" content="Crypto Market Alert System">
<meta property="twitter:description" content="A real-time crypto market crash and pump detector. Built with Flatlogic Generator.">
<meta property="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
<!-- Styles -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
?>
<?php if ($projectDescription): ?>
<!-- Meta description -->
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags -->
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<!-- Twitter meta tags -->
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
<?php endif; ?>
<?php if ($projectImageUrl): ?>
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<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>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
<div class="container-fluid">
<a class="navbar-brand" href="#">
<i class="bi bi-graph-up-arrow text-primary"></i>
Crypto Market Detector
</a>
</div>
</nav>
<main class="container my-5">
<main>
<div class="card">
<div class="card-header bg-white border-bottom-0 pt-4 pb-0">
<h4 class="card-title fw-bold">Live Market Signals</h4>
</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 -->
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer class="text-center text-muted py-4">
<small>Built with Flatlogic</small>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</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>
</html>