""" Phase 4: Advanced Indicators Management Technical analysis indicators configuration and management """ from fastapi import APIRouter, Query, HTTPException from typing import List, Optional from datetime import datetime router = APIRouter(prefix="/api/indicators", tags=["Technical Indicators"]) # Available indicators with their parameters AVAILABLE_INDICATORS = { "moving_averages": { "name": "Moving Averages", "description": "SMA, EMA, DEMA, TEMA, WMA", "indicators": [ { "id": "sma", "name": "Simple Moving Average", "periods": [5, 10, 20, 50, 100, 200], "default_period": 20, "type": "trend", }, { "id": "ema", "name": "Exponential Moving Average", "periods": [5, 10, 20, 50, 100, 200], "default_period": 12, "type": "trend", }, { "id": "wma", "name": "Weighted Moving Average", "periods": [5, 10, 20, 50], "default_period": 20, "type": "trend", }, ], }, "oscillators": { "name": "Oscillators", "description": "RSI, Stochastic, MACD, KDJ", "indicators": [ { "id": "rsi", "name": "Relative Strength Index", "periods": [14], "default_period": 14, "bounds": [0, 100], "overbought": 70, "oversold": 30, "type": "momentum", }, { "id": "stochastic", "name": "Stochastic Oscillator", "periods": [14], "smoothing": [3, 5, 7], "default_period": 14, "bounds": [0, 100], "overbought": 80, "oversold": 20, "type": "momentum", }, { "id": "macd", "name": "MACD", "fast_period": 12, "slow_period": 26, "signal_period": 9, "type": "momentum", }, { "id": "kdj", "name": "KDJ Index", "periods": [9, 14], "default_period": 9, "bounds": [0, 100], "type": "momentum", }, ], }, "volatility": { "name": "Volatility Indicators", "description": "Bollinger Bands, ATR, Keltner Channel", "indicators": [ { "id": "bb", "name": "Bollinger Bands", "periods": [20], "default_period": 20, "std_dev": 2, "type": "volatility", }, { "id": "atr", "name": "Average True Range", "periods": [14], "default_period": 14, "type": "volatility", }, { "id": "kc", "name": "Keltner Channel", "periods": [20], "default_period": 20, "atr_mult": 2, "type": "volatility", }, ], }, "support_resistance": { "name": "Support & Resistance", "description": "Pivot Points, Fibonacci, Trend Lines", "indicators": [ { "id": "pivot", "name": "Pivot Points", "types": ["Classic", "Camarilla", "Woodie"], "default_type": "Classic", "type": "level", }, { "id": "fibonacci", "name": "Fibonacci Retracement", "levels": [0.0, 0.236, 0.382, 0.5, 0.618, 0.786, 1.0], "type": "level", }, ], }, "volume": { "name": "Volume Indicators", "description": "OBV, Volume Profile, CMF", "indicators": [ { "id": "obv", "name": "On-Balance Volume", "periods": [20], "default_period": 20, "type": "volume", }, { "id": "cmf", "name": "Chaikin Money Flow", "periods": [20], "default_period": 20, "type": "volume", }, ], }, } # Default indicator configuration for gold trading DEFAULT_INDICATORS = { "trend": ["ema_12", "ema_26"], "momentum": ["rsi_14", "macd"], "volatility": ["bb_20", "atr_14"], "support_resistance": ["pivot_classic"], } # Mock user configurations USER_INDICATORS = {} @router.get("/available") async def get_available_indicators(): """Get all available technical indicators""" return { "total_categories": len(AVAILABLE_INDICATORS), "categories": AVAILABLE_INDICATORS, "total_indicators": sum( len(cat.get("indicators", [])) for cat in AVAILABLE_INDICATORS.values() ), } @router.get("/categories") async def get_indicator_categories(): """Get indicator categories""" return { "categories": [ {"key": key, "name": value["name"], "description": value["description"]} for key, value in AVAILABLE_INDICATORS.items() ] } @router.get("/category/{category}") async def get_category_indicators(category: str): """Get indicators in a specific category""" if category not in AVAILABLE_INDICATORS: raise HTTPException(status_code=404, detail=f"Category '{category}' not found") return AVAILABLE_INDICATORS[category] @router.get("/{indicator_id}") async def get_indicator_details(indicator_id: str): """Get detailed information about a specific indicator""" for category in AVAILABLE_INDICATORS.values(): for indicator in category.get("indicators", []): if indicator["id"] == indicator_id: return indicator raise HTTPException(status_code=404, detail=f"Indicator '{indicator_id}' not found") @router.get("/default") async def get_default_configuration(): """Get recommended indicator configuration for gold trading""" return { "name": "Gold Trading Starter Pack", "description": "Recommended indicators for gold day trading", "configuration": DEFAULT_INDICATORS, "explanation": { "trend": "EMAs help identify trend direction", "momentum": "RSI and MACD identify overbought/oversold conditions", "volatility": "Bollinger Bands and ATR help with entry/exit zones", "support_resistance": "Pivot points identify key support/resistance levels", }, "best_practices": [ "Use 12/26 EMA crossover for trend confirmation", "RSI above 70 = potential sell, below 30 = potential buy", "MACD crossovers signal momentum changes", "Bollinger Band squeeze precedes volatility expansion", "Trade ATR breakouts for high probability moves", ], } @router.get("/presets") async def get_indicator_presets(): """Get pre-configured indicator setups""" return { "presets": [ { "id": "scalping", "name": "Scalping Setup (1-5 min)", "indicators": [ "ema_5", "ema_10", "rsi_14", "macd", "bb_20", ], "description": "Fast indicators for quick trade entries/exits", }, { "id": "swing", "name": "Swing Trading Setup (4h-1D)", "indicators": [ "sma_50", "ema_200", "rsi_14", "macd", "pivot_classic", ], "description": "Medium-term trend and momentum indicators", }, { "id": "position", "name": "Position Trading Setup (1D+)", "indicators": [ "sma_50", "sma_200", "rsi_14", "bb_20", "fibonacci", ], "description": "Long-term trend and support/resistance levels", }, { "id": "volatility", "name": "Volatility Focus Setup", "indicators": [ "bb_20", "atr_14", "kc_20", "obv_20", ], "description": "For high volatility market conditions", }, { "id": "momentum", "name": "Momentum Focus Setup", "indicators": [ "rsi_14", "stochastic_14", "macd", "kdj_9", ], "description": "For momentum-driven market moves", }, ] } @router.post("/preset/{preset_id}/apply") async def apply_preset(preset_id: str, user_id: Optional[str] = Query(None)): """Apply a pre-configured indicator preset""" presets = await get_indicator_presets() preset = next((p for p in presets["presets"] if p["id"] == preset_id), None) if not preset: raise HTTPException(status_code=404, detail=f"Preset '{preset_id}' not found") # Store user configuration if user_id: USER_INDICATORS[user_id] = preset.copy() return { "status": "preset_applied", "preset": preset, "applied_at": datetime.now().isoformat(), } @router.post("/custom") async def create_custom_configuration( indicators_list: List[str], config_name: str, user_id: Optional[str] = Query(None) ): """Create a custom indicator configuration""" # Validate all requested indicators exist valid_indicators = [] for cat in AVAILABLE_INDICATORS.values(): for ind in cat.get("indicators", []): valid_indicators.append(ind["id"]) invalid = [i for i in indicators_list if i not in valid_indicators] if invalid: raise HTTPException( status_code=400, detail=f"Invalid indicators: {invalid}", ) config = { "name": config_name, "indicators": indicators_list, "created_at": datetime.now().isoformat(), "indicator_count": len(indicators_list), } if user_id: USER_INDICATORS[user_id] = config return { "status": "configuration_created", "configuration": config, } @router.get("/recommendations") async def get_indicator_recommendations( market_condition: str = Query("normal", regex="^(trending|ranging|volatile|calm)$"), trading_style: str = Query("swing", regex="^(scalping|swing|position)$"), ): """Get recommended indicators based on market conditions""" recommendations = { "trending": { "best": ["ema_12_26_crossover", "atr_14", "obv_20"], "supporting": ["pivot_points", "fibonacci"], "avoid": ["stochastic", "rsi_only"], "reasoning": "Use trend-following indicators in trending markets", }, "ranging": { "best": ["rsi_14", "stochastic_14", "bb_20"], "supporting": ["pivot_points"], "avoid": ["moving_average_crossovers"], "reasoning": "Use oscillators for overbought/oversold in ranging markets", }, "volatile": { "best": ["atr_14", "bb_20", "kc_20"], "supporting": ["ema_12_26"], "avoid": ["simple_moving_averages"], "reasoning": "Track volatility expansion with volatility indicators", }, "calm": { "best": ["pivot_points", "fibonacci", "volume_profile"], "supporting": ["rsi_14", "macd"], "avoid": ["atr"], "reasoning": "Focus on support/resistance levels when volatility is low", }, } timeframe_recommendations = { "scalping": { "periods": ["1m", "5m"], "indicators": ["ema_5_10", "rsi_14", "macd"], "setup": "Fast indicators for quick entries", }, "swing": { "periods": ["4h", "1D"], "indicators": ["ema_12_26", "rsi_14", "bb_20", "pivot_points"], "setup": "Balanced trend and momentum", }, "position": { "periods": ["1D", "1W"], "indicators": ["sma_50_200", "rsi_14", "fibonacci"], "setup": "Long-term trend following", }, } return { "market_condition": market_condition, "trading_style": trading_style, "recommended_indicators": recommendations.get( market_condition, recommendations["normal"] ), "timeframe_setup": timeframe_recommendations.get(trading_style), } @router.post("/calculate/{indicator}") async def calculate_indicator( indicator: str, price_data: List[float], period: int = Query(14, ge=2, le=200), ): """ Calculate indicator values (for testing/visualization) This would typically be called for real calculations """ if indicator == "rsi": # Simplified RSI calculation if len(price_data) < period: raise HTTPException( status_code=400, detail=f"Need at least {period} data points", ) changes = [price_data[i] - price_data[i - 1] for i in range(1, len(price_data))] gains = [max(0, c) for c in changes] losses = [abs(min(0, c)) for c in changes] avg_gain = sum(gains[-period:]) / period avg_loss = sum(losses[-period:]) / period rsi = 100 - (100 / (1 + (avg_gain / avg_loss if avg_loss != 0 else 1))) return {"indicator": indicator, "period": period, "value": rsi} raise HTTPException(status_code=400, detail=f"Indicator '{indicator}' calculation not implemented") @router.get("/alerts/golden-cross") async def get_golden_cross_alerts(): """Get alerts for golden cross (50-day SMA crosses above 200-day SMA)""" return { "alert_type": "golden_cross", "description": "50-day SMA crosses above 200-day SMA (bullish signal)", "current_status": "monitoring", "last_occurrence": "2024-11-10", "signal_strength": "strong", "recommended_action": "Consider long positions", } @router.get("/alerts/death-cross") async def get_death_cross_alerts(): """Get alerts for death cross (50-day SMA crosses below 200-day SMA)""" return { "alert_type": "death_cross", "description": "50-day SMA crosses below 200-day SMA (bearish signal)", "current_status": "monitoring", "last_occurrence": None, "signal_strength": None, "recommended_action": "Monitor for potential bearish reversal", } @router.get("/alerts/divergence") async def get_divergence_alerts(): """Get alerts for price/indicator divergences""" return { "divergence_alerts": [ { "type": "bullish_divergence", "indicator": "rsi", "description": "Price makes lower low but RSI makes higher low", "signal": "potential_uptrend_reversal", "strength": "medium", }, { "type": "bearish_divergence", "indicator": "macd", "description": "Price makes higher high but MACD makes lower high", "signal": "potential_downtrend_reversal", "strength": "high", }, ] } @router.get("/cheat-sheet") async def get_indicator_cheat_sheet(): """Get quick reference guide for all indicators""" return { "moving_averages": { "ema_crossover": "Golden Cross (50 > 200) = bullish, Death Cross = bearish", "price_cross_ma": "Price above MA = uptrend, Below = downtrend", "ma_bounce": "Price bounces off MA = trend continuation", }, "oscillators": { "rsi_above_70": "Overbought - look for reversals", "rsi_below_30": "Oversold - look for bounces", "rsi_divergence": "Price higher but RSI lower = bearish signal", "macd_cross": "MACD above signal line = bullish", }, "volatility": { "bb_squeeze": "Low volatility - breakout coming soon", "bb_expansion": "High volatility - expect big moves", "atr_low": "Low volatility period", "atr_high": "High volatility period", }, "support_resistance": { "pivot_s1": "First support level", "pivot_r1": "First resistance level", "fibonacci_618": "Most important retracement level", }, }