import streamlit as st import os from langchain_openai import ChatOpenAI import yfinance as yf import pandas as pd import plotly.graph_objects as go from datetime import datetime # Configure page settings st.set_page_config(page_title="Indian Stock Thematic Analyzer", layout="wide", initial_sidebar_state="expanded") # Indian Stock Universe - Extended list INDIAN_STOCKS = { 'RELIANCE.NS': 'Reliance Industries', 'TCS.NS': 'Tata Consultancy Services', 'HDFCBANK.NS': 'HDFC Bank', 'INFY.NS': 'Infosys', 'ICICIBANK.NS': 'ICICI Bank', 'HINDUNILVR.NS': 'Hindustan Unilever', 'SBIN.NS': 'State Bank of India', 'BHARTIARTL.NS': 'Bharti Airtel', 'ITC.NS': 'ITC Limited', 'KOTAKBANK.NS': 'Kotak Mahindra Bank', 'WIPRO.NS': 'Wipro Limited', 'HCLTECH.NS': 'HCL Technologies', 'ADANIENT.NS': 'Adani Enterprises', 'SUNPHARMA.NS': 'Sun Pharmaceutical', 'ASIANPAINT.NS': 'Asian Paints' } def get_thematic_keywords(theme, api_key): """Generate theme-related keywords using GPT-4""" try: os.environ["OPENAI_API_KEY"] = api_key llm = ChatOpenAI(model_name="gpt-4o-mini") prompt = f"""As an Indian stock market expert, generate: 1. A comprehensive list of keywords related to {theme} theme 2. Include technical terms, industry jargon, emerging trends specific to Indian market 3. Consider both established Indian companies and emerging players Format the output as a comma-separated list.""" response = llm.invoke(prompt) keywords = [k.strip().lower() for k in response.content.split(',')] return keywords except Exception as e: st.error(f"Error generating keywords: {str(e)}") return [] def get_stock_data(ticker): """Fetch comprehensive stock data with company description""" try: stock = yf.Ticker(ticker) info = stock.info hist = stock.history(period='6mo') momentum = ((hist['Close'][-1] / hist['Close'][0] - 1) * 100) if len(hist) > 0 else 0 return { 'name': info.get('longName', ticker), 'sector': info.get('sector', 'N/A'), 'industry': info.get('industry', 'N/A'), 'market_cap': info.get('marketCap', 0), 'beta': info.get('beta', 0), 'momentum': momentum, 'current_price': info.get('currentPrice', 0), 'pe_ratio': info.get('forwardPE', 0), 'revenue_growth': info.get('revenueGrowth', 0), 'description': info.get('longBusinessSummary', '').lower(), 'website': info.get('website', 'N/A') } except Exception as e: st.warning(f"Error fetching data for {ticker}: {str(e)}") return None def keyword_match_score(description, keywords): """Calculate keyword match score for a stock""" if not description or not keywords: return 0 matches = sum(1 for keyword in keywords if keyword in description) return (matches / len(keywords)) * 100 def plot_theme_composition(df): """Create sector composition visualization""" sector_counts = df['sector'].value_counts() fig = go.Figure(data=[go.Pie(labels=sector_counts.index, values=sector_counts.values)]) fig.update_layout(title="Sector Composition") return fig def main(): st.title("🎯 Indian Stock Thematic Analyzer") st.markdown("### AI-Powered Indian Stock Market Analysis") with st.sidebar: # Adjust default values to be more inclusive min_market_cap = st.number_input("Minimum Market Cap (₹ Crores)", value=100, step=100) # Lower minimum min_momentum = st.slider("Minimum Momentum (%)", -50, 50, -20) # Allow negative momentum min_inst_ownership = st.slider("Min Institutional Ownership (%)", 0, 100, 0) # Start from 0 beta_range = st.slider("Beta Range", -2.0, 4.0, (0.0, 2.0)) # Wider range min_pe = st.number_input("Maximum P/E Ratio", value=100) # Higher PE allowed min_revenue_growth = st.slider("Minimum Revenue Growth (%)", -100, 100, -20) # Allow negative growth # 2. Modify the stock filtering logic if data and data['market_cap'] >= min_market_cap * 1e6: # Basic market cap check should_include = True # Check each criterion individually with error handling if pd.notna(data['momentum']) and data['momentum'] < min_momentum: should_include = False if pd.notna(data['inst_ownership']) and data['inst_ownership'] < min_inst_ownership: should_include = False if pd.notna(data['beta']) and (data['beta'] < beta_range[0] or data['beta'] > beta_range[1]): should_include = False if pd.notna(data['pe_ratio']) and data['pe_ratio'] > min_pe: should_include = False if pd.notna(data['revenue_growth']) and (data['revenue_growth'] * 100) < min_revenue_growth: should_include = False if should_include: stock_data.append({**data, 'ticker': ticker}) col1, col2 = st.columns([2, 3]) with col1: st.markdown("## Theme Configuration") theme = st.text_input("Enter Investment Theme:", placeholder="e.g., AI, Clean Energy, EV") generate_button = st.button("Generate Thematic Basket") if generate_button and api_key and theme: with st.spinner("Analyzing theme and generating investment basket..."): keywords = get_thematic_keywords(theme, api_key) if keywords: st.success(f"Generated {len(keywords)} thematic keywords") with st.expander("View Keywords"): st.write(keywords) progress_bar = st.progress(0) stock_data = [] for i, (ticker, name) in enumerate(INDIAN_STOCKS.items()): data = get_stock_data(ticker) if data: match_score = keyword_match_score(data['description'], keywords) if (match_score >= min_keyword_match and data['market_cap'] >= min_market_cap * 10000000 and data['momentum'] >= min_momentum and beta_range[0] <= data['beta'] <= beta_range[1] and data['pe_ratio'] <= min_pe and data['revenue_growth'] * 100 >= min_revenue_growth): stock_data.append({ **data, 'ticker': ticker, 'theme_match': match_score }) progress_bar.progress((i + 1) / len(INDIAN_STOCKS)) if stock_data: st.session_state.results_df = pd.DataFrame(stock_data) else: st.warning("No stocks met the specified criteria.") with col2: if 'results_df' in st.session_state: st.markdown("## Investment Basket Results") df = st.session_state.results_df metrics_col1, metrics_col2, metrics_col3 = st.columns(3) with metrics_col1: st.metric("Number of Stocks", len(df)) with metrics_col2: st.metric("Average Market Cap (₹ Cr)", f"{df['market_cap'].mean() / 10000000:.1f}") with metrics_col3: st.metric("Average Theme Match", f"{df['theme_match'].mean():.1f}%") st.dataframe(df.style.format({ 'market_cap': '₹{:,.0f}', 'momentum': '{:.1f}%', 'beta': '{:.2f}', 'current_price': '₹{:.2f}', 'pe_ratio': '{:.1f}', 'revenue_growth': '{:.1%}', 'theme_match': '{:.1f}%' })) st.plotly_chart(plot_theme_composition(df)) csv = df.to_csv(index=False) st.download_button( label="📥 Download Investment Basket", data=csv, file_name=f"{theme}_investment_basket_{datetime.now().strftime('%Y%m%d')}.csv", mime="text/csv" ) if __name__ == "__main__": main()