Lowered signal tresholds so low that we got signals flowing. Few debug scripts to see way signals were not firing. Fix analyst.py indicator calculation to use TA-lib.
This commit is contained in:
@@ -121,38 +121,44 @@ analysis_conn.commit()
|
||||
|
||||
# ========== Technical Indicator Functions ==========
|
||||
def compute_indicators(df):
|
||||
close = df['close']
|
||||
"""Compute indicators using TA-Lib for accuracy"""
|
||||
import talib
|
||||
|
||||
close = df['close'].values
|
||||
high = df['high'].values
|
||||
low = df['low'].values
|
||||
volume = df['volume'].values
|
||||
|
||||
# EMA and SMA
|
||||
df['ema_9'] = close.ewm(span=9, adjust=False).mean()
|
||||
df['ema_21'] = close.ewm(span=21, adjust=False).mean()
|
||||
df['sma_50'] = close.rolling(window=50, min_periods=1).mean()
|
||||
df['sma_200'] = close.rolling(window=200, min_periods=1).mean()
|
||||
# RSI (14): using 14-period gains/losses and RSI formula (100 - 100/(1+RS)):contentReference[oaicite:3]{index=3}
|
||||
delta = close.diff()
|
||||
gain = delta.clip(lower=0)
|
||||
loss = -delta.clip(upper=0)
|
||||
avg_gain = gain.rolling(window=14, min_periods=14).mean()
|
||||
avg_loss = loss.rolling(window=14, min_periods=14).mean()
|
||||
rs = avg_gain / avg_loss.replace(0, pd.NA)
|
||||
df['rsi_14'] = 100 - (100 / (1 + rs))
|
||||
df['ema_9'] = talib.EMA(close, timeperiod=9)
|
||||
df['ema_21'] = talib.EMA(close, timeperiod=21)
|
||||
df['sma_50'] = talib.SMA(close, timeperiod=50)
|
||||
df['sma_200'] = talib.SMA(close, timeperiod=200)
|
||||
|
||||
# RSI (14) - Proper calculation
|
||||
df['rsi_14'] = talib.RSI(close, timeperiod=14)
|
||||
|
||||
# MACD (12,26,9)
|
||||
ema12 = close.ewm(span=12, adjust=False).mean()
|
||||
ema26 = close.ewm(span=26, adjust=False).mean()
|
||||
macd_line = ema12 - ema26
|
||||
df['macd'] = macd_line
|
||||
df['macd_signal'] = macd_line.ewm(span=9, adjust=False).mean()
|
||||
df['macd_hist'] = df['macd'] - df['macd_signal']
|
||||
macd, macd_signal, macd_hist = talib.MACD(close, fastperiod=12, slowperiod=26, signalperiod=9)
|
||||
df['macd'] = macd
|
||||
df['macd_signal'] = macd_signal
|
||||
df['macd_hist'] = macd_hist
|
||||
|
||||
# Bollinger Bands (20,2)
|
||||
df['bb_middle'] = close.rolling(window=20, min_periods=20).mean()
|
||||
bb_std = close.rolling(window=20, min_periods=20).std()
|
||||
df['bb_upper'] = df['bb_middle'] + 2 * bb_std
|
||||
df['bb_lower'] = df['bb_middle'] - 2 * bb_std
|
||||
# Bollinger Squeeze: detect when BB width is lowest over 20 periods:contentReference[oaicite:4]{index=4}
|
||||
bb_width = df['bb_upper'] - df['bb_lower']
|
||||
rolling_min_width = bb_width.rolling(window=20, min_periods=20).min()
|
||||
df['bb_squeeze'] = (bb_width <= rolling_min_width).astype(int)
|
||||
# Volume moving average (20)
|
||||
df['volume_ma_20'] = df['volume'].rolling(window=20, min_periods=1).mean()
|
||||
bb_upper, bb_middle, bb_lower = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
|
||||
df['bb_upper'] = bb_upper
|
||||
df['bb_middle'] = bb_middle
|
||||
df['bb_lower'] = bb_lower
|
||||
|
||||
# Bollinger Squeeze
|
||||
bb_width = bb_upper - bb_lower
|
||||
bb_width_series = pd.Series(bb_width)
|
||||
rolling_min_width = bb_width_series.rolling(window=20, min_periods=20).min()
|
||||
df['bb_squeeze'] = (bb_width_series <= rolling_min_width).fillna(0).astype(int)
|
||||
|
||||
# Volume MA
|
||||
df['volume_ma_20'] = talib.SMA(volume, timeperiod=20)
|
||||
|
||||
return df
|
||||
|
||||
# ========== Health Check Server ==========
|
||||
|
||||
196
analysis/backfill_indicators.py
Executable file
196
analysis/backfill_indicators.py
Executable file
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Backfill Missing Indicators
|
||||
Calculates RSI and Bollinger Bands for existing data
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import talib
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def backfill_indicators(candles_db: str, analysis_db: str):
|
||||
"""Backfill RSI and Bollinger Bands for all timeframes"""
|
||||
|
||||
print("🔧 BACKFILLING MISSING INDICATORS")
|
||||
print("=" * 70)
|
||||
|
||||
# Connect to databases
|
||||
conn_candles = sqlite3.connect(candles_db)
|
||||
conn_analysis = sqlite3.connect(analysis_db)
|
||||
|
||||
# Get all timeframes
|
||||
cursor = conn_analysis.cursor()
|
||||
cursor.execute("SELECT DISTINCT timeframe FROM analysis ORDER BY timeframe")
|
||||
timeframes = [row[0] for row in cursor.fetchall()]
|
||||
|
||||
total_updated = 0
|
||||
|
||||
for timeframe in timeframes:
|
||||
print(f"\n📊 Processing {timeframe}...")
|
||||
|
||||
# Fetch candle data
|
||||
df_candles = pd.read_sql_query(
|
||||
"SELECT timestamp, close, high, low FROM candles WHERE timeframe = ? ORDER BY timestamp",
|
||||
conn_candles,
|
||||
params=(timeframe,)
|
||||
)
|
||||
|
||||
if len(df_candles) < 20:
|
||||
print(f" ⚠️ Skipping - insufficient data ({len(df_candles)} rows)")
|
||||
continue
|
||||
|
||||
print(f" ✓ Loaded {len(df_candles)} candles")
|
||||
|
||||
# Calculate RSI
|
||||
df_candles['rsi_14'] = talib.RSI(df_candles['close'].values, timeperiod=14)
|
||||
|
||||
# Calculate Bollinger Bands
|
||||
bb_upper, bb_middle, bb_lower = talib.BBANDS(
|
||||
df_candles['close'].values,
|
||||
timeperiod=20,
|
||||
nbdevup=2,
|
||||
nbdevdn=2,
|
||||
matype=0
|
||||
)
|
||||
|
||||
df_candles['bb_upper'] = bb_upper
|
||||
df_candles['bb_middle'] = bb_middle
|
||||
df_candles['bb_lower'] = bb_lower
|
||||
|
||||
# Calculate BB Squeeze
|
||||
# Squeeze = when BB width is in the lowest 20% of recent widths
|
||||
df_candles['bb_width'] = df_candles['bb_upper'] - df_candles['bb_lower']
|
||||
df_candles['bb_width_rank'] = df_candles['bb_width'].rolling(window=100).apply(
|
||||
lambda x: (x.iloc[-1] <= x.quantile(0.2)).astype(int) if len(x) >= 20 else 0,
|
||||
raw=False
|
||||
)
|
||||
df_candles['bb_squeeze'] = df_candles['bb_width_rank'].fillna(0).astype(int)
|
||||
|
||||
# Update analysis database
|
||||
cursor_update = conn_analysis.cursor()
|
||||
updated = 0
|
||||
|
||||
for _, row in df_candles.iterrows():
|
||||
cursor_update.execute("""
|
||||
UPDATE analysis
|
||||
SET rsi_14 = ?, bb_upper = ?, bb_middle = ?, bb_lower = ?, bb_squeeze = ?
|
||||
WHERE timeframe = ? AND timestamp = ?
|
||||
""", (
|
||||
float(row['rsi_14']) if not pd.isna(row['rsi_14']) else None,
|
||||
float(row['bb_upper']) if not pd.isna(row['bb_upper']) else None,
|
||||
float(row['bb_middle']) if not pd.isna(row['bb_middle']) else None,
|
||||
float(row['bb_lower']) if not pd.isna(row['bb_lower']) else None,
|
||||
int(row['bb_squeeze']),
|
||||
timeframe,
|
||||
int(row['timestamp'])
|
||||
))
|
||||
updated += cursor_update.rowcount
|
||||
|
||||
conn_analysis.commit()
|
||||
total_updated += updated
|
||||
|
||||
print(f" ✅ Updated {updated} rows")
|
||||
|
||||
# Show sample
|
||||
latest = df_candles.iloc[-1]
|
||||
print(f" Latest RSI: {latest['rsi_14']:.2f}" if not pd.isna(latest['rsi_14']) else " Latest RSI: NULL")
|
||||
print(f" Latest BB: Upper=${latest['bb_upper']:.2f}, Lower=${latest['bb_lower']:.2f}" if not pd.isna(latest['bb_upper']) else " Latest BB: NULL")
|
||||
|
||||
conn_candles.close()
|
||||
conn_analysis.close()
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
print(f"✅ BACKFILL COMPLETE!")
|
||||
print(f" Total rows updated: {total_updated}")
|
||||
print(f"{'='*70}")
|
||||
|
||||
|
||||
def verify_backfill(analysis_db: str):
|
||||
"""Verify the backfill worked"""
|
||||
print("\n🔍 VERIFICATION")
|
||||
print("=" * 70)
|
||||
|
||||
conn = sqlite3.connect(analysis_db)
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("SELECT DISTINCT timeframe FROM analysis")
|
||||
timeframes = [row[0] for row in cursor.fetchall()]
|
||||
|
||||
for tf in timeframes:
|
||||
# Count NULL values
|
||||
cursor.execute("""
|
||||
SELECT
|
||||
COUNT(*) as total,
|
||||
SUM(CASE WHEN rsi_14 IS NULL THEN 1 ELSE 0 END) as rsi_null,
|
||||
SUM(CASE WHEN bb_upper IS NULL THEN 1 ELSE 0 END) as bb_null
|
||||
FROM analysis
|
||||
WHERE timeframe = ?
|
||||
""", (tf,))
|
||||
|
||||
total, rsi_null, bb_null = cursor.fetchone()
|
||||
|
||||
print(f"\n{tf}:")
|
||||
print(f" Total rows: {total}")
|
||||
print(f" RSI NULL: {rsi_null} ({rsi_null/total*100:.1f}%)" if total > 0 else " RSI NULL: N/A")
|
||||
print(f" BB NULL: {bb_null} ({bb_null/total*100:.1f}%)" if total > 0 else " BB NULL: N/A")
|
||||
|
||||
# Get latest values
|
||||
cursor.execute("""
|
||||
SELECT rsi_14, bb_upper, bb_lower, bb_squeeze
|
||||
FROM analysis
|
||||
WHERE timeframe = ?
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 1
|
||||
""", (tf,))
|
||||
|
||||
row = cursor.fetchone()
|
||||
if row and row[0] is not None:
|
||||
print(f" ✅ Latest: RSI={row[0]:.2f}, BB_upper=${row[1]:.2f}, BB_squeeze={row[3]}")
|
||||
else:
|
||||
print(f" ❌ Latest values still NULL")
|
||||
|
||||
conn.close()
|
||||
|
||||
|
||||
def main():
|
||||
import json
|
||||
|
||||
# Load config
|
||||
try:
|
||||
with open("config.json", "r") as f:
|
||||
config = json.load(f)
|
||||
candles_db = config.get("candles_db", "../onramp/market_data.db")
|
||||
analysis_db = config.get("analysis_db", "../analysis/analysis.db")
|
||||
except FileNotFoundError:
|
||||
print("❌ config.json not found, using default paths")
|
||||
candles_db = "../onramp/market_data.db"
|
||||
analysis_db = "../analysis/analysis.db"
|
||||
|
||||
print(f"Candles DB: {candles_db}")
|
||||
print(f"Analysis DB: {analysis_db}")
|
||||
|
||||
try:
|
||||
backfill_indicators(candles_db, analysis_db)
|
||||
verify_backfill(analysis_db)
|
||||
|
||||
print("\n💡 NEXT STEPS:")
|
||||
print("=" * 70)
|
||||
print("1. Run the signal debugger again:")
|
||||
print(" python3 signal_debugger.py")
|
||||
print("\n2. Restart the signal generator:")
|
||||
print(" pkill -f signals.py")
|
||||
print(" ./signals.py")
|
||||
print("\n3. Update your analysis pipeline to calculate these indicators")
|
||||
print(" going forward so you don't need to backfill again")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user