import os import json import streamlit as st import requests import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart import time from datetime import datetime, timedelta import pandas as pd from groq import Groq import plotly.graph_objects as go from plotly.subplots import make_subplots st.set_page_config(page_title="AI Alert Me", page_icon="πŸ€–", layout="wide") # Initialize session state if 'alerts' not in st.session_state: st.session_state.alerts = [] if 'monitoring' not in st.session_state: st.session_state.monitoring = False if 'price_history' not in st.session_state: st.session_state.price_history = {} if 'ai_insights' not in st.session_state: st.session_state.ai_insights = {} # --- Helper Functions --- def get_crypto_price(symbol, max_retries=3): """Fetch cryptocurrency price from CoinGecko API with retry logic""" for attempt in range(max_retries): try: url = f"https://api.coingecko.com/api/v3/simple/price?ids={symbol}&vs_currencies=usd&include_24hr_change=true" response = requests.get(url, timeout=10) response.raise_for_status() data = response.json() return { 'price': data[symbol]['usd'], 'change_24h': data[symbol].get('usd_24h_change', 0), 'status': 'success' } except requests.exceptions.Timeout: if attempt < max_retries - 1: wait_time = (attempt + 1) * 2 time.sleep(wait_time) continue return {'status': 'error', 'message': f'Timeout - will retry in next cycle'} except requests.exceptions.RequestException as e: if attempt < max_retries - 1: wait_time = (attempt + 1) * 2 time.sleep(wait_time) continue return {'status': 'error', 'message': f'Network error - retrying in next cycle'} except (KeyError, json.JSONDecodeError) as e: return {'status': 'error', 'message': f'Invalid data received'} except Exception as e: return {'status': 'error', 'message': f'Error: {str(e)[:50]}'} return {'status': 'error', 'message': 'Max retries reached'} def get_stock_price(symbol, max_retries=3): """Fetch stock price from Yahoo Finance with retry logic""" for attempt in range(max_retries): try: url = f"https://query1.finance.yahoo.com/v8/finance/chart/{symbol}" response = requests.get(url, timeout=10) response.raise_for_status() data = response.json() price = data['chart']['result'][0]['meta']['regularMarketPrice'] prev_close = data['chart']['result'][0]['meta']['previousClose'] change = ((price - prev_close) / prev_close) * 100 return { 'price': price, 'change_24h': change, 'status': 'success' } except requests.exceptions.Timeout: if attempt < max_retries - 1: wait_time = (attempt + 1) * 2 time.sleep(wait_time) continue return {'status': 'error', 'message': f'Timeout - will retry in next cycle'} except requests.exceptions.RequestException as e: if attempt < max_retries - 1: wait_time = (attempt + 1) * 2 time.sleep(wait_time) continue return {'status': 'error', 'message': f'Network error - retrying in next cycle'} except (KeyError, IndexError, json.JSONDecodeError) as e: return {'status': 'error', 'message': f'Invalid data or symbol not found'} except Exception as e: return {'status': 'error', 'message': f'Error: {str(e)[:50]}'} return {'status': 'error', 'message': 'Max retries reached'} def update_price_history(asset, price): """Track price history for AI analysis""" if asset not in st.session_state.price_history: st.session_state.price_history[asset] = [] st.session_state.price_history[asset].append({ 'timestamp': datetime.now().isoformat(), 'price': price }) # Keep only last 100 data points for better charts if len(st.session_state.price_history[asset]) > 100: st.session_state.price_history[asset] = st.session_state.price_history[asset][-100:] def create_price_chart(asset, target_price=None, alert_type=None): """Create interactive price chart with target line and colored bars""" history = st.session_state.price_history.get(asset, []) if len(history) < 2: return None # Prepare data timestamps = [datetime.fromisoformat(h['timestamp']) for h in history] prices = [h['price'] for h in history] # Calculate price changes price_changes = [0] # First bar is neutral for i in range(1, len(prices)): price_changes.append(prices[i] - prices[i-1]) # Color bars based on price movement colors = ['green' if change > 0 else 'red' if change < 0 else 'gray' for change in price_changes] # Create figure with secondary y-axis fig = make_subplots( rows=2, cols=1, row_heights=[0.7, 0.3], vertical_spacing=0.1, subplot_titles=(f'{asset.upper()} Price Chart', 'Price Change (Bar Chart)') ) # Add line chart fig.add_trace( go.Scatter( x=timestamps, y=prices, mode='lines+markers', name='Price', line=dict(color='#00D9FF', width=2), marker=dict(size=4) ), row=1, col=1 ) # Add target line if specified if target_price is not None: fig.add_hline( y=target_price, line_dash="dash", line_color="yellow", annotation_text=f"Target: ${target_price:.2f}", annotation_position="right", row=1, col=1 ) # Add bar chart for price changes fig.add_trace( go.Bar( x=timestamps, y=price_changes, name='Price Change', marker_color=colors, showlegend=False ), row=2, col=1 ) # Add horizontal line at 0 for bar chart fig.add_hline(y=0, line_dash="solid", line_color="white", line_width=1, row=2, col=1) # Update layout fig.update_layout( height=600, showlegend=True, hovermode='x unified', template='plotly_dark', margin=dict(l=50, r=50, t=80, b=50) ) # Update axes fig.update_xaxes(title_text="Time", row=2, col=1) fig.update_yaxes(title_text="Price ($)", row=1, col=1) fig.update_yaxes(title_text="Change ($)", row=2, col=1) return fig def create_alert_status_chart(): """Create bar chart showing alert status (above/below target)""" if not st.session_state.alerts: return None alerts_data = [] errors = [] for alert in st.session_state.alerts: if alert['type'] == "Cryptocurrency": current_data = get_crypto_price(alert['asset']) else: current_data = get_stock_price(alert['asset']) if current_data and current_data.get('status') == 'success': current_price = current_data['price'] target_price = alert['target_price'] difference = current_price - target_price percentage_diff = (difference / target_price) * 100 alerts_data.append({ 'asset': alert['asset'], 'difference': difference, 'percentage': percentage_diff, 'alert_type': alert['alert_type'], 'triggered': alert['triggered'] }) else: errors.append(f"{alert['asset']}: {current_data.get('message', 'Error')}") # Show errors if any if errors: st.warning("⚠️ Some assets failed to load:\n" + "\n".join(errors)) if not alerts_data: return None df = pd.DataFrame(alerts_data) # Create color based on alert type and difference colors = [] for idx, row in df.iterrows(): if row['triggered']: colors.append('gold') elif row['alert_type'] == 'Above': colors.append('green' if row['difference'] > 0 else 'red') else: # Below colors.append('red' if row['difference'] > 0 else 'green') fig = go.Figure(data=[ go.Bar( x=df['asset'], y=df['percentage'], marker_color=colors, text=[f"{p:.2f}%" for p in df['percentage']], textposition='outside', hovertemplate='%{x}
' + 'Difference: %{y:.2f}%
' + '' ) ]) fig.add_hline(y=0, line_dash="solid", line_color="white", line_width=2) fig.update_layout( title="Alert Status: Distance from Target (%)", xaxis_title="Asset", yaxis_title="Percentage Difference from Target", template='plotly_dark', height=400, showlegend=False ) return fig def ai_analyze_price_trend(asset, current_data, groq_client, model_name): """Use AI to analyze price trends and provide insights""" history = st.session_state.price_history.get(asset, []) if len(history) < 3: return "Not enough data yet for AI analysis. Keep monitoring..." prompt = f""" You are a financial AI analyst. Analyze this price data and provide insights: Asset: {asset} Current Price: ${current_data['price']:.2f} 24h Change: {current_data['change_24h']:.2f}% Recent Price History (last {len(history)} data points): {json.dumps(history[-10:], indent=2)} Provide: 1. Brief trend analysis (bullish/bearish/neutral) 2. Key observations about volatility 3. Short recommendation (1-2 sentences) Keep response under 150 words and professional. """ try: response = groq_client.chat.completions.create( model=model_name, messages=[{"role": "user", "content": prompt}], temperature=0.5, max_tokens=400, ) return response.choices[0].message.content.strip() except Exception as e: return f"AI Analysis unavailable: {str(e)}" def ai_suggest_alert(asset, current_price, groq_client, model_name): """AI suggests optimal alert prices based on current market""" prompt = f""" You are a financial advisor AI. Based on this data, suggest smart alert prices: Asset: {asset} Current Price: ${current_price:.2f} Suggest: 1. A reasonable upper alert price (for taking profits) 2. A reasonable lower alert price (for buying opportunity) Return ONLY valid JSON in this format: {{ "upper_alert": , "lower_alert": , "reasoning": "" }} """ try: response = groq_client.chat.completions.create( model=model_name, messages=[{"role": "user", "content": prompt}], temperature=0.3, max_tokens=300, ) result = response.choices[0].message.content.strip() # Extract JSON from response import re match = re.search(r'\{[\s\S]*\}', result) if match: return json.loads(match.group(0)) return None except Exception as e: return None def send_email_alert(recipient_email, sender_email, sender_password, alert_info): """Send email alert when price target is hit""" try: msg = MIMEMultipart() msg['From'] = sender_email msg['To'] = recipient_email msg['Subject'] = f"πŸ€– AI Alert: {alert_info['asset']} Hit Target!" body = f""" AI Price Alert Triggered! Asset: {alert_info['asset']} Current Price: ${alert_info['current_price']:.2f} Target Price: ${alert_info['target_price']:.2f} Alert Type: {alert_info['alert_type']} 24h Change: {alert_info.get('change_24h', 'N/A')}% Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} AI Insight: {alert_info.get('ai_insight', 'No analysis available')} --- Powered by AI Alert Me """ msg.attach(MIMEText(body, 'plain')) server = smtplib.SMTP('smtp.gmail.com', 587) server.starttls() server.login(sender_email, sender_password) server.send_message(msg) server.quit() return True except Exception as e: st.error(f"Error sending email: {str(e)}") return False # --- Main App --- def main(): st.markdown(""" """, unsafe_allow_html=True) st.title("πŸ€– AI Alert Me - Smart Price Tracker") st.markdown("AI-powered price tracking with intelligent insights and recommendations") # Sidebar Configuration st.sidebar.image("/app/src/2.png") st.sidebar.image("/app/src/1.png") st.sidebar.header("βš™οΈ Configuration") # Groq API Key groq_api_key = st.sidebar.text_input( "Groq API Key", type="password", value=st.secrets.get("GROQ_API_KEY", "") or os.getenv("GROQ_API_KEY", "") ) if not groq_api_key: st.warning("⚠️ Please enter your Groq API key in the sidebar for AI features") groq_client = None else: groq_client = Groq(api_key=groq_api_key) model_name = st.sidebar.selectbox( "AI Model:", ["llama-3.3-70b-versatile","openai/gpt-oss-120b", "mixtral-8x7b-32768", "llama-3.1-70b-versatile"] ) st.sidebar.markdown("---") st.sidebar.header("πŸ“§ Email Configuration") sender_email = st.sidebar.text_input("Gmail Address") sender_password = st.sidebar.text_input("Gmail App Password", type="password") recipient_email = st.sidebar.text_input("Alert Email") st.sidebar.markdown("---") st.sidebar.markdown("**πŸ€– AI Features:**") st.sidebar.markdown("βœ“ Smart price trend analysis") st.sidebar.markdown("βœ“ Alert recommendations") st.sidebar.markdown("βœ“ Market insights") st.sidebar.markdown("βœ“ Interactive price charts") st.sidebar.caption("Built with πŸ’‘ Streamlit + Groq | DW 2025") # Main Interface tab1, tab2, tab3, tab4 = st.tabs(["πŸ“Š Dashboard", "βž• Add Alert", "πŸ“ˆ Charts", "🧠 AI Insights"]) # --- TAB 1: Dashboard --- with tab1: st.header("Current Prices & Alerts") col1, col2, col3, col4 = st.columns(4) with col1: btc_data = get_crypto_price("bitcoin") if btc_data and btc_data.get('status') == 'success': st.metric( "Bitcoin", f"${btc_data['price']:,.2f}", f"{btc_data['change_24h']:.2f}%" ) else: st.error(f"⚠️ Bitcoin: {btc_data.get('message', 'Error')}") with col2: eth_data = get_crypto_price("ethereum") if eth_data and eth_data.get('status') == 'success': st.metric( "Ethereum", f"${eth_data['price']:,.2f}", f"{eth_data['change_24h']:.2f}%" ) else: st.error(f"⚠️ Ethereum: {eth_data.get('message', 'Error')}") with col3: sol_data = get_crypto_price("solana") if sol_data and sol_data.get('status') == 'success': st.metric( "Solana", f"${sol_data['price']:,.2f}", f"{sol_data['change_24h']:.2f}%" ) else: st.error(f"⚠️ Solana: {sol_data.get('message', 'Error')}") with col4: if st.button("πŸ”„ Refresh All"): st.rerun() st.markdown("---") # Alert Status Chart if st.session_state.alerts: st.subheader("🎯 Alert Status Overview") status_chart = create_alert_status_chart() if status_chart: st.plotly_chart(status_chart, use_container_width=True) # Display Active Alerts if st.session_state.alerts: st.subheader("πŸ”” Active Alerts") df = pd.DataFrame(st.session_state.alerts) df_display = df[['asset', 'target_price', 'alert_type', 'triggered']].copy() df_display.columns = ['Asset', 'Target ($)', 'Type', 'Triggered'] st.dataframe(df_display, use_container_width=True) col1, col2 = st.columns(2) with col1: if st.button("▢️ Start Monitoring" if not st.session_state.monitoring else "⏸️ Stop Monitoring"): st.session_state.monitoring = not st.session_state.monitoring st.rerun() with col2: if st.button("πŸ—‘οΈ Clear All Alerts"): st.session_state.alerts = [] st.rerun() else: st.info("πŸ“ No active alerts. Add one in the 'Add Alert' tab!") # --- TAB 2: Add Alert --- with tab2: st.header("βž• Create New Alert") asset_type = st.selectbox("Asset Type", ["Cryptocurrency", "Stock"]) if asset_type == "Cryptocurrency": asset_symbol = st.selectbox( "Select Crypto", ["bitcoin", "ethereum", "cardano", "solana", "dogecoin", "ripple", "polkadot"] ) else: asset_symbol = st.text_input("Stock Symbol (e.g., AAPL, TSLA)").upper() # AI Suggestion Feature if groq_client and asset_symbol: col1, col2 = st.columns([3, 1]) with col2: if st.button("πŸ€– AI Suggest"): with st.spinner("AI analyzing..."): if asset_type == "Cryptocurrency": current_data = get_crypto_price(asset_symbol) else: current_data = get_stock_price(asset_symbol) if current_data: suggestion = ai_suggest_alert( asset_symbol, current_data['price'], groq_client, model_name ) if suggestion: st.success("AI Recommendations:") st.write(f"**Upper Alert:** ${suggestion['upper_alert']:.2f}") st.write(f"**Lower Alert:** ${suggestion['lower_alert']:.2f}") st.info(suggestion['reasoning']) else: st.error(f"⚠️ {current_data.get('message', 'Could not fetch price')}") col_a, col_b = st.columns(2) with col_a: target_price = st.number_input("Target Price ($)", min_value=0.0, step=0.01) with col_b: alert_type = st.selectbox("Alert When", ["Above", "Below"]) if st.button("βž• Add Alert", type="primary"): if asset_symbol and target_price > 0: alert = { 'type': asset_type, 'asset': asset_symbol, 'target_price': target_price, 'alert_type': alert_type, 'triggered': False, 'added_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } st.session_state.alerts.append(alert) st.success(f"βœ… Alert added for {asset_symbol}!") else: st.error("Please fill all fields correctly.") # --- TAB 3: Charts --- with tab3: st.header("πŸ“ˆ Price Charts") if not st.session_state.price_history: st.info("πŸ“Š Start monitoring to see price charts. Price history will appear here.") else: # Asset selector available_assets = list(st.session_state.price_history.keys()) selected_asset = st.selectbox("Select Asset to View", available_assets) if selected_asset: # Find if there's an alert for this asset target_price = None alert_type = None for alert in st.session_state.alerts: if alert['asset'] == selected_asset: target_price = alert['target_price'] alert_type = alert['alert_type'] break # Create and display chart chart = create_price_chart(selected_asset, target_price, alert_type) if chart: st.plotly_chart(chart, use_container_width=True) # Show statistics history = st.session_state.price_history[selected_asset] prices = [h['price'] for h in history] col1, col2, col3, col4 = st.columns(4) with col1: st.metric("Current", f"${prices[-1]:.2f}") with col2: st.metric("High", f"${max(prices):.2f}") with col3: st.metric("Low", f"${min(prices):.2f}") with col4: change = prices[-1] - prices[0] change_pct = (change / prices[0]) * 100 st.metric("Change", f"${change:.2f}", f"{change_pct:.2f}%") # --- TAB 4: AI Insights --- with tab4: st.header("🧠 AI Market Insights") if not groq_client: st.warning("Please enter your Groq API key in the sidebar to use AI features") else: insight_asset_type = st.selectbox("Select Asset Type", ["Cryptocurrency", "Stock"], key="insight_type") if insight_asset_type == "Cryptocurrency": insight_asset = st.selectbox( "Choose Asset to Analyze", ["bitcoin", "ethereum", "cardano", "solana"], key="insight_crypto" ) else: insight_asset = st.text_input("Enter Stock Symbol", key="insight_stock").upper() if st.button("πŸ” Generate AI Analysis") and insight_asset: with st.spinner("AI analyzing market data..."): if insight_asset_type == "Cryptocurrency": current_data = get_crypto_price(insight_asset) else: current_data = get_stock_price(insight_asset) if current_data and current_data.get('status') == 'success': update_price_history(insight_asset, current_data['price']) analysis = ai_analyze_price_trend( insight_asset, current_data, groq_client, model_name ) st.subheader(f"πŸ“Š Analysis for {insight_asset.upper()}") st.info(analysis) st.session_state.ai_insights[insight_asset] = { 'analysis': analysis, 'timestamp': datetime.now().isoformat() } else: st.error(f"⚠️ {current_data.get('message', 'Could not fetch price data')}") # Show historical insights if st.session_state.ai_insights: st.markdown("---") st.subheader("πŸ“œ Recent Insights") for asset, data in st.session_state.ai_insights.items(): with st.expander(f"{asset.upper()} - {data['timestamp'][:19]}"): st.write(data['analysis']) # Monitoring Loop if st.session_state.monitoring: st.markdown("---") st.info("πŸ” Monitoring active... Checking every 60 seconds") status_placeholder = st.empty() with status_placeholder.container(): st.write(f"**Last Check:** {datetime.now().strftime('%H:%M:%S')}") for idx, alert in enumerate(st.session_state.alerts): if not alert['triggered']: # Get current price if alert['type'] == "Cryptocurrency": current_data = get_crypto_price(alert['asset']) else: current_data = get_stock_price(alert['asset']) if current_data and current_data.get('status') == 'success': current_price = current_data['price'] update_price_history(alert['asset'], current_price) st.write(f"**{alert['asset']}:** ${current_price:.2f} β†’ Target: ${alert['target_price']:.2f} ({alert['alert_type']})") # Check if alert triggered should_trigger = False if alert['alert_type'] == "Above" and current_price >= alert['target_price']: should_trigger = True elif alert['alert_type'] == "Below" and current_price <= alert['target_price']: should_trigger = True if should_trigger: # Get AI insight ai_insight = "" if groq_client: ai_insight = ai_analyze_price_trend( alert['asset'], current_data, groq_client, model_name ) alert_info = { 'asset': alert['asset'], 'current_price': current_price, 'target_price': alert['target_price'], 'alert_type': alert['alert_type'], 'change_24h': current_data.get('change_24h', 0), 'ai_insight': ai_insight } st.success(f"🎯 Alert triggered for {alert['asset']}!") st.write(f"**AI Insight:** {ai_insight}") # Send email if sender_email and sender_password and recipient_email: if send_email_alert(recipient_email, sender_email, sender_password, alert_info): st.success(f"πŸ“§ Email sent to {recipient_email}") st.session_state.alerts[idx]['triggered'] = True else: # Show error and retry message error_msg = current_data.get('message', 'Unknown error') if current_data else 'Connection failed' st.warning(f"⚠️ **{alert['asset']}:** {error_msg} - Will retry in 60 seconds") time.sleep(60) st.rerun() if __name__ == "__main__": main()