Spaces:
Sleeping
Sleeping
| 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] | |
| 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" | |
| ] | |
| } | |
| 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) | |
| ) | |
| 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)}") | |
| 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)}") | |
| 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) |