signal-engine / static /index.html
josephrw's picture
Upload folder using huggingface_hub
f201be6 verified
Raw
History Blame Contribute Delete
31.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Signal Engine - AI Trading Intelligence</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet">
<style>
* {
font-family: 'Space Grotesk', sans-serif;
}
.mono {
font-family: 'JetBrains Mono', monospace;
}
:root {
--bg-primary: #0f0f0f;
--bg-secondary: #1a1a1a;
--bg-tertiary: #252525;
--accent: #3a3a3a;
--accent-secondary: #4a4a4a;
--text-primary: #ffffff;
--text-secondary: #999999;
}
body {
background: var(--bg-primary);
color: var(--text-primary);
overflow-x: hidden;
}
.neon-glow {
box-shadow: 0 0 20px rgba(255, 215, 0, 0.3), 0 0 40px rgba(255, 215, 0, 0.1);
}
.neon-text {
text-shadow: 0 0 10px rgba(255, 215, 0, 0.5), 0 0 20px rgba(255, 215, 0, 0.3);
}
.glass-card {
background: rgba(18, 18, 26, 0.6);
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 215, 0, 0.1);
border-radius: 24px;
}
.gradient-border {
position: relative;
background: var(--bg-secondary);
border-radius: 24px;
}
.gradient-border::before {
content: '';
position: absolute;
inset: -2px;
border-radius: 26px;
background: linear-gradient(135deg, var(--accent), var(--accent-secondary), #ff00ff);
z-index: -1;
opacity: 0.5;
filter: blur(8px);
}
@keyframes float {
0%,
100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}
@keyframes pulse-ring {
0% {
transform: scale(0.8);
opacity: 1;
}
100% {
transform: scale(1.5);
opacity: 0;
}
}
.float-animation {
animation: float 3s ease-in-out infinite;
}
.pulse-ring {
position: relative;
}
.pulse-ring::before {
content: '';
position: absolute;
inset: -4px;
border-radius: 50%;
border: 2px solid var(--accent);
animation: pulse-ring 2s infinite;
}
.cyber-grid {
background-image:
linear-gradient(rgba(255, 215, 0, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 215, 0, 0.03) 1px, transparent 1px);
background-size: 50px 50px;
}
.btn-cyber {
background: linear-gradient(135deg, var(--accent) 0%, var(--accent-secondary) 100%);
border: none;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.btn-cyber::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
transition: left 0.5s ease;
}
.btn-cyber:hover::before {
left: 100%;
}
.btn-cyber:hover {
transform: scale(1.05);
box-shadow: 0 0 30px rgba(255, 215, 0, 0.5);
}
.stat-card {
background: linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, rgba(255, 140, 0, 0.05) 100%);
border: 1px solid rgba(255, 215, 0, 0.2);
}
.prediction-card {
background: linear-gradient(135deg, rgba(255, 0, 255, 0.1) 0%, rgba(255, 140, 0, 0.05) 100%);
border: 1px solid rgba(255, 0, 255, 0.2);
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.loader {
border: 3px solid rgba(255, 215, 0, 0.1);
border-top: 3px solid var(--accent);
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
</head>
<body class="min-h-screen cyber-grid">
<!-- Auth Modal -->
<div id="authModal" class="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center hidden">
<div class="glass-card p-8 max-w-md w-full mx-4">
<h2 class="text-3xl font-bold mb-6 neon-text">ZK Login</h2>
<div class="space-y-4">
<div class="text-center text-sm text-[var(--text-secondary)] mb-6">
Zero-Knowledge Proof Authentication
</div>
<button onclick="initZKLogin()" class="w-full btn-cyber py-4 rounded-xl font-bold text-lg text-black">
🛡️ LOGIN WITH ZK
</button>
</div>
</div>
</div>
<!-- Main App -->
<div id="mainApp" class="hidden">
<!-- Header -->
<header class="fixed top-0 left-0 right-0 z-40 glass-card mx-4 mt-4 px-6 py-4">
<div class="flex items-center justify-between">
<div class="flex items-center space-x-4">
<div
class="w-12 h-12 bg-gradient-to-br from-[var(--accent)] to-[var(--accent-secondary)] rounded-xl flex items-center justify-center pulse-ring">
<svg class="w-6 h-6 text-black" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 10V3L4 14h7v7l9-11h-7z"></path>
</svg>
</div>
<div>
<h1 class="text-2xl font-bold neon-text">SIGNAL ENGINE</h1>
<p class="text-xs text-[var(--text-secondary)]">AI-POWERED TRADING INTELLIGENCE</p>
</div>
</div>
<div class="flex items-center space-x-4">
<div class="hidden md:flex items-center space-x-2 bg-[var(--bg-tertiary)] px-4 py-2 rounded-xl">
<div class="w-2 h-2 bg-[var(--accent)] rounded-full animate-pulse"></div>
<span class="text-sm text-[var(--text-secondary)]">SYSTEM ONLINE</span>
</div>
<div id="userInfo" class="text-sm mono text-[var(--accent)]"></div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="pt-32 px-4 pb-8 max-w-7xl mx-auto">
<!-- Hero Stats -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<div class="stat-card p-6 rounded-2xl float-animation" style="animation-delay: 0s;">
<div class="text-[var(--text-secondary)] text-sm mb-2">TOTAL SIGNALS</div>
<div class="text-4xl font-bold mono" id="totalSignals">0</div>
<div class="text-[var(--accent)] text-sm mt-1">+24 today</div>
</div>
<div class="stat-card p-6 rounded-2xl float-animation" style="animation-delay: 0.1s;">
<div class="text-[var(--text-secondary)] text-sm mb-2">ACCURACY</div>
<div class="text-4xl font-bold mono text-[var(--accent)]" id="accuracy">0%</div>
<div class="text-[var(--accent)] text-sm mt-1">Top 5%</div>
</div>
<div class="stat-card p-6 rounded-2xl float-animation" style="animation-delay: 0.2s;">
<div class="text-[var(--text-secondary)] text-sm mb-2">ACTIVE USERS</div>
<div class="text-4xl font-bold mono" id="activeUsers">0</div>
<div class="text-[var(--accent)] text-sm mt-1">Growing</div>
</div>
<div class="stat-card p-6 rounded-2xl float-animation" style="animation-delay: 0.3s;">
<div class="text-[var(--text-secondary)] text-sm mb-2">MARKET CAP</div>
<div class="text-4xl font-bold mono">$2.4T</div>
<div class="text-[var(--accent)] text-sm mt-1">Crypto</div>
</div>
</div>
<!-- Main Grid -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Chart Section -->
<div class="lg:col-span-2 glass-card p-6">
<div class="flex items-center justify-between mb-6">
<div>
<h2 class="text-xl font-bold">BTC/USDT</h2>
<div class="flex items-center space-x-2 mt-1">
<span class="text-3xl font-bold mono text-[var(--accent)]" id="btcPrice">$0.00</span>
<span class="text-[var(--accent)] text-sm" id="btcChange">+0.00%</span>
</div>
</div>
<div class="flex space-x-2">
<button
class="px-4 py-2 bg-[var(--bg-tertiary)] rounded-lg text-sm hover:bg-[var(--accent)] hover:text-black transition-colors">1H</button>
<button
class="px-4 py-2 bg-[var(--accent)] text-black rounded-lg text-sm font-semibold">4H</button>
<button
class="px-4 py-2 bg-[var(--bg-tertiary)] rounded-lg text-sm hover:bg-[var(--accent)] hover:text-black transition-colors">1D</button>
</div>
</div>
<div class="h-80">
<canvas id="priceChart"></canvas>
</div>
</div>
<!-- Live Predictions -->
<div class="glass-card p-6">
<div class="flex items-center justify-between mb-6">
<h2 class="text-xl font-bold">LIVE SIGNALS</h2>
<div class="flex items-center space-x-2">
<div class="w-2 h-2 bg-[var(--accent)] rounded-full animate-pulse"></div>
<span class="text-xs text-[var(--text-secondary)]">REAL-TIME</span>
</div>
</div>
<div id="predictionsList" class="space-y-4 max-h-80 overflow-y-auto scrollbar-hide">
<div class="text-center text-[var(--text-secondary)] py-8">
<div class="loader mx-auto mb-4"></div>
Loading signals...
</div>
</div>
</div>
</div>
<!-- Prediction Generator -->
<div class="mt-6 glass-card p-6">
<h2 class="text-xl font-bold mb-6 neon-text">GENERATE PREDICTION</h2>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div>
<label class="text-sm text-[var(--text-secondary)] mb-2 block">Symbol</label>
<select id="symbolSelect"
class="w-full bg-[var(--bg-tertiary)] border border-[var(--accent)]/30 rounded-xl px-4 py-3 focus:border-[var(--accent)] outline-none">
<option value="BTC_USDT">BTC/USDT</option>
<option value="ETH_USDT">ETH/USDT</option>
<option value="SOL_USDT">SOL/USDT</option>
<option value="DOGE_USDT">DOGE/USDT</option>
</select>
</div>
<div>
<label class="text-sm text-[var(--text-secondary)] mb-2 block">Timeframe</label>
<select id="timeframeSelect"
class="w-full bg-[var(--bg-tertiary)] border border-[var(--accent)]/30 rounded-xl px-4 py-3 focus:border-[var(--accent)] outline-none">
<option value="1h">1 Hour</option>
<option value="4h">4 Hours</option>
<option value="1d">1 Day</option>
</select>
</div>
<div>
<label class="text-sm text-[var(--text-secondary)] mb-2 block">API Key</label>
<input type="text" id="apiKeyInput"
class="w-full bg-[var(--bg-tertiary)] border border-[var(--accent)]/30 rounded-xl px-4 py-3 focus:border-[var(--accent)] outline-none mono"
placeholder="Enter your API key">
</div>
<div class="flex items-end">
<button onclick="generatePrediction()"
class="w-full btn-cyber py-3 rounded-xl font-bold text-black">
GENERATE
</button>
</div>
</div>
<div id="predictionResult" class="mt-6 hidden">
<!-- Result will appear here -->
</div>
</div>
<!-- Subscription Section -->
<div class="mt-6 glass-card p-6">
<h2 class="text-xl font-bold mb-6 neon-text">SMART CONTRACT SUBSCRIPTION</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-[var(--bg-tertiary)] p-6 rounded-xl border border-[var(--accent)]/20">
<div class="text-[var(--text-secondary)] text-sm mb-2">FREE</div>
<div class="text-3xl font-bold">$0</div>
<ul class="text-sm text-[var(--text-secondary)] mt-4 space-y-2">
<li>• 100 requests/hour</li>
<li>• Basic predictions</li>
<li>• Public signals</li>
</ul>
<button
class="w-full mt-4 py-2 bg-[var(--bg-primary)] border border-[var(--accent)]/30 rounded-lg text-sm">Current</button>
</div>
<div
class="bg-gradient-to-b from-[var(--accent)]/10 to-[var(--accent-secondary)]/10 p-6 rounded-xl border border-[var(--accent)]">
<div class="text-[var(--accent)] text-sm mb-2 font-bold">PRO</div>
<div class="text-3xl font-bold">$49<span class="text-lg text-[var(--text-secondary)]">/mo</span>
</div>
<ul class="text-sm text-[var(--text-secondary)] mt-4 space-y-2">
<li>• 1000 requests/hour</li>
<li>• Advanced predictions</li>
<li>• Symbol leaderboard</li>
<li>• Priority support</li>
</ul>
<button onclick="purchaseSubscription('PRO', 1)"
class="w-full mt-4 btn-cyber py-2 rounded-lg font-bold text-black">UPGRADE</button>
</div>
<div class="bg-[var(--bg-tertiary)] p-6 rounded-xl border border-[var(--accent)]/20">
<div class="text-[var(--text-secondary)] text-sm mb-2">ENTERPRISE</div>
<div class="text-3xl font-bold">$500<span
class="text-lg text-[var(--text-secondary)]">/mo</span></div>
<ul class="text-sm text-[var(--text-secondary)] mt-4 space-y-2">
<li>• Unlimited requests</li>
<li>• Custom models</li>
<li>• Dedicated support</li>
<li>• SLA guarantee</li>
</ul>
<button onclick="purchaseSubscription('ENTERPRISE', 1)"
class="w-full mt-4 py-2 bg-[var(--bg-primary)] border border-[var(--accent)]/30 rounded-lg text-sm hover:border-[var(--accent)] transition-colors">CONTACT</button>
</div>
</div>
</div>
<!-- Contract Verification -->
<div class="mt-6 glass-card p-6">
<h2 class="text-xl font-bold mb-6">ON-CHAIN VERIFICATION</h2>
<div class="flex space-x-4">
<input type="text" id="batchIdInput"
class="flex-1 bg-[var(--bg-tertiary)] border border-[var(--accent)]/30 rounded-xl px-4 py-3 focus:border-[var(--accent)] outline-none mono"
placeholder="Enter batch ID">
<button onclick="verifyPrediction()"
class="btn-cyber px-8 py-3 rounded-xl font-bold text-black">VERIFY</button>
</div>
<div id="verificationResult" class="mt-4 hidden"></div>
</div>
</main>
</div>
<script>
let apiKey = localStorage.getItem('apiKey') || '';
let walletAddress = localStorage.getItem('walletAddress') || '';
let priceChart = null;
let priceData = { labels: [], prices: [] };
// Initialize
document.addEventListener('DOMContentLoaded', () => {
if (apiKey || walletAddress) {
showMainApp();
} else {
document.getElementById('authModal').classList.remove('hidden');
}
initChart();
fetchMarketData();
setInterval(fetchMarketData, 3000);
});
function showMainApp() {
document.getElementById('authModal').classList.add('hidden');
document.getElementById('mainApp').classList.remove('hidden');
if (walletAddress) {
document.getElementById('userInfo').textContent = walletAddress.slice(0, 6) + '...' + walletAddress.slice(-4);
}
document.getElementById('apiKeyInput').value = apiKey;
// Update stats
document.getElementById('totalSignals').textContent = '12,847';
document.getElementById('accuracy').textContent = '68.4%';
document.getElementById('activeUsers').textContent = '2,456';
}
async function initZKLogin() {
try {
// Generate random challenge
const challenge = Array.from(crypto.getRandomValues(new Uint8Array(32)))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
// In production, this would use a real ZK library like iden3-js
// For demo, we'll simulate ZK proof generation
const zkProof = await generateZKProof(challenge);
const response = await fetch('/v1/auth/zk', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
challenge: challenge,
proof: zkProof,
public_key: zkProof.public_key
})
});
const data = await response.json();
if (response.ok) {
apiKey = data.api_key;
localStorage.setItem('apiKey', apiKey);
localStorage.setItem('zkIdentity', data.identity);
showMainApp();
} else {
alert(data.detail || 'ZK login failed');
}
} catch (error) {
console.error('ZK login error:', error);
alert('ZK login failed: ' + error.message);
}
}
async function generateZKProof(challenge) {
// Simplified ZK proof generation (demo)
// In production, use iden3-js or similar library
const privateKey = Array.from(crypto.getRandomValues(new Uint8Array(32)))
.map(b => b.toString(16).padStart(2, '0'))
.join('');
const publicKey = derivePublicKey(privateKey);
// Create proof (simplified - real ZK would use SNARKs/STARKs)
const proof = {
public_key: publicKey,
challenge: challenge,
signature: await signChallenge(privateKey, challenge),
timestamp: Date.now()
};
return proof;
}
function derivePublicKey(privateKey) {
// Simplified public key derivation
let hash = 0;
for (let i = 0; i < privateKey.length; i++) {
hash = ((hash << 5) - hash) + privateKey.charCodeAt(i);
hash = hash & hash;
}
return '0x' + Math.abs(hash).toString(16).padStart(40, '0');
}
async function signChallenge(privateKey, challenge) {
// Simplified signing (use real crypto in production)
const encoder = new TextEncoder();
const data = encoder.encode(privateKey + challenge);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return '0x' + hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
function initChart() {
const ctx = document.getElementById('priceChart').getContext('2d');
// Generate initial data
for (let i = 0; i < 50; i++) {
priceData.labels.push(i);
priceData.prices.push(67000 + Math.random() * 1000);
}
priceChart = new Chart(ctx, {
type: 'line',
data: {
labels: priceData.labels,
datasets: [{
data: priceData.prices,
borderColor: '#00ff88',
backgroundColor: 'rgba(255, 215, 0, 0.1)',
fill: true,
tension: 0.4,
pointRadius: 0,
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: {
x: { display: false },
y: {
grid: { color: 'rgba(255, 215, 0, 0.1)' },
ticks: { color: '#888899' }
}
}
}
});
}
async function fetchMarketData() {
try {
const response = await fetch('/v1/market/tickers');
const data = await response.json();
if (data.tickers && data.tickers.length > 0) {
updateMarketOverview(data.tickers);
const btc = data.tickers.find(t => t.symbol === 'BTC/USDT');
if (btc) {
document.getElementById('btcPrice').textContent = '$' + btc.price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
updateChart(btc.price);
}
}
} catch (error) {
console.error('Market data error:', error);
}
}
function updateMarketOverview(tickers) {
const container = document.getElementById('marketOverview');
container.innerHTML = tickers.map(t => `
<div class="bg-[var(--bg-tertiary)] p-4 rounded-xl border border-[var(--accent)]/10 hover:border-[var(--accent)]/30 transition-colors">
<div class="font-bold">${t.symbol}</div>
<div class="mono text-[var(--accent)]">$${t.price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 4 })}</div>
</div>
`).join('');
}
function updateChart(price) {
priceData.labels.push(priceData.labels.length);
priceData.prices.push(price);
if (priceData.labels.length > 50) {
priceData.labels.shift();
priceData.prices.shift();
}
priceChart.data.labels = priceData.labels;
priceChart.data.datasets[0].data = priceData.prices;
priceChart.update('none');
}
async function generatePrediction() {
const symbol = document.getElementById('symbolSelect').value;
const timeframe = document.getElementById('timeframeSelect').value;
const userApiKey = document.getElementById('apiKeyInput').value;
if (!userApiKey) {
alert('Please enter your API key');
return;
}
const resultDiv = document.getElementById('predictionResult');
resultDiv.innerHTML = '<div class="loader mx-auto"></div>';
resultDiv.classList.remove('hidden');
try {
const response = await fetch('/v1/predictions/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': userApiKey
},
body: JSON.stringify({ symbol, timeframe })
});
const data = await response.json();
if (response.ok) {
resultDiv.innerHTML = `
<div class="prediction-card p-6 rounded-xl">
<div class="flex items-center justify-between mb-4">
<h3 class="text-xl font-bold">${data.symbol.replace('_', '/')}</h3>
<span class="px-4 py-2 rounded-full text-sm font-bold ${data.direction === 'LONG' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'}">
${data.direction}
</span>
</div>
<div class="grid grid-cols-3 gap-4">
<div>
<div class="text-[var(--text-secondary)] text-sm">Confidence</div>
<div class="text-2xl font-bold mono text-[var(--accent)]">${(data.confidence * 100).toFixed(1)}%</div>
</div>
<div>
<div class="text-[var(--text-secondary)] text-sm">Entry Price</div>
<div class="text-2xl font-bold mono">$${data.entry_price.toLocaleString()}</div>
</div>
<div>
<div class="text-[var(--text-secondary)] text-sm">Position</div>
<div class="text-2xl font-bold mono">${(data.suggested_position * 100).toFixed(0)}%</div>
</div>
</div>
<div class="mt-4 text-sm text-[var(--text-secondary)]">
Target: ${new Date(data.target_hour).toLocaleString()}
</div>
</div>
`;
} else {
resultDiv.innerHTML = `<div class="text-red-400">${data.detail || 'Prediction failed'}</div>`;
}
} catch (error) {
resultDiv.innerHTML = `<div class="text-red-400">Error: ${error.message}</div>`;
}
}
async function purchaseSubscription(tier, duration) {
if (!apiKey) {
alert('Please login first');
return;
}
try {
const response = await fetch('/v1/contract/purchase', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify({ tier, duration })
});
const data = await response.json();
if (response.ok) {
alert(`Subscription upgraded to ${tier}!`);
} else {
alert(data.detail || 'Purchase failed');
}
} catch (error) {
alert('Error: ' + error.message);
}
}
async function verifyPrediction() {
const batchId = document.getElementById('batchIdInput').value;
if (!batchId) {
alert('Please enter a batch ID');
return;
}
const resultDiv = document.getElementById('verificationResult');
resultDiv.innerHTML = '<div class="loader mx-auto"></div>';
resultDiv.classList.remove('hidden');
try {
const response = await fetch(`/v1/contract/predictions/${batchId}`);
const data = await response.json();
if (response.ok) {
resultDiv.innerHTML = `
<div class="prediction-card p-4 rounded-xl">
<div class="flex items-center space-x-2">
<div class="w-3 h-3 ${data.verified ? 'bg-green-400' : 'bg-red-400'} rounded-full"></div>
<span class="font-bold">${data.verified ? 'VERIFIED' : 'NOT VERIFIED'}</span>
</div>
<div class="text-sm text-[var(--text-secondary)] mt-2">
Batch ID: ${data.batchId}<br>
Timestamp: ${new Date(data.timestamp * 1000).toLocaleString()}
</div>
</div>
`;
} else {
resultDiv.innerHTML = `<div class="text-red-400">${data.detail || 'Verification failed'}</div>`;
}
} catch (error) {
resultDiv.innerHTML = `<div class="text-red-400">Error: ${error.message}</div>`;
}
}
</script>
</body>
</html>