from fastapi import FastAPI, HTTPException, Query from pydantic import BaseModel import talib import numpy as np from typing import List, Optional, Dict, Any import yfinance as yf from datetime import datetime, timedelta from enum import Enum import pandas as pd app = FastAPI( title="Elite US Stock Tracker API", version="2.0.0", description="Advanced technical analysis for top 5 US companies with actionable trading signals" ) # Top 5 US Companies by Market Cap (2024) class EliteStock(str, Enum): APPLE = "AAPL" MICROSOFT = "MSFT" NVIDIA = "NVDA" ALPHABET = "GOOGL" AMAZON = "AMZN" class TimeFrame(str, Enum): ONE_WEEK = "7d" ONE_MONTH = "1mo" THREE_MONTHS = "3mo" SIX_MONTHS = "6mo" ONE_YEAR = "1y" class TradingSignal(str, Enum): STRONG_BUY = "STRONG_BUY" BUY = "BUY" HOLD = "HOLD" SELL = "SELL" STRONG_SELL = "STRONG_SELL" class PositionRecommendation(BaseModel): signal: TradingSignal confidence: float # 0-100% entry_price: Optional[float] = None stop_loss: Optional[float] = None take_profit: Optional[float] = None position_size: str # Small, Medium, Large reason: str class TechnicalIndicators(BaseModel): sma_20: float sma_50: float ema_12: float ema_26: float rsi: float macd: float macd_signal: float macd_histogram: float bollinger_upper: float bollinger_middle: float bollinger_lower: float atr: float stoch_k: float stoch_d: float williams_r: float adx: float class MarketMetrics(BaseModel): current_price: float price_change_24h: float price_change_pct_24h: float volume: int avg_volume_20d: float market_cap: Optional[float] = None pe_ratio: Optional[float] = None support_level: float resistance_level: float class ComprehensiveAnalysis(BaseModel): symbol: str company_name: str last_updated: datetime market_metrics: MarketMetrics technical_indicators: TechnicalIndicators position_recommendation: PositionRecommendation key_levels: Dict[str, float] trend_analysis: Dict[str, Any] @app.get("/") async def root(): return { "message": "Elite US Stock Tracker API - Top 5 Companies", "version": "2.0.0", "supported_stocks": [stock.value for stock in EliteStock], "features": [ "Advanced technical analysis", "Position recommendations", "Support/Resistance levels", "Multi-timeframe analysis", "Risk management signals" ] } @app.get("/stocks") async def list_elite_stocks(): """Get information about all supported elite stocks""" stock_info = { "AAPL": {"name": "Apple Inc.", "sector": "Technology"}, "MSFT": {"name": "Microsoft Corporation", "sector": "Technology"}, "NVDA": {"name": "NVIDIA Corporation", "sector": "Technology"}, "GOOGL": {"name": "Alphabet Inc.", "sector": "Technology"}, "AMZN": {"name": "Amazon.com Inc.", "sector": "Consumer Discretionary"} } return { "elite_stocks": stock_info, "total_count": len(stock_info), "last_updated": datetime.now() } def calculate_support_resistance(prices: np.ndarray, window: int = 20) -> tuple: """Calculate dynamic support and resistance levels""" recent_prices = prices[-window:] support = np.min(recent_prices) resistance = np.max(recent_prices) return support, resistance def generate_trading_signal(indicators: dict, market_data: dict) -> PositionRecommendation: """Generate intelligent trading signals based on multiple indicators""" score = 0 reasons = [] # RSI Analysis rsi = indicators['rsi'] if rsi < 30: score += 2 reasons.append("RSI oversold (bullish)") elif rsi > 70: score -= 2 reasons.append("RSI overbought (bearish)") elif 40 <= rsi <= 60: score += 1 reasons.append("RSI neutral zone") # MACD Analysis if indicators['macd'] > indicators['macd_signal']: score += 1 reasons.append("MACD bullish crossover") else: score -= 1 reasons.append("MACD bearish signal") # Moving Average Analysis current_price = market_data['current_price'] if current_price > indicators['sma_50']: score += 1 reasons.append("Price above 50-day SMA") else: score -= 1 reasons.append("Price below 50-day SMA") # Bollinger Bands Analysis bb_position = (current_price - indicators['bollinger_lower']) / (indicators['bollinger_upper'] - indicators['bollinger_lower']) if bb_position < 0.2: score += 1 reasons.append("Near lower Bollinger Band (potential bounce)") elif bb_position > 0.8: score -= 1 reasons.append("Near upper Bollinger Band (potential reversal)") # ADX Trend Strength if indicators['adx'] > 25: if score > 0: score += 1 reasons.append("Strong trend confirms bullish bias") else: score -= 1 reasons.append("Strong trend confirms bearish bias") # Determine signal and confidence if score >= 4: signal = TradingSignal.STRONG_BUY confidence = min(95, 70 + score * 5) position_size = "Large" elif score >= 2: signal = TradingSignal.BUY confidence = min(85, 60 + score * 5) position_size = "Medium" elif score <= -4: signal = TradingSignal.STRONG_SELL confidence = min(95, 70 + abs(score) * 5) position_size = "Large" elif score <= -2: signal = TradingSignal.SELL confidence = min(85, 60 + abs(score) * 5) position_size = "Medium" else: signal = TradingSignal.HOLD confidence = 50 + abs(score) * 10 position_size = "Small" # Calculate risk management levels atr = indicators['atr'] if signal in [TradingSignal.STRONG_BUY, TradingSignal.BUY]: entry_price = current_price stop_loss = current_price - (2 * atr) take_profit = current_price + (3 * atr) elif signal in [TradingSignal.STRONG_SELL, TradingSignal.SELL]: entry_price = current_price stop_loss = current_price + (2 * atr) take_profit = current_price - (3 * atr) else: entry_price = None stop_loss = None take_profit = None return PositionRecommendation( signal=signal, confidence=confidence, entry_price=entry_price, stop_loss=stop_loss, take_profit=take_profit, position_size=position_size, reason="; ".join(reasons) ) @app.get("/analysis/{symbol}", response_model=ComprehensiveAnalysis) async def get_comprehensive_analysis( symbol: EliteStock, timeframe: TimeFrame = TimeFrame.THREE_MONTHS ): """Get comprehensive technical analysis for an elite stock""" try: # Fetch stock data ticker = yf.Ticker(symbol.value) hist = ticker.history(period=timeframe.value) info = ticker.info if hist.empty: raise HTTPException(status_code=404, detail=f"No data found for {symbol.value}") # Extract price data closes = hist['Close'].values highs = hist['High'].values lows = hist['Low'].values volumes = hist['Volume'].values # Calculate technical indicators sma_20 = talib.SMA(closes, timeperiod=20) sma_50 = talib.SMA(closes, timeperiod=50) ema_12 = talib.EMA(closes, timeperiod=12) ema_26 = talib.EMA(closes, timeperiod=26) rsi = talib.RSI(closes, timeperiod=14) macd, macd_signal, macd_hist = talib.MACD(closes) bb_upper, bb_middle, bb_lower = talib.BBANDS(closes) atr = talib.ATR(highs, lows, closes, timeperiod=14) stoch_k, stoch_d = talib.STOCH(highs, lows, closes) williams_r = talib.WILLR(highs, lows, closes) adx = talib.ADX(highs, lows, closes) # Get latest values current_price = float(closes[-1]) latest_indicators = { 'sma_20': float(sma_20[-1]) if not np.isnan(sma_20[-1]) else current_price, 'sma_50': float(sma_50[-1]) if not np.isnan(sma_50[-1]) else current_price, 'ema_12': float(ema_12[-1]) if not np.isnan(ema_12[-1]) else current_price, 'ema_26': float(ema_26[-1]) if not np.isnan(ema_26[-1]) else current_price, 'rsi': float(rsi[-1]) if not np.isnan(rsi[-1]) else 50.0, 'macd': float(macd[-1]) if not np.isnan(macd[-1]) else 0.0, 'macd_signal': float(macd_signal[-1]) if not np.isnan(macd_signal[-1]) else 0.0, 'macd_histogram': float(macd_hist[-1]) if not np.isnan(macd_hist[-1]) else 0.0, 'bollinger_upper': float(bb_upper[-1]) if not np.isnan(bb_upper[-1]) else current_price * 1.02, 'bollinger_middle': float(bb_middle[-1]) if not np.isnan(bb_middle[-1]) else current_price, 'bollinger_lower': float(bb_lower[-1]) if not np.isnan(bb_lower[-1]) else current_price * 0.98, 'atr': float(atr[-1]) if not np.isnan(atr[-1]) else current_price * 0.02, 'stoch_k': float(stoch_k[-1]) if not np.isnan(stoch_k[-1]) else 50.0, 'stoch_d': float(stoch_d[-1]) if not np.isnan(stoch_d[-1]) else 50.0, 'williams_r': float(williams_r[-1]) if not np.isnan(williams_r[-1]) else -50.0, 'adx': float(adx[-1]) if not np.isnan(adx[-1]) else 25.0 } # Calculate support and resistance support, resistance = calculate_support_resistance(closes) # Market metrics price_change_24h = float(closes[-1] - closes[-2]) if len(closes) > 1 else 0.0 price_change_pct_24h = (price_change_24h / closes[-2] * 100) if len(closes) > 1 else 0.0 avg_volume_20d = float(np.mean(volumes[-20:])) if len(volumes) >= 20 else float(volumes[-1]) market_metrics = MarketMetrics( current_price=current_price, price_change_24h=price_change_24h, price_change_pct_24h=price_change_pct_24h, volume=int(volumes[-1]), avg_volume_20d=avg_volume_20d, market_cap=info.get('marketCap'), pe_ratio=info.get('trailingPE'), support_level=float(support), resistance_level=float(resistance) ) # Generate trading recommendation position_rec = generate_trading_signal(latest_indicators, {'current_price': current_price}) # Key levels analysis key_levels = { "pivot_point": float((highs[-1] + lows[-1] + closes[-1]) / 3), "fibonacci_618": float(support + (resistance - support) * 0.618), "fibonacci_382": float(support + (resistance - support) * 0.382), "vwap": float(np.average(closes[-20:], weights=volumes[-20:])) if len(closes) >= 20 else current_price } # Trend analysis trend_analysis = { "short_term_trend": "bullish" if latest_indicators['ema_12'] > latest_indicators['ema_26'] else "bearish", "medium_term_trend": "bullish" if current_price > latest_indicators['sma_50'] else "bearish", "trend_strength": "strong" if latest_indicators['adx'] > 25 else "weak", "volatility": "high" if latest_indicators['atr'] / current_price > 0.03 else "normal" } return ComprehensiveAnalysis( symbol=symbol.value, company_name=info.get('longName', symbol.value), last_updated=datetime.now(), market_metrics=market_metrics, technical_indicators=TechnicalIndicators(**latest_indicators), position_recommendation=position_rec, key_levels=key_levels, trend_analysis=trend_analysis ) except Exception as e: raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}") @app.get("/portfolio/overview") async def portfolio_overview(): """Get overview of all elite stocks with quick signals""" try: results = {} for stock in EliteStock: ticker = yf.Ticker(stock.value) hist = ticker.history(period="1mo") if not hist.empty: closes = hist['Close'].values current_price = float(closes[-1]) rsi = talib.RSI(closes, timeperiod=14) macd, macd_signal, _ = talib.MACD(closes) # Quick signal latest_rsi = float(rsi[-1]) if not np.isnan(rsi[-1]) else 50.0 latest_macd = float(macd[-1]) if not np.isnan(macd[-1]) else 0.0 latest_macd_signal = float(macd_signal[-1]) if not np.isnan(macd_signal[-1]) else 0.0 if latest_rsi < 30 and latest_macd > latest_macd_signal: quick_signal = "BUY" elif latest_rsi > 70 and latest_macd < latest_macd_signal: quick_signal = "SELL" else: quick_signal = "HOLD" results[stock.value] = { "current_price": current_price, "rsi": latest_rsi, "quick_signal": quick_signal, "price_change_24h": float(closes[-1] - closes[-2]) if len(closes) > 1 else 0.0 } return { "portfolio_overview": results, "market_sentiment": "mixed", # Could be enhanced with market-wide analysis "last_updated": datetime.now() } except Exception as e: raise HTTPException(status_code=500, detail=f"Portfolio overview failed: {str(e)}") @app.get("/alerts/{symbol}") async def get_trading_alerts(symbol: EliteStock): """Get real-time trading alerts for a specific stock""" try: ticker = yf.Ticker(symbol.value) hist = ticker.history(period="5d") # Last 5 days for recent alerts if hist.empty: raise HTTPException(status_code=404, detail=f"No data found for {symbol.value}") closes = hist['Close'].values highs = hist['High'].values lows = hist['Low'].values # Calculate indicators for alerts rsi = talib.RSI(closes, timeperiod=14) bb_upper, bb_middle, bb_lower = talib.BBANDS(closes) alerts = [] current_price = float(closes[-1]) latest_rsi = float(rsi[-1]) if not np.isnan(rsi[-1]) else 50.0 # RSI alerts if latest_rsi <= 30: alerts.append({ "type": "RSI_OVERSOLD", "message": f"RSI at {latest_rsi:.1f} - Potential buying opportunity", "urgency": "HIGH" }) elif latest_rsi >= 70: alerts.append({ "type": "RSI_OVERBOUGHT", "message": f"RSI at {latest_rsi:.1f} - Consider taking profits", "urgency": "HIGH" }) # Bollinger Band alerts if current_price <= bb_lower[-1]: alerts.append({ "type": "BOLLINGER_LOWER", "message": f"Price touching lower Bollinger Band - Potential reversal", "urgency": "MEDIUM" }) elif current_price >= bb_upper[-1]: alerts.append({ "type": "BOLLINGER_UPPER", "message": f"Price touching upper Bollinger Band - Overbought condition", "urgency": "MEDIUM" }) # Volume alerts volumes = hist['Volume'].values avg_volume = np.mean(volumes[:-1]) # Average excluding today volume_ratio = volumes[-1] / avg_volume if volume_ratio > 2: alerts.append({ "type": "HIGH_VOLUME", "message": f"Volume spike: {volume_ratio:.1f}x average - Significant interest", "urgency": "HIGH" }) return { "symbol": symbol.value, "alerts": alerts, "alert_count": len(alerts), "last_updated": datetime.now() } except Exception as e: raise HTTPException(status_code=500, detail=f"Alerts failed: {str(e)}") if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=7860)