import streamlit as st import yfinance as yf import pandas as pd import plotly.graph_objects as go from plotly.subplots import make_subplots import plotly.express as px from datetime import datetime, timedelta import numpy as np import anthropic import json # Page config st.set_page_config( page_title="Meta Stock Analysis", page_icon="📈", layout="wide", initial_sidebar_state="expanded" ) # Title and description st.title("📈 Meta (META) Stock Analysis Dashboard") st.markdown("Real-time analysis of Meta Platforms Inc. stock performance with AI-powered insights") # Sidebar for controls and chatbot st.sidebar.header("🔑 API Configuration") # Anthropic API Key input api_key = st.sidebar.text_input( "Enter your Anthropic API Key:", type="password", help="Enter your Anthropic API key to enable the AI stock analyst chatbot" ) # Initialize session state for chat if 'chat_messages' not in st.session_state: st.session_state.chat_messages = [] # Sidebar for analysis parameters st.sidebar.header("📊 Analysis Parameters") # Time period selection period_options = { "1 Month": "1mo", "3 Months": "3mo", "6 Months": "6mo", "1 Year": "1y", "2 Years": "2y", "5 Years": "5y" } selected_period = st.sidebar.selectbox( "Select Time Period", options=list(period_options.keys()), index=3 # Default to 1 Year ) # Moving averages ma_short = st.sidebar.number_input("Short MA (days)", min_value=5, max_value=50, value=20) ma_long = st.sidebar.number_input("Long MA (days)", min_value=50, max_value=200, value=50) # Load data function @st.cache_data(ttl=300) # Cache for 5 minutes def load_stock_data(symbol, period): """Load stock data from Yahoo Finance""" try: ticker = yf.Ticker(symbol) data = ticker.history(period=period) info = ticker.info return data, info except Exception as e: st.error(f"Error loading data: {str(e)}") return None, None # Technical indicators functions def calculate_rsi(data, window=14): """Calculate RSI (Relative Strength Index)""" delta = data['Close'].diff() gain = (delta.where(delta > 0, 0)).rolling(window=window).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean() rs = gain / loss rsi = 100 - (100 / (1 + rs)) return rsi def calculate_macd(data, fast=12, slow=26, signal=9): """Calculate MACD indicator""" exp1 = data['Close'].ewm(span=fast).mean() exp2 = data['Close'].ewm(span=slow).mean() macd = exp1 - exp2 signal_line = macd.ewm(span=signal).mean() histogram = macd - signal_line return macd, signal_line, histogram def safe_format(value, format_str): """Safely format values that might be None or NaN""" if value is None or pd.isna(value): return "N/A" try: return format_str.format(value) except: return str(value) def get_stock_analysis_context(data, info): """Prepare stock data context for AI analysis""" if data is None or data.empty: return "No stock data available." # Current metrics current_price = data['Close'].iloc[-1] prev_close = data['Close'].iloc[-2] price_change = current_price - prev_close price_change_pct = (price_change / prev_close) * 100 # Technical indicators current_rsi = data['RSI'].iloc[-1] if 'RSI' in data.columns and not pd.isna(data['RSI'].iloc[-1]) else None current_macd = data['MACD'].iloc[-1] if 'MACD' in data.columns and not pd.isna(data['MACD'].iloc[-1]) else None current_macd_signal = data['MACD_Signal'].iloc[-1] if 'MACD_Signal' in data.columns and not pd.isna(data['MACD_Signal'].iloc[-1]) else None # Moving averages ma_short_val = data['MA_Short'].iloc[-1] if 'MA_Short' in data.columns and not pd.isna(data['MA_Short'].iloc[-1]) else None ma_long_val = data['MA_Long'].iloc[-1] if 'MA_Long' in data.columns and not pd.isna(data['MA_Long'].iloc[-1]) else None # Volume analysis current_volume = data['Volume'].iloc[-1] avg_volume = data['Volume'].rolling(20).mean().iloc[-1] # 52-week range week_52_high = data['High'].rolling(252).max().iloc[-1] if len(data) >= 252 else data['High'].max() week_52_low = data['Low'].rolling(252).min().iloc[-1] if len(data) >= 252 else data['Low'].min() # Calculate returns safely returns_5d = "N/A" returns_20d = "N/A" if len(data) >= 6: returns_5d = safe_format(((data['Close'].iloc[-1] / data['Close'].iloc[-6]) - 1) * 100, "{:+.2f}%") if len(data) >= 21: returns_20d = safe_format(((data['Close'].iloc[-1] / data['Close'].iloc[-21]) - 1) * 100, "{:+.2f}%") # RSI analysis rsi_status = "N/A" if current_rsi is not None: if current_rsi > 70: rsi_status = "(Overbought >70)" elif current_rsi < 30: rsi_status = "(Oversold <30)" else: rsi_status = "(Neutral)" # MACD analysis macd_position = "N/A" if current_macd is not None and current_macd_signal is not None: if current_macd > current_macd_signal: macd_position = "Bullish (MACD > Signal)" else: macd_position = "Bearish (MACD < Signal)" # Price vs MA analysis price_vs_short_ma = "N/A" price_vs_long_ma = "N/A" if ma_short_val is not None: if current_price > ma_short_val: pct_diff = ((current_price/ma_short_val)-1)*100 price_vs_short_ma = f"Above (+{pct_diff:.1f}%)" else: pct_diff = ((current_price/ma_short_val)-1)*100 price_vs_short_ma = f"Below ({pct_diff:.1f}%)" if ma_long_val is not None: if current_price > ma_long_val: pct_diff = ((current_price/ma_long_val)-1)*100 price_vs_long_ma = f"Above (+{pct_diff:.1f}%)" else: pct_diff = ((current_price/ma_long_val)-1)*100 price_vs_long_ma = f"Below ({pct_diff:.1f}%)" context = f"""META STOCK ANALYSIS DATA (Last Updated: {data.index[-1].strftime('%Y-%m-%d')}): CURRENT PRICE METRICS: - Current Price: ${current_price:.2f} - Daily Change: ${price_change:+.2f} ({price_change_pct:+.2f}%) - 52-Week High: ${week_52_high:.2f} - 52-Week Low: ${week_52_low:.2f} - Current Volume: {current_volume:,.0f} - 20-Day Avg Volume: {avg_volume:,.0f} TECHNICAL INDICATORS: - RSI (14): {safe_format(current_rsi, '{:.1f}')} {rsi_status} - MACD: {safe_format(current_macd, '{:.3f}')} - MACD Signal: {safe_format(current_macd_signal, '{:.3f}')} - MACD Position: {macd_position} MOVING AVERAGES: - {ma_short}-Day MA: ${safe_format(ma_short_val, '{:.2f}')} - {ma_long}-Day MA: ${safe_format(ma_long_val, '{:.2f}')} - Price vs Short MA: {price_vs_short_ma} - Price vs Long MA: {price_vs_long_ma} COMPANY INFO: - Company: {info.get('longName', 'Meta Platforms Inc.')} - Market Cap: ${safe_format(info.get('marketCap', 0)/1e9 if info.get('marketCap') else None, '{:.1f}B')} - P/E Ratio: {info.get('trailingPE', 'N/A')} - Sector: {info.get('sector', 'Technology')} RECENT PERFORMANCE: - 5-Day Return: {returns_5d} - 20-Day Return: {returns_20d} You are an expert stock analyst. Use this data to provide insights about whether META is overbought, oversold, or fairly valued based on technical indicators. """ return context def chat_with_ai(messages, api_key, stock_context): """Chat with Anthropic AI using stock context""" try: client = anthropic.Anthropic(api_key=api_key) # Prepare system message with stock context system_message = f"""You are an expert stock analyst AI assistant specializing in Meta (META) stock analysis. {stock_context} Instructions for analysis: 1. Use the technical indicators (RSI, MACD, Moving Averages) to determine if the stock is overbought, oversold, or neutral 2. Consider multiple timeframes and indicators together 3. Provide specific reasoning for your conclusions 4. Give actionable insights while noting this is not financial advice 5. Be concise but thorough in your analysis 6. If asked about other stocks, politely redirect to Meta analysis 7. Use the current data provided to give relevant, timely insights Key Analysis Rules: - RSI > 70: Potentially overbought - RSI < 30: Potentially oversold - MACD above signal line: Bullish momentum - MACD below signal line: Bearish momentum - Price above both MAs: Uptrend - Price below both MAs: Downtrend - Volume above average: Strong conviction in moves """ # Convert messages to Anthropic format formatted_messages = [] for msg in messages: formatted_messages.append({ "role": msg["role"], "content": msg["content"] }) response = client.messages.create( model="claude-3-haiku-20240307", max_tokens=1000, system=system_message, messages=formatted_messages ) return response.content[0].text except Exception as e: return f"Error: {str(e)}. Please check your API key and try again." # Load Meta stock data data, info = load_stock_data("META", period_options[selected_period]) if data is not None and not data.empty: # Calculate indicators data['MA_Short'] = data['Close'].rolling(window=ma_short).mean() data['MA_Long'] = data['Close'].rolling(window=ma_long).mean() data['RSI'] = calculate_rsi(data) data['MACD'], data['MACD_Signal'], data['MACD_Histogram'] = calculate_macd(data) # Get stock context for AI stock_context = get_stock_analysis_context(data, info) # Current price info current_price = data['Close'].iloc[-1] prev_close = data['Close'].iloc[-2] price_change = current_price - prev_close price_change_pct = (price_change / prev_close) * 100 # AI Chatbot in Sidebar st.sidebar.header("🤖 AI Stock Analyst") if api_key: st.sidebar.success("✅ API Key configured - Chat enabled!") # Chat interface st.sidebar.subheader("đŸ’Ŧ Ask about META stock") # Display chat messages chat_container = st.sidebar.container() with chat_container: for message in st.session_state.chat_messages: if message["role"] == "user": st.write(f"**You:** {message['content']}") else: st.write(f"**AI:** {message['content']}") # Chat input user_question = st.sidebar.text_input( "Ask about META analysis:", placeholder="Is META overbought right now?", key="chat_input" ) if st.sidebar.button("Send", key="send_chat"): if user_question: # Add user message st.session_state.chat_messages.append({ "role": "user", "content": user_question }) # Get AI response with st.spinner("🤖 AI analyzing..."): ai_response = chat_with_ai( st.session_state.chat_messages, api_key, stock_context ) # Add AI response st.session_state.chat_messages.append({ "role": "assistant", "content": ai_response }) st.rerun() # Quick analysis buttons st.sidebar.subheader("đŸŽ¯ Quick Analysis") if st.sidebar.button("📊 Overall Analysis"): quick_question = "Based on all the technical indicators, is META currently overbought, oversold, or fairly valued? Give me a comprehensive analysis." st.session_state.chat_messages.append({"role": "user", "content": quick_question}) with st.spinner("🤖 Analyzing..."): ai_response = chat_with_ai(st.session_state.chat_messages, api_key, stock_context) st.session_state.chat_messages.append({"role": "assistant", "content": ai_response}) st.rerun() if st.sidebar.button("đŸŽ¯ Buy/Sell Signal"): quick_question = "Should I buy, sell, or hold META stock right now based on the technical indicators?" st.session_state.chat_messages.append({"role": "user", "content": quick_question}) with st.spinner("🤖 Analyzing..."): ai_response = chat_with_ai(st.session_state.chat_messages, api_key, stock_context) st.session_state.chat_messages.append({"role": "assistant", "content": ai_response}) st.rerun() if st.sidebar.button("📈 Risk Assessment"): quick_question = "What are the current risks and opportunities for META stock based on the technical analysis?" st.session_state.chat_messages.append({"role": "user", "content": quick_question}) with st.spinner("🤖 Analyzing..."): ai_response = chat_with_ai(st.session_state.chat_messages, api_key, stock_context) st.session_state.chat_messages.append({"role": "assistant", "content": ai_response}) st.rerun() # Clear chat button if st.sidebar.button("đŸ—‘ī¸ Clear Chat"): st.session_state.chat_messages = [] st.rerun() else: st.sidebar.warning("âš ī¸ Enter Anthropic API key to enable AI chat") st.sidebar.info("Get your API key from: https://console.anthropic.com/") # Display key metrics col1, col2, col3, col4 = st.columns(4) with col1: st.metric( "Current Price", f"${current_price:.2f}", f"{price_change:+.2f} ({price_change_pct:+.2f}%)" ) with col2: day_high = data['High'].iloc[-1] day_low = data['Low'].iloc[-1] st.metric("Day Range", f"${day_low:.2f} - ${day_high:.2f}") with col3: volume = data['Volume'].iloc[-1] avg_volume = data['Volume'].rolling(20).mean().iloc[-1] volume_ratio = volume / avg_volume st.metric( "Volume", f"{volume:,.0f}", f"{volume_ratio:.2f}x avg" ) with col4: market_cap = info.get('marketCap', 0) if market_cap: market_cap_b = market_cap / 1e9 st.metric("Market Cap", f"${market_cap_b:.1f}B") # Main price chart st.subheader("📊 Price Chart with Moving Averages") fig_price = go.Figure() # Candlestick chart fig_price.add_trace(go.Candlestick( x=data.index, open=data['Open'], high=data['High'], low=data['Low'], close=data['Close'], name="META" )) # Moving averages fig_price.add_trace(go.Scatter( x=data.index, y=data['MA_Short'], name=f"MA {ma_short}", line=dict(color='orange', width=2) )) fig_price.add_trace(go.Scatter( x=data.index, y=data['MA_Long'], name=f"MA {ma_long}", line=dict(color='red', width=2) )) fig_price.update_layout( title="Meta Stock Price with Moving Averages", yaxis_title="Price ($)", xaxis_title="Date", height=500, showlegend=True ) st.plotly_chart(fig_price, use_container_width=True) # Technical indicators col1, col2 = st.columns(2) with col1: st.subheader("📈 RSI (Relative Strength Index)") fig_rsi = go.Figure() fig_rsi.add_trace(go.Scatter( x=data.index, y=data['RSI'], name="RSI", line=dict(color='purple', width=2) )) fig_rsi.add_hline(y=70, line_dash="dash", line_color="red", annotation_text="Overbought (70)") fig_rsi.add_hline(y=30, line_dash="dash", line_color="green", annotation_text="Oversold (30)") fig_rsi.update_layout( yaxis_title="RSI", xaxis_title="Date", height=300, yaxis=dict(range=[0, 100]) ) st.plotly_chart(fig_rsi, use_container_width=True) current_rsi = data['RSI'].iloc[-1] if pd.notna(current_rsi): if current_rsi > 70: st.warning(f"âš ī¸ RSI: {current_rsi:.1f} - Potentially Overbought") elif current_rsi < 30: st.success(f"✅ RSI: {current_rsi:.1f} - Potentially Oversold") else: st.info(f"â„šī¸ RSI: {current_rsi:.1f} - Neutral") with col2: st.subheader("📊 MACD") fig_macd = go.Figure() fig_macd.add_trace(go.Scatter( x=data.index, y=data['MACD'], name="MACD", line=dict(color='blue', width=2) )) fig_macd.add_trace(go.Scatter( x=data.index, y=data['MACD_Signal'], name="Signal", line=dict(color='red', width=2) )) fig_macd.add_trace(go.Bar( x=data.index, y=data['MACD_Histogram'], name="Histogram", opacity=0.7 )) fig_macd.update_layout( yaxis_title="MACD", xaxis_title="Date", height=300 ) st.plotly_chart(fig_macd, use_container_width=True) # Volume analysis st.subheader("📊 Volume Analysis") fig_volume = px.bar( x=data.index, y=data['Volume'], title="Daily Trading Volume" ) fig_volume.update_layout( yaxis_title="Volume", xaxis_title="Date", height=300 ) st.plotly_chart(fig_volume, use_container_width=True) # Company information if info: st.subheader("đŸĸ Company Information") col1, col2 = st.columns(2) with col1: st.write(f"**Company:** {info.get('longName', 'Meta Platforms Inc.')}") st.write(f"**Sector:** {info.get('sector', 'N/A')}") st.write(f"**Industry:** {info.get('industry', 'N/A')}") st.write(f"**Employees:** {info.get('fullTimeEmployees', 'N/A'):,}" if info.get('fullTimeEmployees') else "**Employees:** N/A") with col2: st.write(f"**P/E Ratio:** {info.get('trailingPE', 'N/A')}") st.write(f"**52W High:** ${info.get('fiftyTwoWeekHigh', 'N/A')}") st.write(f"**52W Low:** ${info.get('fiftyTwoWeekLow', 'N/A')}") st.write(f"**Dividend Yield:** {info.get('dividendYield', 'N/A')}") # Company description if 'longBusinessSummary' in info: st.subheader("📄 Business Summary") st.write(info['longBusinessSummary']) # Performance summary st.subheader("📈 Performance Summary") # Calculate returns returns_1d = ((data['Close'].iloc[-1] / data['Close'].iloc[-2]) - 1) * 100 returns_1w = ((data['Close'].iloc[-1] / data['Close'].iloc[-8]) - 1) * 100 if len(data) >= 8 else None returns_1m = ((data['Close'].iloc[-1] / data['Close'].iloc[-22]) - 1) * 100 if len(data) >= 22 else None returns_ytd = ((data['Close'].iloc[-1] / data['Close'].iloc[0]) - 1) * 100 perf_col1, perf_col2, perf_col3, perf_col4 = st.columns(4) with perf_col1: st.metric("1 Day Return", f"{returns_1d:+.2f}%") with perf_col2: if returns_1w is not None: st.metric("1 Week Return", f"{returns_1w:+.2f}%") with perf_col3: if returns_1m is not None: st.metric("1 Month Return", f"{returns_1m:+.2f}%") with perf_col4: st.metric(f"{selected_period} Return", f"{returns_ytd:+.2f}%") else: st.error("❌ Unable to load Meta stock data. Please check your internet connection and try again.") # Footer st.markdown("---") st.markdown("**Disclaimer:** This is for educational purposes only and should not be considered as investment advice.") st.markdown("**AI Analysis:** The AI chatbot provides analysis based on technical indicators but should not be used as the sole basis for investment decisions.") st.markdown("Data source: Yahoo Finance via yfinance library â€ĸ AI: Anthropic Claude")