Friday, 14 March 2025

TamTam - Hardcore Nerd DS Gear??

import requests
from binance.client import Client
from datetime import datetime
import time
import threading
from dotenv import load_dotenv
import os
import sys
import itertools
import signal

# ANSI Colors
RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
CYAN = "\033[96m"
RESET = "\033[0m"

# Load API keys from .env
load_dotenv()
BINANCE_API_KEY = os.getenv("BINANCE_API_KEY")
BINANCE_API_SECRET = os.getenv("BINANCE_API_SECRET")

# Initialize Binance Client
client = Client(BINANCE_API_KEY, BINANCE_API_SECRET)

# Configuration
CHECK_INTERVAL = 3600  # 1 hour between NVT checks
COINS_TO_MONITOR = ['BTC', 'ETH', 'XRP', 'ADA', 'DOGE']
NVT_UNDERVALUE_THRESHOLD = 35
PRICE_JUMP_THRESHOLDS = {'1m': 1.5, '5m': 2.0, '15m': 3.0}

# CoinGecko ID Mapping (Global Scope)
COINGECKO_IDS = {
    'BTC': 'bitcoin',
    'ETH': 'ethereum',
    'XRP': 'ripple',
    'ADA': 'cardano',
    'DOGE': 'dogecoin'
}

# Activity monitoring
stop_event = threading.Event()
spinner_chars = itertools.cycle(['|', '/', '-', '\\'])

def spinner():
    """Show rotating spinner while processing."""
    while not stop_event.is_set():
        sys.stdout.write(f"{CYAN}\rActive {next(spinner_chars)} {RESET}")
        sys.stdout.flush()
        time.sleep(0.2)
    sys.stdout.write('\r            \r')  # Clear spinner

def get_nvt_ratio(symbol):
    """Calculate NVT Ratio using CoinGecko/Blockchain.com data."""
    try:
        time.sleep(1)  # Rate limit delay
        coin_id = COINGECKO_IDS[symbol]
        cg_data = requests.get(
            f"https://api.coingecko.com/api/v3/coins/{coin_id}"
        ).json()
        
        if 'market_data' not in cg_data:
            print(f"{RED}CoinGecko data missing for {symbol}{RESET}")
            return None
        
        market_cap = cg_data['market_data']['market_cap']['usd']

        # Get on-chain volume
        onchain_volume = requests.get(
            "https://api.blockchain.info/charts/estimated-transaction-volume-usd",
            params={'timespan': '1days', 'format': 'json'}
        ).json()['values'][0]['y']

        return market_cap / onchain_volume
    except Exception as e:
        print(f"{RED}NVT Error for {symbol}: {str(e)}{RESET}")
        return None

def price_jump_monitor(symbol):
    """Monitor price jumps across intervals."""
    print(f"{YELLOW}Starting price monitor for {symbol}{RESET}")
    while not stop_event.is_set():
        try:
            for interval, threshold in PRICE_JUMP_THRESHOLDS.items():
                candles = client.get_klines(
                    symbol=f"{symbol}USDT",
                    interval=interval,
                    limit=5
                )
                if len(candles) < 2:
                    continue

                old_price = float(candles[0][4])
                new_price = float(candles[-1][4])
                change_pct = ((new_price - old_price)/old_price) * 100

                if abs(change_pct) >= threshold:
                    direction = "↑" if change_pct > 0 else "↓"
                    print(
                        f"{RED}{datetime.now().strftime('%H:%M:%S')} - "
                        f"{symbol} {interval}: {direction}{abs(change_pct):.2f}% "
                        f"({old_price:.2f} → {new_price:.2f}){RESET}"
                    )
            time.sleep(30)
        except Exception as e:
            print(f"{RED}Price error ({symbol}): {str(e)}{RESET}")

def nvt_screener():
    """Main screening/monitoring function."""
    monitored_coins = set()
    print(f"{CYAN}System started at {datetime.now().strftime('%Y-%m-%d %H:%M')}{RESET}")
    
    while not stop_event.is_set():
        print(f"\n{YELLOW}=== NVT Screening Cycle ==={RESET}")
        for symbol in COINS_TO_MONITOR:
            nvt = get_nvt_ratio(symbol)
            if nvt is None:
                continue

            status_color = GREEN if nvt < NVT_UNDERVALUE_THRESHOLD else RED
            print(f"{status_color}{symbol}: NVT {nvt:.1f}{RESET}")

            if nvt < NVT_UNDERVALUE_THRESHOLD:
                if symbol not in monitored_coins:
                    monitored_coins.add(symbol)
                    thread = threading.Thread(
                        target=price_jump_monitor,
                        args=(symbol,),
                        daemon=True
                    )
                    thread.start()
            else:
                if symbol in monitored_coins:
                    monitored_coins.remove(symbol)
        
        time.sleep(CHECK_INTERVAL)

def signal_handler(sig, frame):
    """Handle Ctrl+C gracefully."""
    print(f"\n{YELLOW}Shutting down...{RESET}")
    stop_event.set()
    sys.exit(0)

if __name__ == "__main__":
    signal.signal(signal.SIGINT, signal_handler)
    spinner_thread = threading.Thread(target=spinner, daemon=True)
    spinner_thread.start()
    nvt_screener()