import requests
import json
import time
import datetime
import pandas as pd
import numpy as np
from decimal import Decimal

class BinanceAITradingData:
    def __init__(self):
        self.base_url = "https://api.binance.com/api/v3"
        self.data_file = "binance_data.jsonl"
        self.initialize_data_file()
        
        # Zeitintervalle für technische Indikatoren
        self.intervals = ['1m', '3m', '5m', '15m', '1h', '4h']
        
    def initialize_data_file(self):
        """Initialisiert die Log-Datei"""
        try:
            with open(self.data_file, 'w') as f:
                f.write("# AI Trading Data with Technical Indicators\n")
            print(f"📁 Data file initialized: {self.data_file}")
        except Exception as e:
            print(f"❌ Error initializing file: {e}")

    def get_klines(self, symbol='BTCUSDT', interval='1m', limit=500):
        """Holt KLine/Candlestick Daten für technische Indikatoren"""
        try:
            url = f"{self.base_url}/klines?symbol={symbol}&interval={interval}&limit={limit}"
            response = requests.get(url)
            data = response.json()
            
            # Konvertiere zu DataFrame
            df = pd.DataFrame(data, columns=[
                'open_time', 'open', 'high', 'low', 'close', 'volume',
                'close_time', 'quote_asset_volume', 'number_of_trades',
                'taker_buy_base_volume', 'taker_buy_quote_volume', 'ignore'
            ])
            
            # Konvertiere zu float
            for col in ['open', 'high', 'low', 'close', 'volume']:
                df[col] = df[col].astype(float)
                
            df['open_time'] = pd.to_datetime(df['open_time'], unit='ms')
            return df
            
        except Exception as e:
            print(f"❌ Error getting klines for {interval}: {e}")
            return None

    def calculate_ema(self, prices, period):
        """Berechnet Exponential Moving Average (EMA)"""
        return prices.ewm(span=period, adjust=False).mean()

    def calculate_rsi(self, prices, period=14):
        """Berechnet Relative Strength Index (RSI)"""
        delta = prices.diff()
        gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
        rs = gain / loss
        rsi = 100 - (100 / (1 + rs))
        return rsi

    def calculate_macd(self, prices, fast=12, slow=26, signal=9):
        """Berechnet MACD"""
        ema_fast = self.calculate_ema(prices, fast)
        ema_slow = self.calculate_ema(prices, slow)
        macd = ema_fast - ema_slow
        macd_signal = self.calculate_ema(macd, signal)
        macd_histogram = macd - macd_signal
        return macd, macd_signal, macd_histogram

    def calculate_mfi(self, high, low, close, volume, period=14):
        """Berechnet Money Flow Index (MFI)"""
        typical_price = (high + low + close) / 3
        money_flow = typical_price * volume
        
        positive_flow = money_flow.where(typical_price > typical_price.shift(1), 0)
        negative_flow = money_flow.where(typical_price < typical_price.shift(1), 0)
        
        positive_flow_sum = positive_flow.rolling(window=period).sum()
        negative_flow_sum = negative_flow.rolling(window=period).sum()
        
        money_ratio = positive_flow_sum / negative_flow_sum
        mfi = 100 - (100 / (1 + money_ratio))
        return mfi

    def calculate_bollinger_bands(self, prices, period=20, std_dev=2):
        """Berechnet Bollinger Bands"""
        sma = prices.rolling(window=period).mean()
        std = prices.rolling(window=period).std()
        
        upper_band = sma + (std * std_dev)
        lower_band = sma - (std * std_dev)
        
        return upper_band, sma, lower_band

    def calculate_atr(self, high, low, close, period=14):
        """Berechnet Average True Range (ATR)"""
        tr1 = high - low
        tr2 = abs(high - close.shift())
        tr3 = abs(low - close.shift())
        tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
        atr = tr.rolling(window=period).mean()
        return atr

    def calculate_technical_indicators(self, df):
        """Berechnet alle technischen Indikatoren"""
        if df is None or len(df) < 200:
            return None
            
        try:
            # Preis-Serie
            close = df['close']
            high = df['high']
            low = df['low']
            volume = df['volume']
            
            indicators = {}
            
            # MACD
            macd, macd_signal, macd_histogram = self.calculate_macd(close)
            indicators['macd'] = float(macd.iloc[-1])
            indicators['macd_signal'] = float(macd_signal.iloc[-1])
            indicators['macd_histogram'] = float(macd_histogram.iloc[-1])
            
            # RSI (14 Perioden)
            rsi = self.calculate_rsi(close, 14)
            indicators['rsi_14'] = float(rsi.iloc[-1])
            
            # Volumen Indikatoren
            indicators['volume'] = float(volume.iloc[-1])
            indicators['volume_sma_20'] = float(volume.rolling(20).mean().iloc[-1])
            
            # Money Flow Index (MFI)
            mfi = self.calculate_mfi(high, low, close, volume, 14)
            indicators['mfi_14'] = float(mfi.iloc[-1])
            
            # EMAs
            indicators['ema_20'] = float(self.calculate_ema(close, 20).iloc[-1])
            indicators['ema_50'] = float(self.calculate_ema(close, 50).iloc[-1])
            indicators['ema_200'] = float(self.calculate_ema(close, 200).iloc[-1])
            
            # Bollinger Bands
            bb_upper, bb_middle, bb_lower = self.calculate_bollinger_bands(close)
            indicators['bb_upper'] = float(bb_upper.iloc[-1])
            indicators['bb_middle'] = float(bb_middle.iloc[-1])
            indicators['bb_lower'] = float(bb_lower.iloc[-1])
            indicators['bb_width'] = indicators['bb_upper'] - indicators['bb_lower']
            
            # ATR (Average True Range)
            atr = self.calculate_atr(high, low, close)
            indicators['atr_14'] = float(atr.iloc[-1])
            
            # Einfache Moving Averages für zusätzliche Kontext
            indicators['sma_20'] = float(close.rolling(20).mean().iloc[-1])
            indicators['sma_50'] = float(close.rolling(50).mean().iloc[-1])
            
            return indicators
            
        except Exception as e:
            print(f"❌ Error calculating indicators: {e}")
            return None

    def get_current_market_data(self):
        """Holt aktuelle Marktdaten"""
        try:
            # Aktueller Preis
            price_response = requests.get(f"{self.base_url}/ticker/price?symbol=BTCUSDT")
            price_data = price_response.json()
            
            # 24h Statistik
            ticker_response = requests.get(f"{self.base_url}/ticker/24hr?symbol=BTCUSDT")
            ticker_data = ticker_response.json()
            
            # Orderbuch
            orderbook_response = requests.get(f"{self.base_url}/depth?symbol=BTCUSDT&limit=10")
            orderbook_data = orderbook_response.json()
            
            return {
                'current_price': float(price_data['price']),
                'symbol': price_data['symbol'],
                '24h_change': float(ticker_data['priceChange']),
                '24h_change_percent': float(ticker_data['priceChangePercent']),
                '24h_high': float(ticker_data['highPrice']),
                '24h_low': float(ticker_data['lowPrice']),
                '24h_volume': float(ticker_data['volume']),
                'orderbook_bids': [[float(bid[0]), float(bid[1])] for bid in orderbook_data['bids'][:3]],
                'orderbook_asks': [[float(ask[0]), float(ask[1])] for ask in orderbook_data['asks'][:3]]
            }
            
        except Exception as e:
            print(f"❌ Error getting market data: {e}")
            return None

    def collect_all_data(self):
        """Sammelt alle Daten und technischen Indikatoren"""
        timestamp = datetime.datetime.utcnow().isoformat()
        
        # Aktuelle Marktdaten
        market_data = self.get_current_market_data()
        if not market_data:
            return None
        
        # Technische Indikatoren für alle Zeitintervalle
        technical_data = {}
        
        for interval in self.intervals:
            df = self.get_klines(interval=interval, limit=500)
            indicators = self.calculate_technical_indicators(df)
            if indicators:
                technical_data[interval] = indicators
            else:
                print(f"⚠️ No indicators for {interval}")
        
        # Kombiniere alle Daten
        combined_data = {
            'timestamp': timestamp,
            'market_data': market_data,
            'technical_indicators': technical_data
        }
        
        return combined_data

    def display_terminal_output(self, data):
        """Zeigt übersichtliche Darstellung im Terminal"""
        if not data:
            return
        
        market = data['market_data']
        tech_1m = data['technical_indicators'].get('1m', {})
        tech_5m = data['technical_indicators'].get('5m', {})
        tech_1h = data['technical_indicators'].get('1h', {})
        
        print("\n" + "="*80)
        print(f"🤖 BTC/USDT AI TRADING DATA - {data['timestamp']}")
        print("="*80)
        
        # Marktdaten
        print(f"💰 PRICE: ${market['current_price']:,.2f}")
        change_color = "🟢" if market['24h_change'] >= 0 else "🔴"
        print(f"{change_color} 24h: {market['24h_change_percent']:+.2f}% | "
              f"H: ${market['24h_high']:,.2f} | L: ${market['24h_low']:,.2f}")
        print(f"📊 Volume: {market['24h_volume']:,.0f} BTC")
        
        # Orderbuch
        asks = [f"${ask[0]:.1f}" for ask in market['orderbook_asks']]
        bids = [f"${bid[0]:.1f}" for bid in market['orderbook_bids']]
        print(f"📈 Orderbook: ASK {', '.join(asks)} | BID {', '.join(bids)}")
        
        print("-"*80)
        
        # Technische Indikatoren - 1 Minute
        if tech_1m:
            print("⏰ 1MINUTE:")
            print(f"   RSI: {tech_1m.get('rsi_14', 0):.1f} | "
                  f"MACD: {tech_1m.get('macd', 0):.3f} | "
                  f"MFI: {tech_1m.get('mfi_14', 0):.1f}")
            print(f"   EMA20: ${tech_1m.get('ema_20', 0):.2f} | "
                  f"EMA50: ${tech_1m.get('ema_50', 0):.2f}")
        
        # Technische Indikatoren - 5 Minuten
        if tech_5m:
            print("⏰ 5MINUTE:")
            print(f"   RSI: {tech_5m.get('rsi_14', 0):.1f} | "
                  f"MACD Hist: {tech_5m.get('macd_histogram', 0):.3f} | "
                  f"BB Width: {tech_5m.get('bb_width', 0):.2f}")
        
        # Technische Indikatoren - 1 Stunde
        if tech_1h:
            print("⏰ 1HOUR:")
            print(f"   RSI: {tech_1h.get('rsi_14', 0):.1f} | "
                  f"EMA200: ${tech_1h.get('ema_200', 0):.2f} | "
                  f"ATR: {tech_1h.get('atr_14', 0):.2f}")
        
        print("="*80)

    def save_to_file(self, data):
        """Speichert Daten in JSONL Format für LLM Verarbeitung"""
        if not data:
            return
        
        try:
            with open(self.data_file, 'a') as f:
                # Konvertiere numpy types zu Python native types für JSON
                def convert_types(obj):
                    if isinstance(obj, (np.float32, np.float64)):
                        return float(obj)
                    elif isinstance(obj, (np.int32, np.int64)):
                        return int(obj)
                    elif isinstance(obj, dict):
                        return {k: convert_types(v) for k, v in obj.items()}
                    elif isinstance(obj, list):
                        return [convert_types(i) for i in obj]
                    return obj
                
                json_data = convert_types(data)
                f.write(json.dumps(json_data) + '\n')
                
        except Exception as e:
            print(f"❌ Error saving data: {e}")

    def run_collector(self):
        """Hauptfunktion die alle 30 Sekunden läuft"""
        print("🚀 Starting Binance AI Trading Data Collector...")
        print("⏰ Collecting data every 30 seconds")
        print("📊 Technical Indicators: MACD, RSI, MFI, EMA20/50/200, Volume, ATR, Bollinger Bands")
        print("⏱️  Timeframes: 1m, 3m, 5m, 15m, 1h, 4h")
        print("💾 Data saved to:", self.data_file)
        
        try:
            while True:
                start_time = time.time()
                
                data = self.collect_all_data()
                
                if data:
                    self.display_terminal_output(data)
                    self.save_to_file(data)
                    print("✅ Data collected and saved successfully")
                else:
                    print("❌ Failed to collect data")
                
                # Exakte 30 Sekunden Intervalle
                processing_time = time.time() - start_time
                sleep_time = max(0, 30 - processing_time)
                
                print(f"⏳ Next update in {sleep_time:.1f}s ({datetime.datetime.now().strftime('%H:%M:%S')})")
                time.sleep(sleep_time)
                
        except KeyboardInterrupt:
            print("\n🛑 Collector stopped by user")
        except Exception as e:
            print(f"💥 Unexpected error: {e}")

if __name__ == "__main__":
    collector = BinanceAITradingData()
    collector.run_collector()