|
<!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/hhh" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
</html>{ |
|
"ticker": "NVDA", |
|
"text": "Analyst upgrades NVDA to Strong Buy", |
|
"source": "MarketBeat", |
|
"date": "2024-11-10", |
|
"action": "Upgrade", |
|
"future_return_1d": +2.1, |
|
"future_return_7d": +6.5, |
|
"label": "Strong Buy" |
|
} |