| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Wall Street Watchdog AI</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| @keyframes pulse { |
| 0%, 100% { opacity: 1; } |
| 50% { opacity: 0.5; } |
| } |
| .animate-pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } |
| .card-hover:hover { |
| transform: translateY(-5px); |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); |
| } |
| .ticker-badge { |
| position: absolute; |
| top: -10px; |
| right: -10px; |
| width: 40px; |
| height: 40px; |
| border-radius: 50%; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-weight: bold; |
| font-size: 0.9rem; |
| } |
| .action-buy { background-color: rgba(74, 222, 128, 0.9); } |
| .action-sell { background-color: rgba(248, 113, 113, 0.9); } |
| .action-hold { background-color: rgba(251, 191, 36, 0.9); } |
| .action-news { background-color: rgba(96, 165, 250, 0.9); } |
| .action-upgrade { background-color: rgba(167, 243, 208, 0.9); color: #065f46; } |
| .action-downgrade { background-color: rgba(254, 202, 202, 0.9); color: #991b1b; } |
| .skeleton-loader { |
| background: linear-gradient(90deg, #1a1a1a 25%, #2d2d2d 50%, #1a1a1a 75%); |
| background-size: 200% 100%; |
| animation: shimmer 1.5s infinite linear; |
| } |
| @keyframes shimmer { |
| 0% { background-position: 200% 0; } |
| 100% { background-position: -200% 0; } |
| } |
| </style> |
| </head> |
| <body class="bg-black text-white min-h-screen"> |
| <div class="container mx-auto px-4 py-8"> |
| |
| <header class="mb-8"> |
| <div class="flex justify-between items-center"> |
| <div> |
| <h1 class="text-3xl md:text-4xl font-bold bg-gradient-to-r from-green-400 to-blue-500 bg-clip-text text-transparent"> |
| Wall Street Watchdog AI |
| </h1> |
| <p class="text-gray-400 mt-2">Real-time market intelligence powered by AI</p> |
| </div> |
| <div class="flex items-center space-x-4"> |
| <div class="hidden md:block"> |
| <div class="text-sm text-gray-400">Last updated</div> |
| <div id="lastUpdated" class="text-sm font-mono">Just now</div> |
| </div> |
| <button id="refreshBtn" class="bg-gray-800 hover:bg-gray-700 text-white px-4 py-2 rounded-lg flex items-center space-x-2 transition-all"> |
| <i class="fas fa-sync-alt" id="refreshIcon"></i> |
| <span class="hidden md:inline">Refresh</span> |
| </button> |
| </div> |
| </div> |
| |
| <div class="mt-6 flex flex-wrap items-center justify-between gap-4"> |
| <div class="flex items-center space-x-4"> |
| <div class="bg-gray-800 px-3 py-1 rounded-full text-sm flex items-center"> |
| <span class="w-2 h-2 rounded-full bg-green-500 mr-2 animate-pulse"></span> |
| <span id="alertCount">0</span> active alerts |
| </div> |
| <div class="hidden md:block text-sm text-gray-400"> |
| Auto-refresh in <span id="countdown">300</span>s |
| </div> |
| </div> |
| |
| <div class="flex space-x-2"> |
| <button class="filter-btn active bg-gray-800 px-3 py-1 rounded-full text-sm" data-filter="all">All</button> |
| <button class="filter-btn bg-gray-800 px-3 py-1 rounded-full text-sm" data-filter="buy">Buy</button> |
| <button class="filter-btn bg-gray-800 px-3 py-1 rounded-full text-sm" data-filter="sell">Sell</button> |
| <button class="filter-btn bg-gray-800 px-3 py-1 rounded-full text-sm" data-filter="news">News</button> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <main> |
| |
| <div id="loadingState" class="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> |
| <div class="bg-gray-900 rounded-xl p-4 h-64 skeleton-loader"></div> |
| <div class="bg-gray-900 rounded-xl p-4 h-64 skeleton-loader"></div> |
| <div class="bg-gray-900 rounded-xl p-4 h-64 skeleton-loader hidden md:block"></div> |
| </div> |
|
|
| |
| <div id="alertsGrid" class="grid md:grid-cols-2 lg:grid-cols-3 gap-6 hidden"> |
| |
| </div> |
|
|
| |
| <div id="emptyState" class="text-center py-16 hidden"> |
| <i class="fas fa-bell-slash text-4xl text-gray-600 mb-4"></i> |
| <h3 class="text-xl font-medium text-gray-300">No alerts found</h3> |
| <p class="text-gray-500 mt-2">Try refreshing or check back later for updates</p> |
| </div> |
| </main> |
|
|
| |
| <footer class="mt-12 pt-6 border-t border-gray-800 text-center text-gray-500 text-sm"> |
| <p>Wall Street Watchdog AI © 2023 - Real-time market monitoring</p> |
| <p class="mt-1">Data sources: Yahoo Finance, MarketBeat, CoinDesk</p> |
| </footer> |
| </div> |
|
|
| <script> |
| |
| const mockAlerts = [ |
| { |
| ticker: "AAPL", |
| action: "Upgrade", |
| summary: "Apple received an upgrade to Buy from Neutral at UBS with a price target of $210, citing strong iPhone 15 demand.", |
| source: "MarketBeat", |
| date: new Date().toISOString() |
| }, |
| { |
| ticker: "TSLA", |
| action: "Downgrade", |
| summary: "Tesla downgraded to Hold from Buy at Morgan Stanley due to valuation concerns after recent rally.", |
| source: "MarketBeat", |
| date: new Date().toISOString() |
| }, |
| { |
| ticker: "NVDA", |
| action: "Buy", |
| summary: "Nvidia maintains Buy rating at Goldman Sachs with $650 target, as AI chip demand continues to exceed supply.", |
| source: "MarketBeat", |
| date: new Date().toISOString() |
| }, |
| { |
| ticker: "CRYPTO", |
| action: "News", |
| summary: "Bitcoin surges past $45,000 as spot ETF approvals appear imminent, according to industry analysts.", |
| source: "CoinDesk", |
| date: new Date().toISOString() |
| }, |
| { |
| ticker: "AMZN", |
| action: "News", |
| summary: "Amazon Web Services announces new AI tools and partnerships at re:Invent conference, boosting cloud growth prospects.", |
| source: "Yahoo Finance", |
| date: new Date().toISOString() |
| }, |
| { |
| ticker: "META", |
| action: "Hold", |
| summary: "Meta Platforms rating maintained at Equal Weight by Barclays, awaiting clearer signs of Reality Labs profitability.", |
| source: "MarketBeat", |
| date: new Date().toISOString() |
| } |
| ]; |
| |
| |
| const alertsGrid = document.getElementById('alertsGrid'); |
| const loadingState = document.getElementById('loadingState'); |
| const emptyState = document.getElementById('emptyState'); |
| const refreshBtn = document.getElementById('refreshBtn'); |
| const refreshIcon = document.getElementById('refreshIcon'); |
| const countdownEl = document.getElementById('countdown'); |
| const lastUpdatedEl = document.getElementById('lastUpdated'); |
| const alertCountEl = document.getElementById('alertCount'); |
| const filterButtons = document.querySelectorAll('.filter-btn'); |
| |
| |
| let countdown = 300; |
| let currentFilter = 'all'; |
| let alerts = []; |
| |
| |
| document.addEventListener('DOMContentLoaded', () => { |
| fetchAlerts(); |
| startCountdown(); |
| |
| |
| filterButtons.forEach(btn => { |
| btn.addEventListener('click', () => { |
| filterButtons.forEach(b => b.classList.remove('active')); |
| btn.classList.add('active'); |
| currentFilter = btn.dataset.filter; |
| renderAlerts(); |
| }); |
| }); |
| }); |
| |
| |
| function fetchAlerts() { |
| loadingState.classList.remove('hidden'); |
| alertsGrid.classList.add('hidden'); |
| emptyState.classList.add('hidden'); |
| |
| |
| setTimeout(() => { |
| alerts = mockAlerts; |
| renderAlerts(); |
| updateLastUpdated(); |
| resetCountdown(); |
| |
| loadingState.classList.add('hidden'); |
| alertsGrid.classList.remove('hidden'); |
| |
| if (alerts.length === 0) { |
| emptyState.classList.remove('hidden'); |
| } |
| }, 1000); |
| |
| |
| refreshIcon.classList.add('fa-spin'); |
| setTimeout(() => { |
| refreshIcon.classList.remove('fa-spin'); |
| }, 1000); |
| } |
| |
| |
| function renderAlerts() { |
| alertsGrid.innerHTML = ''; |
| |
| const filteredAlerts = currentFilter === 'all' |
| ? alerts |
| : alerts.filter(alert => { |
| const action = alert.action.toLowerCase(); |
| if (currentFilter === 'buy') return action.includes('buy') || action.includes('upgrade'); |
| if (currentFilter === 'sell') return action.includes('sell') || action.includes('downgrade'); |
| if (currentFilter === 'news') return action.includes('news'); |
| return true; |
| }); |
| |
| alertCountEl.textContent = filteredAlerts.length; |
| |
| if (filteredAlerts.length === 0) { |
| emptyState.classList.remove('hidden'); |
| return; |
| } |
| |
| filteredAlerts.forEach(alert => { |
| const alertCard = createAlertCard(alert); |
| alertsGrid.appendChild(alertCard); |
| }); |
| } |
| |
| |
| function createAlertCard(alert) { |
| const card = document.createElement('div'); |
| card.className = 'bg-gray-900 border border-gray-800 rounded-xl overflow-hidden transition-all duration-300 card-hover relative'; |
| |
| |
| const action = alert.action.toLowerCase(); |
| let actionClass = 'action-news'; |
| if (action.includes('buy') || action.includes('upgrade')) actionClass = 'action-buy'; |
| if (action.includes('sell') || action.includes('downgrade')) actionClass = 'action-sell'; |
| if (action.includes('hold')) actionClass = 'action-hold'; |
| |
| |
| const date = new Date(alert.date); |
| const timeString = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); |
| const dateString = date.toLocaleDateString([], { month: 'short', day: 'numeric' }); |
| |
| card.innerHTML = ` |
| <div class="p-5"> |
| <div class="flex justify-between items-start mb-3"> |
| <div> |
| <h3 class="text-xl font-bold">${alert.ticker}</h3> |
| <p class="text-sm text-gray-400">${alert.source} • ${timeString}</p> |
| </div> |
| <div class="flex items-center space-x-2"> |
| <span class="px-2 py-1 rounded text-xs font-medium ${getActionColorClass(alert.action)}"> |
| ${alert.action} |
| </span> |
| </div> |
| </div> |
| |
| <p class="text-gray-300 mb-4">${alert.summary}</p> |
| |
| <div class="bg-gray-800 rounded-lg p-3"> |
| <div class="flex justify-between items-center text-sm mb-2"> |
| <span class="text-gray-400">${dateString}</span> |
| <a href="#" class="text-blue-400 hover:text-blue-300">View details <i class="fas fa-chevron-right ml-1 text-xs"></i></a> |
| </div> |
| <div class="flex justify-between items-center"> |
| <div> |
| <span class="text-2xl font-bold">$${getRandomPrice(100, 500).toFixed(2)}</span> |
| <span class="ml-2 text-sm ${Math.random() > 0.5 ? 'text-green-400' : 'text-red-400'}"> |
| ${(Math.random() * 5).toFixed(2)}% |
| <i class="fas ${Math.random() > 0.5 ? 'fa-caret-up' : 'fa-caret-down'} ml-1"></i> |
| </span> |
| </div> |
| <div class="text-right"> |
| <div class="text-xs text-gray-400">Volume</div> |
| <div class="text-sm">${(Math.random() * 10).toFixed(1)}M</div> |
| </div> |
| </div> |
| </div> |
| </div> |
| |
| <div class="ticker-badge ${actionClass}"> |
| ${alert.ticker === 'CRYPTO' ? '₿' : alert.ticker} |
| </div> |
| `; |
| |
| return card; |
| } |
| |
| |
| function getActionColorClass(action) { |
| action = action.toLowerCase(); |
| if (action.includes('buy') || action.includes('upgrade')) return 'bg-green-900 text-green-300'; |
| if (action.includes('sell') || action.includes('downgrade')) return 'bg-red-900 text-red-300'; |
| if (action.includes('hold')) return 'bg-yellow-900 text-yellow-300'; |
| return 'bg-blue-900 text-blue-300'; |
| } |
| |
| |
| function getRandomPrice(min, max) { |
| return Math.random() * (max - min) + min; |
| } |
| |
| |
| function startCountdown() { |
| setInterval(() => { |
| countdown--; |
| countdownEl.textContent = countdown; |
| |
| if (countdown <= 0) { |
| fetchAlerts(); |
| countdown = 300; |
| } |
| }, 1000); |
| } |
| |
| function resetCountdown() { |
| countdown = 300; |
| countdownEl.textContent = countdown; |
| } |
| |
| |
| function updateLastUpdated() { |
| const now = new Date(); |
| lastUpdatedEl.textContent = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); |
| } |
| |
| |
| refreshBtn.addEventListener('click', fetchAlerts); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=70pher703/panda-py-screener" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |