import gradio as gr import yfinance as yf import pandas as pd import numpy as np from datetime import datetime import plotly.graph_objects as go from plotly.subplots import make_subplots import pixeltable as pxt from pixeltable.functions import openai import json import os import getpass from typing import Dict, Any # Set up OpenAI API key if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('Enter your OpenAI API key: ') class NumpyEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, (np.int_, np.intc, np.intp, np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64)): return int(obj) elif isinstance(obj, (np.float_, np.float16, np.float32, np.float64)): return float(obj) elif isinstance(obj, (np.ndarray,)): return obj.tolist() return json.JSONEncoder.default(self, obj) def safe_json_serialize(obj): return json.dumps(obj, cls=NumpyEncoder) def calculate_basic_indicators(data: pd.DataFrame) -> pd.DataFrame: df = data.copy() # Moving averages df['MA20'] = df['Close'].rolling(window=20).mean() df['MA50'] = df['Close'].rolling(window=50).mean() df['MA200'] = df['Close'].rolling(window=200).mean() # RSI delta = df['Close'].diff() gain = (delta.where(delta > 0, 0)).rolling(window=14).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean() rs = gain / loss df['RSI'] = 100 - (100 / (1 + rs)) # MACD exp1 = df['Close'].ewm(span=12, adjust=False).mean() exp2 = df['Close'].ewm(span=26, adjust=False).mean() df['MACD'] = exp1 - exp2 df['MACD_Signal'] = df['MACD'].ewm(span=9, adjust=False).mean() return df.ffill().bfill() @pxt.udf def generate_analysis_prompt(data: str, analysis_type: str, time_horizon: str, risk_tolerance: str, investment_style: str, technical_depth: str) -> list[dict]: """Generate a structured prompt for AI analysis""" # Create specific guidance based on parameters time_horizon_guidance = { 'short': 'Focus on short-term trading opportunities within 1-3 months. Emphasize technical signals and immediate catalysts.', 'medium': 'Balance short-term opportunities with medium-term trends over 3-12 months. Consider both technical and fundamental factors.', 'long': 'Prioritize long-term growth potential over 1+ years. Emphasize fundamental analysis and strategic positioning.' } risk_tolerance_guidance = { 'conservative': 'Prioritize capital preservation. Focus on stable, large-cap stocks with strong fundamentals. Suggest conservative options strategies like covered calls.', 'moderate': 'Balance growth opportunities with risk management. Consider mid-cap stocks and moderate options strategies.', 'aggressive': 'Seek high-growth opportunities. Include small-cap stocks and more aggressive options strategies in the analysis.' } investment_style_guidance = { 'value': 'Focus on valuation metrics, margin of safety, and undervalued opportunities.', 'growth': 'Emphasize revenue growth, market expansion, and future potential.', 'momentum': 'Focus on price trends, relative strength, and technical indicators.', 'balanced': 'Consider both value and growth factors, maintaining a balanced perspective.', 'income': 'Prioritize dividend yield, payout ratio, and income-generating options strategies.' } analysis_depth = { 'comprehensive': 'Provide detailed analysis across all categories.', 'quantitative': 'Focus on numerical metrics and statistical analysis.', 'technical': 'Emphasize technical analysis and chart patterns.' } technical_guidance = { 'basic': 'Focus on essential technical indicators (MA, RSI, MACD).', 'advanced': 'Include advanced technical analysis including Fibonacci levels, Elliott Wave patterns, and advanced options analysis.' } system_prompt = f'''You are a senior investment advisor and market analyst with decades of experience, holding CFA and CMT certifications. Provide analysis tailored to the following parameters: TIME HORIZON: {time_horizon.upper()} {time_horizon_guidance[time_horizon]} RISK PROFILE: {risk_tolerance.upper()} {risk_tolerance_guidance[risk_tolerance]} INVESTMENT STYLE: {investment_style.upper()} {investment_style_guidance[investment_style]} ANALYSIS FOCUS: {analysis_type.upper()} {analysis_depth[analysis_type]} TECHNICAL DEPTH: {technical_depth.upper()} {technical_guidance[technical_depth]} Structure your response using EXACTLY the following format and sections: SUMMARY Provide a clear 2-3 sentence executive summary aligned with the specified time horizon ({time_horizon}) and risk tolerance ({risk_tolerance}). TECHNICAL ANALYSIS {technical_guidance[technical_depth]} • Moving Averages: Analyze trends with emphasis on {time_horizon} timeframe • RSI Analysis: Current RSI level and implications • MACD Analysis: Signal trends relevant to {time_horizon} horizon • Volume Analysis: Notable volume patterns and implications {"• Advanced Patterns: Fibonacci levels, Elliott Wave analysis" if technical_depth == "advanced" else ""} MARKET CONTEXT • Sector Analysis: {"Long-term industry trends and positioning" if time_horizon == "long" else "Short-term sector momentum" if time_horizon == "short" else "Medium-term sector outlook"} • {"Fundamental Drivers: Key growth catalysts" if investment_style == "growth" else "Value Metrics: Key valuation catalysts" if investment_style == "value" else "Market Dynamics: Key price catalysts"} • Economic Impact: Factors relevant to {time_horizon} horizon RISKS • Identify and quantify risks specific to {risk_tolerance} risk tolerance • Focus on {time_horizon} horizon risks • {"Include volatility analysis" if technical_depth == "advanced" else ""} OPPORTUNITIES • Align opportunities with {investment_style} style • Focus on {time_horizon} timeline • Match risk/reward to {risk_tolerance} profile OPTIONS STRATEGY {"• Advanced Options Analysis: Complex strategies and volatility analysis" if technical_depth == "advanced" else "• Basic Options Strategies: Simple hedging and income generation"} • Tailor strategies to {risk_tolerance} risk tolerance • Focus on {time_horizon} expiration cycles • Match strategies to {investment_style} style objectives RECOMMENDATION • Provide specific recommendations aligned with: - {time_horizon.capitalize()} time horizon - {risk_tolerance.capitalize()} risk tolerance - {investment_style.capitalize()} investment style • Include position sizing appropriate for risk level • Specify entry, exit, and stop-loss levels • {"Include advanced technical levels" if technical_depth == "advanced" else "Focus on key support/resistance levels"} IMPORTANT: This analysis is for informational purposes only. All investments carry risk. Please consult with a licensed financial advisor before making investment decisions.''' return [ {'role': 'system', 'content': system_prompt}, {'role': 'user', 'content': f'Analyze this market data considering the specified parameters:\n{data}'} ] def parse_analysis_response(response: str) -> Dict[str, str]: """Parse the structured AI response into sections with support for markdown formatting""" sections = { 'SUMMARY': None, 'TECHNICAL ANALYSIS': None, 'MARKET CONTEXT': None, 'RISKS': None, 'OPPORTUNITIES': None, 'OPTIONS STRATEGY': None, 'RECOMMENDATION': None } current_section = None buffer = [] if not response or not response.strip(): return {k: "Analysis not available" for k in sections.keys()} for line in response.split('\n'): line = line.strip() matched_section = None for section in sections.keys(): # Remove asterisks and check for exact match cleaned_line = line.replace('*', '').strip() if cleaned_line == section: matched_section = section break if matched_section: # Save previous section if exists if current_section and buffer: sections[current_section] = '\n'.join(buffer).strip() current_section = matched_section buffer = [] elif current_section and line: # Clean up markdown formatting in content cleaned_content = line.replace('*', '').strip() if cleaned_content: # Only add non-empty lines buffer.append(cleaned_content) if current_section and buffer: sections[current_section] = '\n'.join(buffer).strip() section_messages = { 'SUMMARY': 'Market analysis summary not available', 'TECHNICAL ANALYSIS': 'Technical analysis not available', 'MARKET CONTEXT': 'Market context information not available', 'RISKS': 'Risk assessment not available', 'OPPORTUNITIES': 'Opportunity analysis not available', 'OPTIONS STRATEGY': 'Options trading analysis not available', 'RECOMMENDATION': 'Investment recommendation not available' } for key in sections: if sections[key] is None or not sections[key].strip(): sections[key] = section_messages[key] return sections def create_visualization(data: pd.DataFrame, technical_depth: str) -> go.Figure: fig = make_subplots( rows=3 if technical_depth == 'advanced' else 2, cols=1, shared_xaxes=True, vertical_spacing=0.05, subplot_titles=('Price & Moving Averages', 'Volume', 'RSI' if technical_depth == 'advanced' else None) ) fig.add_trace( go.Candlestick( x=data.index, open=data['Open'], high=data['High'], low=data['Low'], close=data['Close'], name='Price', increasing_line_color='#26A69A', decreasing_line_color='#EF5350' ), row=1, col=1 ) colors = {'MA20': '#1E88E5', 'MA50': '#FFC107', 'MA200': '#7B1FA2'} for ma, color in colors.items(): fig.add_trace( go.Scatter( x=data.index, y=data[ma], name=ma, line=dict(color=color, width=1.5) ), row=1, col=1 ) colors = ['#26A69A' if close >= open_price else '#EF5350' for close, open_price in zip(data['Close'].values, data['Open'].values)] fig.add_trace( go.Bar( x=data.index, y=data['Volume'], name='Volume', marker_color=colors ), row=2, col=1 ) if technical_depth == 'advanced': fig.add_trace( go.Scatter( x=data.index, y=data['RSI'], name='RSI', line=dict(color='#7C4DFF', width=1.5) ), row=3, col=1 ) fig.add_hline(y=70, line_dash="dash", line_color="red", row=3, col=1) fig.add_hline(y=30, line_dash="dash", line_color="green", row=3, col=1) fig.update_layout( height=800, template='plotly_white', showlegend=True, legend=dict( orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1 ) ) fig.update_yaxes(title_text="Price", row=1, col=1) fig.update_yaxes(title_text="Volume", row=2, col=1) if technical_depth == 'advanced': fig.update_yaxes(title_text="RSI", row=3, col=1) return fig def process_outputs(ticker_symbol, analysis_type, time_horizon, risk_tolerance, investment_style, technical_depth, include_market_context=True, max_positions=3): try: # Initialize Pixeltable pxt.drop_dir('financial_analysis', force=True) pxt.create_dir('financial_analysis') data_table = pxt.create_table( 'financial_analysis.stock_data', { 'ticker': pxt.StringType(), 'data': pxt.StringType(), 'timestamp': pxt.TimestampType() } ) # Fetch and process data stock = yf.Ticker(ticker_symbol.strip().upper()) market_data = stock.history(period='1y') if market_data.empty: raise ValueError("No data found for the specified ticker symbol.") options_data = stock.options # Get available expiration dates if len(options_data) > 0: nearest_expiry = options_data[0] opt_chain = stock.option_chain(nearest_expiry) options_info = { 'Nearest Expiry': nearest_expiry, 'Call Volume': int(opt_chain.calls['volume'].sum()), 'Put Volume': int(opt_chain.puts['volume'].sum()), 'Put/Call Ratio': float(opt_chain.puts['volume'].sum() / opt_chain.calls['volume'].sum()), 'Implied Volatility': float(opt_chain.calls['impliedVolatility'].mean()) } else: options_info = { 'Nearest Expiry': 'N/A', 'Call Volume': 0, 'Put Volume': 0, 'Put/Call Ratio': 0, 'Implied Volatility': 0 } technical_data = calculate_basic_indicators(market_data) market_data_json = technical_data.to_json(date_format='iso') # Store data and generate analysis data_table.insert([{ 'ticker': ticker_symbol.upper(), 'data': market_data_json, 'timestamp': datetime.now() }]) data_table['prompt'] = generate_analysis_prompt( data_table.data, analysis_type, time_horizon, risk_tolerance, investment_style, technical_depth ) data_table['analysis'] = openai.chat_completions( messages=data_table.prompt, model='gpt-4o-mini-2024-07-18', temperature=0.7, max_tokens=1000 ) try: analysis_text = data_table.select( analysis=data_table.analysis.choices[0].message.content ).tail(1)['analysis'][0] parsed_analysis = parse_analysis_response(analysis_text) except Exception as analysis_error: print(f"Analysis error: {str(analysis_error)}") parsed_analysis = parse_analysis_response("") # This will return default messages # Prepare company info with proper JSON formatting company_info_data = { 'Name': str(stock.info.get('longName', 'N/A')), 'Sector': str(stock.info.get('sector', 'N/A')), 'Industry': str(stock.info.get('industry', 'N/A')), 'Exchange': str(stock.info.get('exchange', 'N/A')) } raw_llm_output = "" try: raw_llm_output = data_table.select( analysis=data_table.analysis.choices[0].message.content ).tail(1)['analysis'][0] parsed_analysis = parse_analysis_response(raw_llm_output) except Exception as analysis_error: print(f"Analysis error: {str(analysis_error)}") parsed_analysis = parse_analysis_response("") raw_llm_output = f"Error processing analysis: {str(analysis_error)}" try: current_price = float(technical_data['Close'].iloc[-1]) previous_price = float(technical_data['Close'].iloc[-2]) daily_change = float((current_price / previous_price - 1) * 100) volume = int(technical_data['Volume'].iloc[-1]) rsi = float(technical_data['RSI'].iloc[-1]) except (IndexError, KeyError, TypeError): current_price = daily_change = volume = rsi = 0 market_stats_data = { 'Current Price': f"${current_price:.2f}", 'Daily Change': f"{daily_change:.2f}%", 'Volume': f"{volume:,}", 'RSI': f"{rsi:.2f}" } technical_data_with_time = technical_data.reset_index() technical_data_with_time['Date'] = technical_data_with_time['Date'].dt.strftime('%Y-%m-%d %H:%M:%S') # Create visualization plot = create_visualization(technical_data, technical_depth) return ( json.dumps(company_info_data), json.dumps(market_stats_data), json.dumps(options_info), # Add options info to return values plot, parsed_analysis['SUMMARY'], parsed_analysis['TECHNICAL ANALYSIS'], parsed_analysis['MARKET CONTEXT'], parsed_analysis['RISKS'], parsed_analysis['OPPORTUNITIES'], parsed_analysis['OPTIONS STRATEGY'], # Add options strategy parsed_analysis['RECOMMENDATION'], technical_data_with_time, raw_llm_output ) except Exception as e: error_msg = f"Error processing data: {str(e)}" empty_json = json.dumps({}) no_data_msg = "Analysis not available due to data processing error" empty_df = pd.DataFrame() return ( empty_json, empty_json, empty_json, None, no_data_msg, no_data_msg, no_data_msg, no_data_msg, no_data_msg, no_data_msg, no_data_msg, empty_df, f"Error occurred: {str(e)}" ) def create_interface() -> gr.Blocks: """Create the production-ready Gradio interface""" with gr.Blocks(theme=gr.themes.Base()) as demo: # Header gr.Markdown( """ # 📈 AI Financial Analysis Platform AI-powered market analysis and technical indicators powered by OpenAI GPT models and Pixeltable. """ ) gr.HTML( """
This is a demonstration platform showcasing how to leverage LLMs for financial analysis. The accuracy of information, data retrieved from yfinance, and calculated metrics are NOT guaranteed. Use at your own risk. The creators and operators of this tool are not responsible for any financial losses or decisions made based on this analysis. This tool is intended for demonstration and educational purposes only.
This tool provides financial analysis for informational purposes only and should not be considered as financial advice, please:
Open Source AI Data infrastructure for building intelligent applications.
© 2024 AI Financial Analysis Platform powered by Pixeltable. This work is licensed under the Apache License 2.0.