// Wall Street Watchdog AI - Full App (Frontend + Backend) // --- Frontend (React) --- import React, { useEffect, useState } from 'react'; import { Card, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { ReloadIcon } from "lucide-react"; import axios from 'axios'; const API_ENDPOINT = "/api/alerts"; export default function WatchdogApp() { const [alerts, setAlerts] = useState([]); const [loading, setLoading] = useState(false); const [countdown, setCountdown] = useState(300); const fetchAlerts = async () => { setLoading(true); try { const res = await axios.get(API_ENDPOINT); setAlerts(res.data.alerts); } catch (err) { console.error("Error fetching alerts:", err); } setLoading(false); }; useEffect(() => { fetchAlerts(); const interval = setInterval(fetchAlerts, 300000); const countdownInterval = setInterval(() => setCountdown((c) => (c > 0 ? c - 1 : 300)), 1000); return () => { clearInterval(interval); clearInterval(countdownInterval); }; }, []); return ( <div className="p-6 bg-black text-white min-h-screen"> <h1 className="text-3xl font-bold mb-4">Wall Street Watchdog AI</h1> <div className="flex justify-between items-center mb-2"> <p className="text-sm">Auto-refresh in {countdown}s</p> <Button onClick={fetchAlerts} disabled={loading}> {loading ? <ReloadIcon className="animate-spin" /> : "Refresh Now"} </Button> </div> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4"> {alerts.map((alert, idx) => ( <Card key={idx} className="bg-gray-900 border border-gray-700 rounded-2xl shadow-xl"> <CardContent className="p-4"> <p className="text-xl font-semibold">{alert.ticker} — {alert.action}</p> <p className="text-sm text-gray-400">{alert.source}</p> <p className="text-base mt-2">{alert.summary}</p> <div className="mt-4"> <iframe src={`https://s.tradingview.com/embed-widget/mini-symbol-overview/?symbol=${alert.ticker}&locale=en`} width="100%" height="100" frameBorder="0" allowTransparency scrolling="no" /> </div> </CardContent> </Card> ))} </div> </div> ); } // --- Backend API (FastAPI) --- from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel from typing import List import requests from bs4 import BeautifulSoup from datetime import datetime import openai import os app = FastAPI() app.add_middleware( CORSMiddleware, allow_origins=[""], allow_credentials=True, allow_methods=[""], allow_headers=["*"], ) class Alert(BaseModel): ticker: str action: str summary: str source: str date: str class AlertResponse(BaseModel): alerts: List[Alert] openai.api_key = os.getenv("OPENAI_API_KEY") def summarize_text(text): try: response = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[ {"role": "system", "content": "Summarize the following financial update in 1-2 sentences."}, {"role": "user", "content": text} ] ) return response['choices'][0]['message']['content'] except: return text[:200] def get_yahoo_news(): url = "https://finance.yahoo.com/" r = requests.get(url) soup = BeautifulSoup(r.text, "html.parser") articles = soup.select("li.js-stream-content")[:5] alerts = [] for a in articles: headline = a.get_text() link = a.find("a")['href'] if a.find("a") else "" summary = summarize_text(headline) alerts.append(Alert( ticker="MKT", action="News", summary=summary, source="Yahoo Finance", date=str(datetime.utcnow()) )) return alerts def get_marketbeat_ratings(): url = "https://www.marketbeat.com/ratings/" r = requests.get(url) soup = BeautifulSoup(r.text, "html.parser") rows = soup.select("table.ratings-table tbody tr")[:5] alerts = [] for row in rows: cols = row.find_all("td") if len(cols) >= 5: ticker = cols[0].get_text(strip=True) action = cols[2].get_text(strip=True) summary = summarize_text(f"{ticker} received a {action} rating from {cols[1].get_text(strip=True)}") alerts.append(Alert( ticker=ticker, action=action, summary=summary, source="MarketBeat", date=str(datetime.utcnow()) )) return alerts def get_coindesk(): url = "https://www.coindesk.com/" r = requests.get(url) soup = BeautifulSoup(r.text, "html.parser") articles = soup.select("a.card-title")[:5] alerts = [] for a in articles: headline = a.get_text(strip=True) summary = summarize_text(headline) alerts.append(Alert( ticker="CRYPTO", action="News", summary=summary, source="CoinDesk", date=str(datetime.utcnow()) )) return alerts @app.get("/api/alerts", response_model=AlertResponse) async def get_all_alerts(): alerts = [] alerts += get_yahoo_news() alerts += get_marketbeat_ratings() alerts += get_coindesk() return {"alerts": alerts} |