hhh / index.html
70pher703's picture
Add 3 files
51a91b8 verified
<!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 -->
<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 Content -->
<main>
<!-- Loading State -->
<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>
<!-- Alerts Grid -->
<div id="alertsGrid" class="grid md:grid-cols-2 lg:grid-cols-3 gap-6 hidden">
<!-- Alerts will be inserted here by JavaScript -->
</div>
<!-- Empty State -->
<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 -->
<footer class="mt-12 pt-6 border-t border-gray-800 text-center text-gray-500 text-sm">
<p>Wall Street Watchdog AI &copy; 2023 - Real-time market monitoring</p>
<p class="mt-1">Data sources: Yahoo Finance, MarketBeat, CoinDesk</p>
</footer>
</div>
<script>
// Mock data - in a real app this would come from an API
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()
}
];
// DOM Elements
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');
// State
let countdown = 300;
let currentFilter = 'all';
let alerts = [];
// Initialize
document.addEventListener('DOMContentLoaded', () => {
fetchAlerts();
startCountdown();
// Set up filter buttons
filterButtons.forEach(btn => {
btn.addEventListener('click', () => {
filterButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentFilter = btn.dataset.filter;
renderAlerts();
});
});
});
// Fetch alerts (simulated)
function fetchAlerts() {
loadingState.classList.remove('hidden');
alertsGrid.classList.add('hidden');
emptyState.classList.add('hidden');
// Simulate API call delay
setTimeout(() => {
alerts = mockAlerts;
renderAlerts();
updateLastUpdated();
resetCountdown();
loadingState.classList.add('hidden');
alertsGrid.classList.remove('hidden');
if (alerts.length === 0) {
emptyState.classList.remove('hidden');
}
}, 1000);
// Add loading animation to refresh button
refreshIcon.classList.add('fa-spin');
setTimeout(() => {
refreshIcon.classList.remove('fa-spin');
}, 1000);
}
// Render alerts based on current filter
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);
});
}
// Create an alert card element
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';
// Determine action class
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';
// Format date
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;
}
// Helper function to get action color class
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';
}
// Helper function to generate random price
function getRandomPrice(min, max) {
return Math.random() * (max - min) + min;
}
// Countdown timer
function startCountdown() {
setInterval(() => {
countdown--;
countdownEl.textContent = countdown;
if (countdown <= 0) {
fetchAlerts();
countdown = 300;
}
}, 1000);
}
function resetCountdown() {
countdown = 300;
countdownEl.textContent = countdown;
}
// Update last updated time
function updateLastUpdated() {
const now = new Date();
lastUpdatedEl.textContent = now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
}
// Event listeners
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"
}