Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| from urllib.request import urlopen | |
| import certifi | |
| import json | |
| import pandas as pd | |
| import os | |
| import warnings | |
| import re | |
| warnings.filterwarnings("ignore") | |
| from langchain_community.document_loaders import CSVLoader | |
| from langchain_community.vectorstores import Chroma | |
| from langchain.text_splitter import RecursiveCharacterTextSplitter | |
| from langchain_community.embeddings import HuggingFaceEmbeddings | |
| from langchain.chains import RetrievalQA | |
| from langchain.prompts import PromptTemplate | |
| from langchain_community.llms import HuggingFacePipeline | |
| from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline | |
| # Hugging Face API Key | |
| os.environ["HUGGINGFACEHUB_API_TOKEN"] = os.environ.get("HUGGINGFACEHUB_API_TOKEN") | |
| # Load Falcon LLM | |
| model_id = "tiiuae/Falcon3-3B-Instruct" | |
| tokenizer = AutoTokenizer.from_pretrained(model_id) | |
| model = AutoModelForCausalLM.from_pretrained(model_id) | |
| pipe = pipeline( | |
| "text-generation", | |
| model=model, | |
| tokenizer=tokenizer, | |
| max_new_tokens=512, | |
| do_sample=True, | |
| temperature=0.1, | |
| top_k=50, | |
| top_p=0.95, | |
| eos_token_id=tokenizer.eos_token_id, | |
| ) | |
| llm = HuggingFacePipeline(pipeline=pipe) | |
| # Enhanced Prompt Template | |
| template = """ | |
| You are a Financial Market Expert. Analyze the provided market data and create a comprehensive financial report. | |
| Market Data: {context} | |
| Company: {question} | |
| Please provide a detailed analysis including: | |
| 1. Current stock performance metrics | |
| 2. Price movements and trends | |
| 3. Market capitalization details | |
| 4. Trading volume analysis | |
| 5. Key financial indicators | |
| Format the response as a clear, professional financial report with proper sections and bullet points. | |
| """ | |
| PROMPT = PromptTemplate(input_variables=["context", "question"], template=template) | |
| def format_currency(value): | |
| """Format currency values""" | |
| try: | |
| if isinstance(value, (int, float)): | |
| return f"${value:,.2f}" | |
| return str(value) | |
| except: | |
| return str(value) | |
| def format_percentage(value): | |
| """Format percentage values""" | |
| try: | |
| if isinstance(value, (int, float)): | |
| return f"{value:.2f}%" | |
| return str(value) | |
| except: | |
| return str(value) | |
| def create_html_report(data, llm_analysis): | |
| """Create a formatted HTML report""" | |
| if not data or len(data) == 0: | |
| return "<div class='error'>No data available for this ticker.</div>" | |
| # Get the first data point (assuming it's the most relevant) | |
| stock_data = data[0] if isinstance(data, list) else data | |
| # Extract key metrics | |
| symbol = stock_data.get('symbol', 'N/A') | |
| name = stock_data.get('name', 'N/A') | |
| price = stock_data.get('price', 0) | |
| change = stock_data.get('change', 0) | |
| change_percent = stock_data.get('changesPercentage', 0) | |
| market_cap = stock_data.get('marketCap', 0) | |
| volume = stock_data.get('volume', 0) | |
| avg_volume = stock_data.get('avgVolume', 0) | |
| pe_ratio = stock_data.get('pe', 'N/A') | |
| eps = stock_data.get('eps', 'N/A') | |
| # Determine trend color | |
| trend_color = "#22c55e" if change >= 0 else "#ef4444" | |
| trend_icon = "π" if change >= 0 else "π" | |
| html = f""" | |
| <div style="font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 800px; margin: 0 auto;"> | |
| <!-- Header Section --> | |
| <div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 25px; border-radius: 12px; margin-bottom: 20px; box-shadow: 0 4px 15px rgba(0,0,0,0.1);"> | |
| <h1 style="margin: 0 0 10px 0; font-size: 28px; font-weight: 600;">{symbol} - Financial Report</h1> | |
| <h2 style="margin: 0; font-size: 18px; font-weight: 400; opacity: 0.9;">{name}</h2> | |
| <div style="margin-top: 15px; font-size: 14px; opacity: 0.8;"> | |
| π Generated on {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')} | |
| </div> | |
| </div> | |
| <!-- Key Metrics Grid --> | |
| <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 25px;"> | |
| <!-- Current Price Card --> | |
| <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); border-left: 4px solid #3b82f6;"> | |
| <div style="color: #6b7280; font-size: 14px; margin-bottom: 5px;">Current Price</div> | |
| <div style="font-size: 24px; font-weight: 700; color: #1f2937;">{format_currency(price)}</div> | |
| </div> | |
| <!-- Price Change Card --> | |
| <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); border-left: 4px solid {trend_color};"> | |
| <div style="color: #6b7280; font-size: 14px; margin-bottom: 5px;">Price Change {trend_icon}</div> | |
| <div style="font-size: 18px; font-weight: 600; color: {trend_color};"> | |
| {format_currency(change)} ({format_percentage(change_percent)}) | |
| </div> | |
| </div> | |
| <!-- Market Cap Card --> | |
| <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); border-left: 4px solid #10b981;"> | |
| <div style="color: #6b7280; font-size: 14px; margin-bottom: 5px;">Market Cap</div> | |
| <div style="font-size: 18px; font-weight: 600; color: #1f2937;"> | |
| {format_currency(market_cap) if market_cap else 'N/A'} | |
| </div> | |
| </div> | |
| <!-- Volume Card --> | |
| <div style="background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); border-left: 4px solid #f59e0b;"> | |
| <div style="color: #6b7280; font-size: 14px; margin-bottom: 5px;">Volume</div> | |
| <div style="font-size: 18px; font-weight: 600; color: #1f2937;"> | |
| {f"{volume:,}" if volume else 'N/A'} | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Additional Metrics Table --> | |
| <div style="background: white; border-radius: 10px; padding: 25px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin-bottom: 25px;"> | |
| <h3 style="margin: 0 0 20px 0; color: #1f2937; font-size: 20px; font-weight: 600; border-bottom: 2px solid #e5e7eb; padding-bottom: 10px;"> | |
| π Detailed Metrics | |
| </h3> | |
| <table style="width: 100%; border-collapse: collapse;"> | |
| <tr style="border-bottom: 1px solid #e5e7eb;"> | |
| <td style="padding: 12px 0; font-weight: 600; color: #374151; width: 40%;">P/E Ratio</td> | |
| <td style="padding: 12px 0; color: #6b7280;">{pe_ratio}</td> | |
| </tr> | |
| <tr style="border-bottom: 1px solid #e5e7eb;"> | |
| <td style="padding: 12px 0; font-weight: 600; color: #374151;">Earnings Per Share (EPS)</td> | |
| <td style="padding: 12px 0; color: #6b7280;">{format_currency(eps) if eps != 'N/A' else 'N/A'}</td> | |
| </tr> | |
| <tr style="border-bottom: 1px solid #e5e7eb;"> | |
| <td style="padding: 12px 0; font-weight: 600; color: #374151;">Average Volume</td> | |
| <td style="padding: 12px 0; color: #6b7280;">{f"{avg_volume:,}" if avg_volume else 'N/A'}</td> | |
| </tr> | |
| <tr> | |
| <td style="padding: 12px 0; font-weight: 600; color: #374151;">Exchange</td> | |
| <td style="padding: 12px 0; color: #6b7280;">{stock_data.get('exchange', 'N/A')}</td> | |
| </tr> | |
| </table> | |
| </div> | |
| <!-- AI Analysis Section --> | |
| <div style="background: white; border-radius: 10px; padding: 25px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);"> | |
| <h3 style="margin: 0 0 20px 0; color: #1f2937; font-size: 20px; font-weight: 600; border-bottom: 2px solid #e5e7eb; padding-bottom: 10px;"> | |
| π€ AI Analysis | |
| </h3> | |
| <div style="background: #f8fafc; border-radius: 8px; padding: 20px; border-left: 4px solid #6366f1;"> | |
| <pre style="white-space: pre-wrap; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; line-height: 1.6; color: #374151;">{llm_analysis}</pre> | |
| </div> | |
| </div> | |
| <!-- Footer --> | |
| <div style="text-align: center; margin-top: 25px; padding: 20px; color: #6b7280; font-size: 14px; border-top: 1px solid #e5e7eb;"> | |
| <p style="margin: 0;">β οΈ This report is for informational purposes only and should not be considered as investment advice.</p> | |
| </div> | |
| </div> | |
| """ | |
| return html | |
| def generate_report(ticker: str, exchange: str): | |
| """Generate enhanced financial report""" | |
| api_key = "C1HRSweTniWdBuLmTTse9w8KpkoiouM5" | |
| if not ticker.strip(): | |
| return "<div style='color: red; padding: 20px; text-align: center;'>β Please enter a valid ticker symbol.</div>" | |
| try: | |
| # Show loading message | |
| loading_html = """ | |
| <div style="text-align: center; padding: 40px; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;"> | |
| <div style="display: inline-block; padding: 20px; background: #f0f9ff; border-radius: 10px; border: 1px solid #0ea5e9;"> | |
| <div style="font-size: 18px; color: #0369a1; margin-bottom: 10px;">π Generating Report...</div> | |
| <div style="color: #64748b;">Fetching data and running AI analysis</div> | |
| </div> | |
| </div> | |
| """ | |
| # Fetch Data | |
| ticker = ticker.upper().strip() | |
| if exchange == "NSE": | |
| url = f"https://financialmodelingprep.com/api/v3/search?query={ticker}&exchange=NSE&apikey={api_key}" | |
| else: | |
| url = f"https://financialmodelingprep.com/api/v3/quote/{ticker}?apikey={api_key}" | |
| response = urlopen(url, cafile=certifi.where()) | |
| data = json.loads(response.read().decode("utf-8")) | |
| if not data: | |
| return "<div style='color: red; padding: 20px; text-align: center;'>β No data found for ticker: " + ticker + "</div>" | |
| # Save to CSV for RAG processing | |
| df = pd.DataFrame(data) | |
| if 'timestamp' in df.columns: | |
| df['timestamp'] = pd.to_datetime(df['timestamp'], errors='coerce') | |
| if 'earningsAnnouncement' in df.columns: | |
| df['earningsAnnouncement'] = pd.to_datetime(df['earningsAnnouncement'], errors='coerce') | |
| df.to_csv("eco_ind.csv", index=False) | |
| # Create vector store and run LLM analysis | |
| loader = CSVLoader("eco_ind.csv") | |
| documents = loader.load() | |
| splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20) | |
| texts = splitter.split_documents(documents) | |
| embeddings = HuggingFaceEmbeddings() | |
| chroma = Chroma.from_documents( | |
| documents=texts, | |
| collection_name="economic_data", | |
| embedding=embeddings, | |
| persist_directory="docs/chroma_rag" | |
| ) | |
| retriever = chroma.as_retriever(search_kwargs={"k": 3}) | |
| qa = RetrievalQA.from_chain_type( | |
| llm=llm, | |
| chain_type="stuff", | |
| chain_type_kwargs={"prompt": PROMPT}, | |
| retriever=retriever, | |
| return_source_documents=False | |
| ) | |
| # Get LLM analysis | |
| query = f"Provide a comprehensive financial analysis for {ticker}" | |
| result = qa({"query": query}) | |
| llm_analysis = result["result"] | |
| # Create and return formatted HTML report | |
| return create_html_report(data, llm_analysis) | |
| except Exception as e: | |
| error_html = f""" | |
| <div style="font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 600px; margin: 20px auto; padding: 25px; background: #fef2f2; border-radius: 10px; border: 1px solid #fecaca;"> | |
| <h3 style="color: #dc2626; margin: 0 0 15px 0; display: flex; align-items: center;"> | |
| β Error Occurred | |
| </h3> | |
| <div style="background: white; padding: 15px; border-radius: 6px; border: 1px solid #f3f4f6;"> | |
| <strong>Details:</strong> {str(e)} | |
| </div> | |
| <div style="margin-top: 15px; color: #6b7280; font-size: 14px;"> | |
| Please check your ticker symbol and try again. Make sure you have a stable internet connection. | |
| </div> | |
| </div> | |
| """ | |
| return error_html | |
| # Custom CSS for better styling | |
| custom_css = """ | |
| #component-0 { | |
| max-width: 1200px !important; | |
| margin: 0 auto !important; | |
| } | |
| .gradio-container { | |
| background-color: black !important; | |
| min-height: 100vh !important; | |
| } | |
| #title { | |
| text-align: center !important; | |
| color: white !important; | |
| font-size: 2.5rem !important; | |
| font-weight: 700 !important; | |
| margin-bottom: 1rem !important; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.1) !important; | |
| } | |
| #description { | |
| text-align: center !important; | |
| color: white !important; | |
| font-size: 1.1rem !important; | |
| margin-bottom: 2rem !important; | |
| } | |
| .input-container { | |
| background: white !important; | |
| border-radius: 5px !important; | |
| padding: 3px !important; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.1) !important; | |
| margin-bottom: 20px !important; | |
| } | |
| .output-container { | |
| background: transparent !important; | |
| border-radius: 15px !important; | |
| padding: 10px; | |
| border: 2px white solid ; | |
| overflow: hidden !important; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.2) !important; | |
| } | |
| """ | |
| # Create the Gradio interface with sidebar for inputs | |
| with gr.Blocks(css=custom_css, title="π Financial Report Generator") as iface: | |
| gr.HTML('<div id="title">π AI-Powered Financial Report Generator</div>') | |
| gr.HTML('<div id="description">Enter a stock ticker and exchange to get a comprehensive AI-generated financial analysis</div>') | |
| # Sidebar for inputs | |
| with gr.Sidebar(): | |
| gr.Markdown("## π₯ Input Parameters") | |
| ticker_input = gr.Textbox( | |
| label="π― Stock Ticker Symbol", | |
| placeholder="e.g., MSFT, AAPL, GOOGL", | |
| info="Enter the stock symbol you want to analyze" | |
| ) | |
| exchange_input = gr.Radio( | |
| choices=["US", "NSE"], | |
| label="π’ Exchange", | |
| value="US", | |
| info="Select the stock exchange" | |
| ) | |
| generate_btn = gr.Button( | |
| "π Generate Report", | |
| variant="primary" | |
| ) | |
| clear_btn = gr.Button( | |
| "π Clear", | |
| variant="secondary" | |
| ) | |
| # Output section | |
| with gr.Row(elem_classes=["output-container"]): | |
| output = gr.HTML(label="π Financial Report") | |
| # Event handlers | |
| generate_btn.click( | |
| fn=generate_report, | |
| inputs=[ticker_input, exchange_input], | |
| outputs=output | |
| ) | |
| clear_btn.click( | |
| lambda: ["", "US", ""], | |
| outputs=[ticker_input, exchange_input, output] | |
| ) | |
| ticker_input.submit( | |
| fn=generate_report, | |
| inputs=[ticker_input, exchange_input], | |
| outputs=output | |
| ) | |
| if __name__ == "__main__": | |
| iface.launch( | |
| share=True, | |
| show_error=True | |
| ) |