#!/usr/bin/env python3 """ Signal Generator Debugger Analyzes why signals aren't being generated """ import sqlite3 import pandas as pd import numpy as np import talib import json from datetime import datetime import sys def load_config(): with open("config.json", "r") as f: return json.load(f) def fetch_data(candles_db, analysis_db, timeframe, lookback=200): """Fetch and enrich data exactly like signals.py does""" try: conn_c = sqlite3.connect(f"file:{candles_db}?mode=ro", uri=True, timeout=10) conn_c.execute(f"ATTACH DATABASE 'file:{analysis_db}?mode=ro' AS analysis_db") query = """ SELECT c.timeframe, c.timestamp, c.open, c.high, c.low, c.close, c.volume, c.buy_volume, a.ema_9, a.ema_21, a.sma_50, a.sma_200, a.rsi_14, a.macd, a.macd_signal, a.macd_hist, a.bb_upper, a.bb_middle, a.bb_lower, a.bb_squeeze, a.volume_ma_20 FROM candles c JOIN analysis_db.analysis a ON c.timeframe = a.timeframe AND c.timestamp = a.timestamp WHERE c.timeframe = ? ORDER BY c.timestamp DESC LIMIT ? """ df = pd.read_sql_query(query, conn_c, params=(timeframe, lookback)) conn_c.close() if df.empty: return None df = df.sort_values("timestamp").reset_index(drop=True) df["datetime"] = pd.to_datetime(df["timestamp"], unit="s") # Filter closed candles import time current_time = int(time.time()) window = {"1m": 60, "5m": 300, "15m": 900, "1h": 3600}.get(timeframe, 60) df = df[df["timestamp"] < (current_time - window)] if len(df) < 50: return None df = df.dropna(subset=["open", "high", "low", "close", "volume"]) if len(df) < 50: return None # Add Stochastic df["stoch_k"], df["stoch_d"] = talib.STOCH( df["high"].values, df["low"].values, df["close"].values, fastk_period=14, slowk_period=3, slowd_period=3, ) df["buy_ratio"] = df["buy_volume"] / df["volume"].replace(0, np.nan) df["net_flow"] = df["buy_volume"] - (df["volume"] - df["buy_volume"]) return df except Exception as e: print(f"Error fetching data: {e}") return None def analyze_scalping(df, weights, min_confidence): """Analyze scalping signal generation""" if len(df) < 21: print(" āŒ Insufficient data for scalping (need 21+ rows)") return latest = df.iloc[-1] prev = df.iloc[-2] print(f"\nšŸ“Š SCALPING ANALYSIS ({len(df)} candles)") print("=" * 70) # Check for NULL values required = ["ema_9", "ema_21", "rsi_14", "stoch_k", "stoch_d", "macd", "macd_signal"] null_cols = [col for col in required if pd.isna(latest[col])] if null_cols: print(f" āŒ SKIPPED: Missing indicators: {', '.join(null_cols)}") return else: print(" āœ“ All required indicators present") print(f"\n Latest candle: {latest['datetime']}") print(f" Close: ${latest['close']:.2f}") # EMA Analysis print(f"\n EMA Crossover Check:") print(f" Current: EMA9={latest['ema_9']:.2f} vs EMA21={latest['ema_21']:.2f}") print(f" Previous: EMA9={prev['ema_9']:.2f} vs EMA21={prev['ema_21']:.2f}") ema_cross_up = latest["ema_9"] > latest["ema_21"] and prev["ema_9"] <= prev["ema_21"] ema_cross_down = latest["ema_9"] < latest["ema_21"] and prev["ema_9"] >= prev["ema_21"] if ema_cross_up: print(f" āœ“ BULLISH CROSSOVER DETECTED!") signal_type = "BUY" score = weights["ema_cross"] elif ema_cross_down: print(f" āœ“ BEARISH CROSSOVER DETECTED!") signal_type = "SELL" score = weights["ema_cross"] else: print(f" āŒ No crossover (EMA9 {'above' if latest['ema_9'] > latest['ema_21'] else 'below'} EMA21)") # Show trend direction ema_diff = latest["ema_9"] - latest["ema_21"] prev_diff = prev["ema_9"] - prev["ema_21"] trend = "converging" if abs(ema_diff) < abs(prev_diff) else "diverging" print(f" EMAs are {trend} (diff: {ema_diff:.2f} vs prev: {prev_diff:.2f})") return # We have a crossover, check other indicators print(f"\n Signal Type: {signal_type}") print(f" Base Score: {score:.3f} (from EMA crossover)") # Stochastic print(f"\n Stochastic:") print(f" K={latest['stoch_k']:.1f}, D={latest['stoch_d']:.1f}") if signal_type == "BUY": if latest["stoch_k"] > latest["stoch_d"] and latest["stoch_k"] < 30: score += weights["stoch"] print(f" āœ“ Oversold crossover (+{weights['stoch']:.3f})") else: print(f" āŒ Not oversold crossover (K>D: {latest['stoch_k'] > latest['stoch_d']}, K<30: {latest['stoch_k'] < 30})") else: if latest["stoch_k"] < latest["stoch_d"] and latest["stoch_k"] > 70: score += weights["stoch"] print(f" āœ“ Overbought crossover (+{weights['stoch']:.3f})") else: print(f" āŒ Not overbought crossover (K70: {latest['stoch_k'] > 70})") # RSI print(f"\n RSI: {latest['rsi_14']:.1f}") if signal_type == "BUY" and latest["rsi_14"] < 40: score += weights["rsi"] print(f" āœ“ Undersold (+{weights['rsi']:.3f})") elif signal_type == "SELL" and latest["rsi_14"] > 60: score += weights["rsi"] print(f" āœ“ Oversold (+{weights['rsi']:.3f})") else: print(f" āŒ Not in range (BUY needs <40, SELL needs >60)") # Volume vol_ratio = latest["volume"] / latest["volume_ma_20"] if latest["volume_ma_20"] else 0 print(f"\n Volume: {latest['volume']:.2f} vs MA20: {latest['volume_ma_20']:.2f}") print(f" Ratio: {vol_ratio:.2f}x") if vol_ratio > 1.5: score += weights["volume"] print(f" āœ“ Volume surge (+{weights['volume']:.3f})") else: print(f" āŒ No surge (need >1.5x)") # MACD print(f"\n MACD: {latest['macd']:.2f} vs Signal: {latest['macd_signal']:.2f}") if signal_type == "BUY" and latest["macd"] > latest["macd_signal"]: score += weights["macd"] print(f" āœ“ Bullish (+{weights['macd']:.3f})") elif signal_type == "SELL" and latest["macd"] < latest["macd_signal"]: score += weights["macd"] print(f" āœ“ Bearish (+{weights['macd']:.3f})") else: print(f" āŒ Not aligned") # Final score print(f"\n {'='*70}") print(f" FINAL SCORE: {score:.3f}") print(f" THRESHOLD: {min_confidence:.3f}") if score >= min_confidence: print(f" āœ… SIGNAL WOULD BE GENERATED!") else: print(f" āŒ Below threshold (need {min_confidence - score:.3f} more)") def analyze_swing(df, weights, min_confidence): """Analyze swing signal generation""" if len(df) < 200: print(f" āŒ Insufficient data for swing (need 200+ rows, have {len(df)})") return latest = df.iloc[-1] prev = df.iloc[-2] print(f"\nšŸ“Š SWING ANALYSIS ({len(df)} candles)") print("=" * 70) # Check for NULL values required = ["sma_50", "sma_200", "bb_upper", "bb_lower", "bb_squeeze", "macd", "macd_signal", "buy_ratio"] null_cols = [col for col in required if pd.isna(latest[col])] if null_cols: print(f" āŒ SKIPPED: Missing indicators: {', '.join(null_cols)}") return else: print(" āœ“ All required indicators present") print(f"\n Latest candle: {latest['datetime']}") print(f" Close: ${latest['close']:.2f}") # Regime Analysis print(f"\n Regime Analysis:") print(f" Price: ${latest['close']:.2f}") print(f" SMA50: ${latest['sma_50']:.2f}") print(f" SMA200: ${latest['sma_200']:.2f}") bull_regime = latest["close"] > latest["sma_50"] > latest["sma_200"] bear_regime = latest["close"] < latest["sma_50"] < latest["sma_200"] score = 0 signal_type = None if bull_regime: signal_type = "BUY" score += weights["regime"] print(f" āœ“ BULL REGIME (Price > SMA50 > SMA200) (+{weights['regime']:.3f})") elif bear_regime: signal_type = "SELL" score += weights["regime"] print(f" āœ“ BEAR REGIME (Price < SMA50 < SMA200) (+{weights['regime']:.3f})") else: print(f" āŒ No clear regime") print(f" Price vs SMA50: {'above' if latest['close'] > latest['sma_50'] else 'below'}") print(f" SMA50 vs SMA200: {'above' if latest['sma_50'] > latest['sma_200'] else 'below'}") return print(f"\n Signal Type: {signal_type}") print(f" Base Score: {score:.3f} (from regime)") # BB Squeeze print(f"\n Bollinger Bands:") print(f" Squeeze: {latest['bb_squeeze']} (prev: {prev['bb_squeeze']})") print(f" Upper: ${latest['bb_upper']:.2f}, Lower: ${latest['bb_lower']:.2f}") if latest["bb_squeeze"] == 1 or prev["bb_squeeze"] == 1: if signal_type == "BUY" and latest["close"] > latest["bb_upper"]: score += weights["bb_squeeze"] print(f" āœ“ Squeeze breakout upside (+{weights['bb_squeeze']:.3f})") elif signal_type == "SELL" and latest["close"] < latest["bb_lower"]: score += weights["bb_squeeze"] print(f" āœ“ Squeeze breakout downside (+{weights['bb_squeeze']:.3f})") else: print(f" āŒ Squeeze present but no breakout") else: print(f" āŒ No squeeze") # MACD print(f"\n MACD:") print(f" Current: {latest['macd']:.2f} vs Signal: {latest['macd_signal']:.2f}") print(f" Previous: {prev['macd']:.2f} vs Signal: {prev['macd_signal']:.2f}") macd_cross_up = latest["macd"] > latest["macd_signal"] and prev["macd"] <= prev["macd_signal"] macd_cross_down = latest["macd"] < latest["macd_signal"] and prev["macd"] >= prev["macd_signal"] if signal_type == "BUY" and macd_cross_up: score += weights["macd"] print(f" āœ“ Bullish crossover (+{weights['macd']:.3f})") elif signal_type == "SELL" and macd_cross_down: score += weights["macd"] print(f" āœ“ Bearish crossover (+{weights['macd']:.3f})") else: print(f" āŒ No crossover or not aligned") # Net flow print(f"\n Buy/Sell Pressure:") print(f" Buy Ratio: {latest['buy_ratio']:.2%}") if signal_type == "BUY" and latest["buy_ratio"] > 0.55: score += weights["flow"] print(f" āœ“ Strong buy pressure (+{weights['flow']:.3f})") elif signal_type == "SELL" and latest["buy_ratio"] < 0.45: score += weights["flow"] print(f" āœ“ Strong sell pressure (+{weights['flow']:.3f})") else: print(f" āŒ Neutral pressure") # RSI print(f"\n RSI: {latest['rsi_14']:.1f}") if signal_type == "BUY" and latest["rsi_14"] < 50: score += weights["rsi"] print(f" āœ“ Not overbought (+{weights['rsi']:.3f})") elif signal_type == "SELL" and latest["rsi_14"] > 50: score += weights["rsi"] print(f" āœ“ Not oversold (+{weights['rsi']:.3f})") else: print(f" āŒ Unfavorable") # Final score print(f"\n {'='*70}") print(f" FINAL SCORE: {score:.3f}") print(f" THRESHOLD: {min_confidence:.3f}") if score >= min_confidence: print(f" āœ… SIGNAL WOULD BE GENERATED!") else: print(f" āŒ Below threshold (need {min_confidence - score:.3f} more)") def main(): config = load_config() print("šŸ” SIGNAL GENERATOR DEBUGGER") print("=" * 70) print(f"Min Confidence: {config['min_confidence']}") print(f"Timeframes: {', '.join(config['timeframes'])}") print(f"Lookback: {config['lookback']} candles") for timeframe in config["timeframes"]: print(f"\n\n{'='*70}") print(f"TIMEFRAME: {timeframe}") print(f"{'='*70}") df = fetch_data(config["candles_db"], config["analysis_db"], timeframe, config["lookback"]) if df is None: print(f" āŒ No data available") continue print(f" āœ“ Loaded {len(df)} candles") # Analyze both personalities analyze_scalping(df, config["weights"]["scalping"], config["min_confidence"]) analyze_swing(df, config["weights"]["swing"], config["min_confidence"]) if __name__ == "__main__": main()