Files
trading_bot_v4/scripts/debug_ma_gap.py
mindesbunister cc56b72df2 fix: Database-first cluster status detection + Stop button clarification
CRITICAL FIX (Nov 30, 2025):
- Dashboard showed 'idle' despite 22+ worker processes running
- Root cause: SSH-based worker detection timing out
- Solution: Check database for running chunks FIRST

Changes:
1. app/api/cluster/status/route.ts:
   - Query exploration database before SSH detection
   - If running chunks exist, mark workers 'active' even if SSH fails
   - Override worker status: 'offline' → 'active' when chunks running
   - Log: ' Cluster status: ACTIVE (database shows running chunks)'
   - Database is source of truth, SSH only for supplementary metrics

2. app/cluster/page.tsx:
   - Stop button ALREADY EXISTS (conditionally shown)
   - Shows Start when status='idle', Stop when status='active'
   - No code changes needed - fixed by status detection

Result:
- Dashboard now shows 'ACTIVE' with 2 workers (correct)
- Workers show 'active' status (was 'offline')
- Stop button automatically visible when cluster active
- System resilient to SSH timeouts/network issues

Verified:
- Container restarted: Nov 30 21:18 UTC
- API tested: Returns status='active', activeWorkers=2
- Logs confirm: Database-first logic working
- Workers confirmed running: 22+ processes on worker1, workers on worker2
2025-11-30 22:23:01 +01:00

69 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""Debug MA gap scoring to understand parameter insensitivity."""
import sys
from pathlib import Path
import numpy as np
import pandas as pd
# Add backtester to path
sys.path.insert(0, str(Path(__file__).parent.parent))
from backtester.data_loader import load_csv
from backtester.indicators.money_line import MoneyLineInputs, ema
def analyze_ma_gap_distribution(csv_path: str):
"""Analyze MA gap score distribution to understand parameter behavior."""
print("=" * 80)
print("MA GAP DISTRIBUTION ANALYSIS")
print("=" * 80)
# Load data
data_slice = load_csv(Path(csv_path), "SOL-PERP", "5m")
df = data_slice.data # DataSlice.data is the DataFrame
print(f"Loaded {len(df)} bars\n")
# Calculate EMAs
df["ema_fast"] = ema(df["close"], 50)
df["ema_slow"] = ema(df["close"], 200)
# Calculate raw MA gap percentage
ma_gap = 100.0 * (df["ema_fast"] - df["ema_slow"]) / df["close"]
print("RAW MA GAP (%) DISTRIBUTION:")
print(f" Min: {ma_gap.min():.4f}%")
print(f" 25th: {ma_gap.quantile(0.25):.4f}%")
print(f" Median: {ma_gap.median():.4f}%")
print(f" 75th: {ma_gap.quantile(0.75):.4f}%")
print(f" Max: {ma_gap.max():.4f}%")
print(f" Std: {ma_gap.std():.4f}%\n")
# Test different thresholds
for threshold in [0.2, 0.3, 0.35, 0.4, 0.5]:
gap_score = np.tanh(ma_gap / threshold)
print(f"\nTHRESHOLD = {threshold} (default: 0.35):")
print(f" gap_score min: {gap_score.min():.6f}")
print(f" gap_score max: {gap_score.max():.6f}")
print(f" gap_score median: {gap_score.median():.6f}")
# Check flip_threshold comparisons
for flip_pct in [0.4, 0.5, 0.6, 0.7]:
flip_threshold = flip_pct / 100.0 # Convert to decimal
# Count how many bars exceed threshold
long_signals = (gap_score > flip_threshold).sum()
short_signals = (gap_score < -flip_threshold).sum()
total_signals = long_signals + short_signals
print(f" flip_threshold={flip_pct}% → {total_signals} potential signals")
print(f" (LONG: {long_signals}, SHORT: {short_signals})")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python debug_ma_gap.py <csv_path>")
sys.exit(1)
analyze_ma_gap_distribution(sys.argv[1])