Spaces:
Sleeping
Sleeping
| # Install required packages | |
| # !pip install gradio yfinance beautifulsoup4 requests pandas numpy transformers xgboost scikit-learn python-dotenv | |
| # !pip install python-decouple | |
| import gradio as gr | |
| import yfinance as yf | |
| import requests | |
| from bs4 import BeautifulSoup | |
| import pandas as pd | |
| import numpy as np | |
| from transformers import AutoModelForSequenceClassification, AutoTokenizer | |
| import xgboost as xgb | |
| from datetime import datetime, timedelta | |
| import json | |
| import warnings | |
| import os | |
| from dotenv import load_dotenv | |
| from decouple import config | |
| import logging | |
| warnings.filterwarnings('ignore') | |
| load_dotenv() | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') | |
| # WhatsApp API Configuration | |
| WHATSAPP_API_BASE_URL = os.getenv('WHATSAPP_API_BASE_URL') | |
| WHATSAPP_API_KEY = os.getenv('WHATSAPP_API_KEY') | |
| WHATSAPP_INSTANCE_NAME = os.getenv('WHATSAPP_INSTANCE_NAME') | |
| class ConfigManager: | |
| """ | |
| Centralized configuration management | |
| """ | |
| def get_api_config(): | |
| """ | |
| Retrieve API configurations securely | |
| """ | |
| return { | |
| 'amfi_base_url': config('AMFI_API_URL', default='https://api.mfapi.in/mf'), | |
| } | |
| class WhatsAppManager: | |
| def __init__(self, base_url, api_key): | |
| self.base_url = base_url | |
| self.api_key = api_key | |
| def send_message(self, instance, phone, message): | |
| if not self.base_url or not self.api_key or not instance: | |
| logging.error("WhatsApp API base URL, key or instance not configured.") | |
| return "WhatsApp API base URL, key or instance not configured." | |
| headers = { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': self.api_key | |
| } | |
| payload = json.dumps({ | |
| "phone": phone, | |
| "message": message | |
| }) | |
| try: | |
| response = requests.post(f"{self.base_url}/message/sendText/{instance}", headers=headers, data=payload) | |
| response.raise_for_status() | |
| return response.json() | |
| except requests.exceptions.RequestException as e: | |
| logging.error(f"Error sending WhatsApp message: {str(e)}") | |
| return f"Error sending message: {str(e)}" | |
| class AMFIApi: | |
| """ | |
| Mutual Fund API Handler with real-time data fetching | |
| """ | |
| def get_all_mutual_funds(): | |
| """ | |
| Retrieve comprehensive mutual funds list from AMFI API | |
| """ | |
| config = ConfigManager.get_api_config() | |
| try: | |
| response = requests.get(config['amfi_base_url']) | |
| if response.status_code == 200: | |
| return response.json() | |
| else: | |
| logging.error("Failed to fetch mutual fund data.") | |
| return "Error fetching mutual funds." | |
| except Exception as e: | |
| logging.error(f"API request error: {str(e)}") | |
| return f"Error fetching mutual funds: {str(e)}" | |
| def analyze_mutual_fund(scheme_code): | |
| """ | |
| Fetch real-time mutual fund NAV and analyze returns. | |
| """ | |
| try: | |
| config = ConfigManager.get_api_config() | |
| amfi_url = f"{config['amfi_base_url']}/{scheme_code}" | |
| response = requests.get(amfi_url) | |
| if response.status_code != 200: | |
| logging.error("Failed to fetch NAV data.") | |
| return None, None, "Failed to fetch live NAV data." | |
| fund_data = response.json() | |
| if 'data' not in fund_data: | |
| logging.error("Invalid fund data received.") | |
| return None, None, "Invalid fund data received." | |
| nav_data = pd.DataFrame(fund_data['data']) | |
| # Data type conversions | |
| nav_data['date'] = pd.to_datetime(nav_data['date'], format='%d-%m-%Y') | |
| nav_data['nav'] = pd.to_numeric(nav_data['nav'], errors='coerce') | |
| # Sort by date | |
| nav_data = nav_data.sort_values('date') | |
| # Calculate returns | |
| latest_nav = nav_data.iloc[-1]['nav'] | |
| first_nav = nav_data.iloc[0]['nav'] | |
| returns = { | |
| 'scheme_name': fund_data.get('meta', {}).get('scheme_name', 'Unknown'), | |
| 'current_nav': latest_nav, | |
| 'initial_nav': first_nav, | |
| 'total_return': ((latest_nav - first_nav) / first_nav) * 100 | |
| } | |
| return returns, nav_data[['date', 'nav']].rename(columns={'nav': 'NAV'}), None | |
| except Exception as e: | |
| logging.error(f"Analysis error: {str(e)}") | |
| return None, None, f"Analysis error: {str(e)}" | |
| def get_stock_data(symbol, period='3y'): | |
| try: | |
| logging.info(f"Fetching stock data for symbol: {symbol}") | |
| stock = yf.Ticker(symbol) | |
| logging.info(f"Ticker object created successfully for symbol: {symbol}") | |
| hist = stock.history(period=period) | |
| if hist.empty: | |
| logging.error(f"No stock data available for symbol: {symbol} after fetching history.") | |
| return f"No stock data available for symbol: {symbol}" | |
| logging.info(f"Successfully fetched stock data for symbol: {symbol}") | |
| return hist | |
| except Exception as e: | |
| logging.error(f"Error fetching stock data for symbol: {symbol}, error: {str(e)}") | |
| return f"Error fetching stock data: {str(e)}" | |
| def calculate_rsi(data, periods=14): | |
| delta = data.diff() | |
| gain = (delta.where(delta > 0, 0)).rolling(window=periods).mean() | |
| loss = (-delta.where(delta < 0, 0)).rolling(window=periods).mean() | |
| rs = gain / loss | |
| return 100 - (100 / (1 + rs)) | |
| def predict_stock(symbol): | |
| df = get_stock_data(symbol) | |
| if isinstance(df, str): | |
| return df | |
| logging.info(f"Dataframe before feature creation for {symbol}: \n{df.head()}") | |
| df['SMA_20'] = df['Close'].rolling(window=20).mean() | |
| df['SMA_50'] = df['Close'].rolling(window=50).mean() | |
| df['RSI'] = calculate_rsi(df['Close']) | |
| features = ['SMA_20', 'SMA_50', 'RSI', 'Volume'] | |
| X = df[features].dropna() | |
| y = df['Close'].shift(-1).dropna() | |
| logging.info(f"Dataframe after feature creation: \nX:\n{X.head()}\ny:\n{y.head()}") | |
| #Align X and Y | |
| X = X.iloc[:len(y)] | |
| split = int(len(X) * 0.8) | |
| X_train, X_test = X[:split], X[split:] | |
| y_train, y_test = y[:split], y[split:] | |
| logging.info(f"Data split details: \nTrain Data size: {len(X_train)}\nTest Data Size: {len(X_test)}") | |
| if len(X_train) == 0 or len(y_train) == 0: | |
| logging.error(f"Insufficient training data for prediction for symbol: {symbol}") | |
| return "Insufficient data for prediction." | |
| model = xgb.XGBRegressor(objective='reg:squarederror', n_estimators=100) | |
| model.fit(X_train, y_train) | |
| if not X_test.empty: | |
| last_data = X_test.iloc[-1:] | |
| prediction = model.predict(last_data)[0] | |
| return prediction | |
| else: | |
| logging.warning(f"No test data available for prediction for symbol: {symbol}") | |
| return "No test data available for prediction." | |
| def analyze_sentiment(text): | |
| tokenizer = AutoTokenizer.from_pretrained("ProsusAI/finbert") | |
| model = AutoModelForSequenceClassification.from_pretrained("ProsusAI/finbert") | |
| inputs = tokenizer(text, return_tensors="pt", padding=True, truncation=True) | |
| outputs = model(**inputs) | |
| predictions = outputs.logits.softmax(dim=-1) | |
| labels = ['negative', 'neutral', 'positive'] | |
| return {label: float(pred) for label, pred in zip(labels, predictions[0])} | |
| def setup_notifications(phone, stock, mf, sentiment): | |
| if not WHATSAPP_API_BASE_URL or not WHATSAPP_API_KEY or not WHATSAPP_INSTANCE_NAME: | |
| return "WhatsApp API credentials or instance missing." | |
| whatsapp_manager = WhatsAppManager(WHATSAPP_API_BASE_URL, WHATSAPP_API_KEY) | |
| try: | |
| result = whatsapp_manager.send_message( | |
| WHATSAPP_INSTANCE_NAME, | |
| phone, | |
| "🎉 Welcome to AI Finance Manager!\nYour WhatsApp notifications have been set up successfully." | |
| ) | |
| alerts = [] | |
| if stock: alerts.append("Stock") | |
| if mf: alerts.append("Mutual Fund") | |
| if sentiment: alerts.append("Sentiment") | |
| return f"WhatsApp notifications set up for: {', '.join(alerts)} - {result}" | |
| except Exception as e: | |
| logging.error(f"Error setting up notifications: {str(e)}") | |
| return f"Error setting up notifications: {str(e)}" | |
| # Chatbot Function | |
| def chatbot_response(user_input): | |
| user_input = user_input.lower() | |
| if "stock" in user_input: | |
| parts = user_input.split() | |
| if len(parts) > 1: | |
| symbol = parts[-1].upper() | |
| prediction = predict_stock(symbol) | |
| if isinstance(prediction,str): | |
| return prediction | |
| else: | |
| return f"The predicted next-day closing price for {symbol} is {prediction:.2f}" | |
| else: | |
| return "Please provide a stock symbol." | |
| elif "mutual fund" in user_input: | |
| parts = user_input.split() | |
| if len(parts) > 2 and parts[1] == "code": | |
| scheme_code = parts[-1] | |
| mf_returns, mf_nav_history, error = AMFIApi.analyze_mutual_fund(scheme_code) | |
| if error: | |
| return error | |
| else: | |
| return f"Mutual Fund Analysis:\nName: {mf_returns.get('scheme_name', 'Unknown')}\nCurrent NAV: {mf_returns.get('current_nav', 'N/A'):.2f}\nTotal Return: {mf_returns.get('total_return', 'N/A'):.2f}%" | |
| else: | |
| return "Please enter the mutual fund scheme code for analysis (e.g. 'analyze mutual fund code 123456')." | |
| elif "sentiment" in user_input: | |
| return "Enter the financial news text for sentiment analysis." | |
| elif user_input.startswith("analyze sentiment"): | |
| text = user_input[len("analyze sentiment"):].strip() | |
| if text: | |
| sentiment_result = analyze_sentiment(text) | |
| if sentiment_result: | |
| return f"Sentiment Analysis: {sentiment_result}" | |
| else: | |
| return "No text provided for sentiment analysis." | |
| else: | |
| return "Please provide text for sentiment analysis." | |
| return "I can help with Stock Analysis, Mutual Funds, and Sentiment Analysis. Please ask your query." | |
| # Create Gradio Interface | |
| def create_gradio_interface(): | |
| with gr.Blocks() as app: | |
| gr.Markdown("# AI Finance & Stock Manager with Chat and WhatsApp Alerts") | |
| with gr.Tab("Chat"): | |
| chat_input = gr.Textbox(label="Ask about Stocks, Mutual Funds, or Sentiment Analysis") | |
| chat_output = gr.Textbox(label="AI Response", interactive=False) | |
| chat_btn = gr.Button("Ask AI") | |
| with gr.Tab("Stock Analysis"): | |
| stock_input = gr.Textbox(label="Enter Stock Symbol (e.g., AAPL)") | |
| stock_btn = gr.Button("Analyze Stock") | |
| stock_output = gr.DataFrame() | |
| prediction_output = gr.Number(label="Predicted Next Day Close Price") | |
| with gr.Tab("Mutual Fund Analysis"): | |
| mf_code = gr.Textbox(label="Enter Scheme Code") | |
| mf_analyze_btn = gr.Button("Analyze Fund") | |
| # Analysis Outputs | |
| mf_returns = gr.JSON(label="Fund Returns") | |
| mf_nav_history = gr.DataFrame(label="NAV History") | |
| mf_analysis_error = gr.Textbox(label="Error Messages", visible=False) | |
| with gr.Tab("WhatsApp Notifications"): | |
| phone_input = gr.Textbox(label="WhatsApp Number (with country code)") | |
| enable_stock_alerts = gr.Checkbox(label="Stock Alerts") | |
| enable_mf_alerts = gr.Checkbox(label="Mutual Fund Alerts") | |
| enable_sentiment_alerts = gr.Checkbox(label="Sentiment Alerts") | |
| notification_status = gr.Textbox(label="Notification Status", interactive=False) | |
| setup_btn = gr.Button("Setup WhatsApp Notifications") | |
| with gr.Tab("Sentiment Analysis"): | |
| text_input = gr.Textbox(label="Enter financial news or text") | |
| sentiment_btn = gr.Button("Analyze Sentiment") | |
| sentiment_output = gr.Label() | |
| # Event Handlers | |
| chat_btn.click( | |
| fn=chatbot_response, | |
| inputs=chat_input, | |
| outputs=chat_output | |
| ) | |
| stock_btn.click( | |
| fn=lambda x: (get_stock_data(x), predict_stock(x)), | |
| inputs=stock_input, | |
| outputs=[stock_output, prediction_output] | |
| ) | |
| mf_analyze_btn.click( | |
| fn=AMFIApi.analyze_mutual_fund, | |
| inputs=mf_code, | |
| outputs=[mf_returns,mf_nav_history,mf_analysis_error] | |
| ) | |
| sentiment_btn.click( | |
| fn=analyze_sentiment, | |
| inputs=text_input, | |
| outputs=sentiment_output | |
| ) | |
| setup_btn.click( | |
| fn=setup_notifications, | |
| inputs=[phone_input, enable_stock_alerts, enable_mf_alerts, enable_sentiment_alerts], | |
| outputs=notification_status | |
| ) | |
| return app | |
| # Launch the app | |
| if __name__ == "__main__": | |
| app = create_gradio_interface() | |
| app.launch(share=True, debug=True) |