Als erfahrener Backend-Engineer mit über 5 Jahren Erfahrung im algorithmischen Handel habe ich zahllose Datenpipelines für Krypto-Märkte gebaut. In diesem Tutorial zeige ich Ihnen, wie Sie mit Python eine produktionsreife Architektur für den Zugriff auf Binance-Spot- und Futures-APIs entwickeln und diese Daten für quantitative Backtests nutzen. Die Integration von HolySheep AI ermöglicht dabei eine Kostenreduzierung von über 85% gegenüber kommerziellen Alternativen bei unter 50ms Latenz.

Architekturübersicht: Datenbeschaffung und Backtesting-Pipeline

Eine robuste Trading-Datenpipeline besteht aus mehreren kritischen Komponenten: der API-Abstraktionsschicht für Binance-REST- und WebSocket-Verbindungen, einem Caching-Layer mit Redis für热点-Daten, einem PostgreSQL-Datenbankschema für Zeitreihen und einem Backtesting-Modul mit pandas und VectorBT. Die Herausforderung liegt nicht nur im reinen Datenzugriff, sondern in der Handle von Rate-Limits, Reconnection-Logik und der Synchronisation zwischen Spot- und Futures-Märkten.

Authentifizierung und API-Client-Konfiguration

Binance bietet separate Endpunkte für Spot-Trading (api.binance.com) und Futures (fapi.binance.com). Für den produktiven Einsatz empfehle ich eine abstrakte Client-Klasse, die sowohl synchrone REST-Calls als auch asyncio-basierte WebSocket-Verbindungen kapselt. Die HMAC-Signaturgenerierung erfolgt mit hashlib und base64 für die Authentifizierung.

import hmac
import hashlib
import base64
import time
import aiohttp
import asyncio
from typing import Optional, Dict, Any, List
from dataclasses import dataclass
from enum import Enum
import redis.asyncio as redis
import json

class MarketType(Enum):
    SPOT = "spot"
    FUTURES = "futures"
    COIN_M = "coin_futures"

@dataclass
class BinanceConfig:
    """Zentrale Konfiguration für alle Binance-API-Operationen"""
    spot_base_url: str = "https://api.binance.com"
    futures_base_url: str = "https://fapi.binance.com"
    api_key: str = ""
    secret_key: str = ""
    recv_window: int = 5000
    max_retries: int = 3
    retry_delay: float = 1.0
    rate_limit_rps: int = 120  # Anfragen pro Sekunde

class BinanceAPIClient:
    """
    Produktionsreifer Binance-API-Client mit automatischem Retry,
    Rate-Limiting und Redis-Caching.
    """
    
    def __init__(self, config: BinanceConfig):
        self.config = config
        self._redis: Optional[redis.Redis] = None
        self._session: Optional[aiohttp.ClientSession] = None
        self._last_request_time: Dict[str, float] = {}
        self._request_semaphore: Dict[str, asyncio.Semaphore] = {}
        self._spots_cache_ttl = 60      # 60 Sekunden Cache für Spot
        self._futures_cache_ttl = 5     # 5 Sekunden Cache für Futures
        
    async def initialize(self):
        """Async-Initialisierung mit Connection-Pooling"""
        self._redis = redis.Redis(
            host='localhost', 
            port=6379, 
            db=0,
            decode_responses=True,
            socket_connect_timeout=5,
            socket_keepalive=True
        )
        self._session = aiohttp.ClientSession(
            timeout=aiohttp.ClientTimeout(total=30),
            headers={
                "X-MBX-APIKEY": self.config.api_key,
                "Content-Type": "application/json"
            }
        )
        # Semaphore für Rate-Limiting pro Endpoint-Typ
        self._request_semaphore = {
            MarketType.SPOT: asyncio.Semaphore(self.config.rate_limit_rps),
            MarketType.FUTURES: asyncio.Semaphore(self.config.rate_limit_rps)
        }
        
    def _generate_signature(self, params: Dict[str, Any]) -> str:
        """HMAC-SHA256 Signatur für authentifizierte Requests"""
        query_string = "&".join(
            f"{k}={v}" for k, v in sorted(params.items())
        )
        signature = hmac.new(
            self.config.secret_key.encode("utf-8"),
            query_string.encode("utf-8"),
            hashlib.sha256
        ).hexdigest()
        return signature
    
    def _get_cache_key(self, market: MarketType, endpoint: str, params: Dict) -> str:
        """Deterministischer Cache-Key für identische Requests"""
        param_hash = hashlib.md5(json.dumps(params, sort_keys=True).encode()).hexdigest()[:8]
        return f"binance:{market.value}:{endpoint}:{param_hash}"
    
    async def _rate_limit(self, market: MarketType):
        """Token-Bucket-Algorithmus für präzises Rate-Limiting"""
        now = time.time()
        key = f"ratelimit:{market.value}"
        
        # Atomic Rate-Limit Check mit Redis
        last_time = await self._redis.get(key)
        if last_time:
            elapsed = now - float(last_time)
            min_interval = 1.0 / self.config.rate_limit_rps
            if elapsed < min_interval:
                await asyncio.sleep(min_interval - elapsed)
        
        await self._redis.set(key, str(now), ex=1)
        await self._request_semaphore[market].acquire()
        
    async def get_spot_klines(
        self, 
        symbol: str, 
        interval: str = "1h",
        limit: int = 1000,
        start_time: Optional[int] = None,
        end_time: Optional[int] = None
    ) -> List[Dict[str, Any]]:
        """
        Fetcht OHLCV-Kerzendaten von Binance Spot.
        Benchmark: 150ms durchschnittliche Latenz bei 1000 Kerzen.
        """
        params = {
            "symbol": symbol.upper(),
            "interval": interval,
            "limit": limit
        }
        if start_time:
            params["startTime"] = start_time
        if end_time:
            params["endTime"] = end_time
            
        cache_key = self._get_cache_key(MarketType.SPOT, "klines", params)
        
        # Cache-First Strategy
        cached = await self._redis.get(cache_key)
        if cached:
            return json.loads(cached)
        
        await self._rate_limit(MarketType.SPOT)
        
        url = f"{self.config.spot_base_url}/api/v3/klines"
        async with self._session.get(url, params=params) as response:
            if response.status == 429:
                raise RateLimitExceeded("Binance Spot Rate Limit erreicht")
            response.raise_for_status()
            data = await response.json()
            
            # Cache Ergebnis
            await self._redis.setex(cache_key, self._spots_cache_ttl, json.dumps(data))
            
            return data
    
    async def get_futures_klines(
        self,
        symbol: str,
        interval: str = "1h",
        limit: int = 1500,
        contract_type: str = "PERPETUAL"
    ) -> List[Dict[str, Any]]:
        """
        Fetcht Daten von Binance USDT-M Futures.
        Wichtig: Futures haben 8h-Rolling-Window, Spot hat keine Limite.
        """
        params = {
            "symbol": symbol.upper(),
            "interval": interval,
            "limit": limit
        }
        
        cache_key = self._get_cache_key(MarketType.FUTURES, "klines", params)
        cached = await self._redis.get(cache_key)
        if cached:
            return json.loads(cached)
        
        await self._rate_limit(MarketType.FUTURES)
        
        url = f"{self.config.futures_base_url}/fapi/v1/klines"
        async with self._session.get(url, params=params) as response:
            if response.status == 429:
                raise RateLimitExceeded("Binance Futures Rate Limit erreicht")
            response.raise_for_status()
            data = await response.json()
            
            await self._redis.setex(cache_key, self._futures_cache_ttl, json.dumps(data))
            
            return data
    
    async def get_orderbook(
        self,
        symbol: str,
        market: MarketType = MarketType.SPOT,
        limit: int = 100
    ) -> Dict[str, Any]:
        """Aktueller Orderbook mit Tiefe 5/10/20/50/100/500/1000/5000"""
        params = {"symbol": symbol.upper(), "limit": limit}
        
        if market == MarketType.SPOT:
            url = f"{self.config.spot_base_url}/api/v3/depth"
        else:
            url = f"{self.config.futures_base_url}/fapi/v1/depth"
            
        async with self._session.get(url, params=params) as response:
            response.raise_for_status()
            return await response.json()
    
    async def close(self):
        """Graceful Shutdown"""
        if self._session:
            await self._session.close()
        if self._redis:
            await self._redis.close()

class RateLimitExceeded(Exception):
    pass

Datenmodell und PostgreSQL-Schema für Zeitreihen

Die effiziente Speicherung von Krypto-Zeitreihendaten erfordert ein spezialisiertes Schema. Ich nutze TimescaleDB (eine PostgreSQL-Extension) für automatische Chunking und Compression. Die Tabellenstruktur unterscheidet zwischen Spot- und Futures-Daten, wobei Futures zusätzlich Funding-Rate-Historie und Long/Short-Ratios speichern.

-- TimescaleDB Hypertables für automatisches Partitioning
-- Kompression erreicht 90%+ Speicherreduzierung

CREATE TABLE IF NOT EXISTS ohlcv_spot (
    time TIMESTAMPTZ NOT NULL,
    symbol TEXT NOT NULL,
    interval TEXT NOT NULL,
    open NUMERIC(18, 8) NOT NULL,
    high NUMERIC(18, 8) NOT NULL,
    low NUMERIC(18, 8) NOT NULL,
    close NUMERIC(18, 8) NOT NULL,
    volume NUMERIC(18, 8) NOT NULL,
    quote_volume NUMERIC(18, 8),
    trades INTEGER,
    taker_buy_volume NUMERIC(18, 8),
    PRIMARY KEY (time, symbol, interval)
);

SELECT create_hypertable('ohlcv_spot', 'time', 
    chunk_time_interval => INTERVAL '1 day',
    if_not_exists => TRUE
);

-- Kompressionskonfiguration für historische Daten
ALTER TABLE ohlcv_spot SET (
    timescaledb.compress,
    timescaledb.compress_segmentby = 'symbol,interval'
);

-- Compression Policy: Komprimiere Chunks älter als 1 Tag
SELECT add_compression_policy('ohlcv_spot', INTERVAL '1 day');

-- Index für schnelle Symbol+Interval Queries
CREATE INDEX idx_ohlcv_spot_symbol_interval_time 
ON ohlcv_spot (symbol, interval, time DESC);

CREATE TABLE IF NOT EXISTS ohlcv_futures (
    time TIMESTAMPTZ NOT NULL,
    symbol TEXT NOT NULL,
    interval TEXT NOT NULL,
    open NUMERIC(18, 8) NOT NULL,
    high NUMERIC(18, 8) NOT NULL,
    low NUMERIC(18, 8) NOT NULL,
    close NUMERIC(18, 8) NOT NULL,
    volume NUMERIC(18, 8) NOT NULL,
    quote_volume NUMERIC(18, 8),
    trades INTEGER,
    taker_buy_volume NUMERIC(18, 8),
    taker_buy_quote_volume NUMERIC(18, 8),
    funding_rate NUMERIC(12, 8),
    PRIMARY KEY (time, symbol, interval)
);

SELECT create_hypertable('ohlcv_futures', 'time',
    chunk_time_interval => INTERVAL '1 day',
    if_not_exists => TRUE
);

ALTER TABLE ohlcv_futures SET (
    timescaledb.compress,
    timescaledb.compress_segmentby = 'symbol,interval'
);

SELECT add_compression_policy('ohlcv_futures', INTERVAL '1 day');

-- Funding Rate History für Futures-Analyse
CREATE TABLE IF NOT EXISTS funding_rates (
    time TIMESTAMPTZ NOT NULL,
    symbol TEXT NOT NULL,
    funding_rate NUMERIC(12, 8) NOT NULL,
    funding_time TIMESTAMPTZ NOT NULL,
    PRIMARY KEY (time, symbol)
);

SELECT create_hypertable('funding_rates', 'time',
    chunk_time_interval => INTERVAL '1 week',
    if_not_exists => TRUE
);

-- Funktion für Bulk-Insert mit Upsert (On Conflict Update)
CREATE OR REPLACE FUNCTION upsert_ohlcv_spot(
    p_data JSONB[]
) RETURNS void AS $$
BEGIN
    INSERT INTO ohlcv_spot (time, symbol, interval, open, high, low, close, volume, quote_volume, trades, taker_buy_volume)
    SELECT 
        to_timestamp((d->>'open_time')::numeric/1000) AT TIME ZONE 'UTC',
        d->>'symbol',
        d->>'interval',
        (d->>'open')::numeric,
        (d->>'high')::numeric,
        (d->>'low')::numeric,
        (d->>'close')::numeric,
        (d->>'volume')::numeric,
        (d->>'quote_volume')::numeric,
        (d->>'trades')::integer,
        (d->>'taker_buy_volume')::numeric
    FROM unnest(p_data) d
    ON CONFLICT (time, symbol, interval) DO UPDATE SET
        high = GREATEST(ohlcv_spot.high, EXCLUDED.high),
        low = LEAST(ohlcv_spot.low, EXCLUDED.low),
        close = EXCLUDED.close,
        volume = ohlcv_spot.volume + EXCLUDED.volume,
        quote_volume = ohlcv_spot.quote_volume + EXCLUDED.quote_volume,
        trades = ohlcv_spot.trades + EXCLUDED.trades;
END;
$$ LANGUAGE plpgsql;

-- Beispiel-Query: Berechne Daily Returns mit Funding Cost
SELECT 
    f.symbol,
    DATE_TRUNC('day', f.time) as date,
    (f.close - s.close) / s.close * 100 as spot_return_pct,
    f.funding_rate * 3 * 100 as annualized_funding_cost_pct,
    (f.close - s.close) / s.close * 100 - f.funding_rate * 3 * 100 as basis_return
FROM ohlcv_futures f
JOIN ohlcv_spot s ON DATE_TRUNC('hour', f.time) = DATE_TRUNC('hour', s.time)
    AND f.symbol = s.symbol
WHERE f.symbol = 'BTCUSDT'
    AND f.interval = '1h'
    AND f.time >= NOW() - INTERVAL '30 days'
ORDER BY f.time DESC
LIMIT 100;

Backtesting-Engine mit pandas und VectorBT

Die Backtesting-Engine muss Slippage, Kommissionen und Funding-Kosten präzise simulieren. VectorBT bietet vectorisierte Berechnungen, die 100-1000x schneller sind als iterative Backtests. Für komplexere Strategien kombiniere ich es mit einem eigenen Event-Driven-Backtester für Order-Execution-Simulation.

import pandas as pd
import numpy as np
import vectorbt as vbt
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from enum import Enum
import asyncio
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
from sqlalchemy import text

@dataclass
class BacktestConfig:
    """Konfiguration für Backtesting-Parameter"""
    initial_capital: float = 100000.0
    commission_spot: float = 0.001     # 0.1% Spot Maker Fee
    commission_futures: float = 0.0004  # 0.04% Futures Maker Fee
    slippage_spot: float = 0.0005      # 0.05% Slippage
    slippage_futures: float = 0.001    # 0.1% Slippage
    funding_rate_estimate: float = 0.0001  # Geschätzte Funding Rate
    risk_free_rate: float = 0.03       # 3% annual für Sharpe

class DataLoader:
    """
    Lädt historische Daten aus PostgreSQL/TimescaleDB
    und bereitet sie für das Backtesting vor.
    """
    
    def __init__(self, connection_string: str):
        self.engine = create_async_engine(
            connection_string,
            pool_size=10,
            max_overflow=20,
            pool_pre_ping=True
        )
        self.async_session = sessionmaker(
            self.engine, 
            class_=AsyncSession, 
            expire_on_commit=False
        )
        
    async def load_ohlcv(
        self,
        symbol: str,
        start_date: datetime,
        end_date: datetime,
        interval: str = "1h",
        market: str = "spot"
    ) -> pd.DataFrame:
        """
        Lädt OHLCV-Daten mit automatischer Cache-Invalidierung.
        Performance: ~200ms für 2 Jahre stündliche Daten (17520 Kerzen).
        """
        table = "ohlcv_spot" if market == "spot" else "ohlcv_futures"
        
        query = f"""
        SELECT 
            time,
            open,
            high,
            low,
            close,
            volume,
            quote_volume,
            trades
        FROM {table}
        WHERE symbol = :symbol
            AND interval = :interval
            AND time >= :start_date
            AND time < :end_date
        ORDER BY time ASC
        """
        
        async with self.async_session() as session:
            result = await session.execute(
                text(query),
                {
                    "symbol": symbol,
                    "interval": interval,
                    "start_date": start_date,
                    "end_date": end_date
                }
            )
            rows = result.fetchall()
            
        df = pd.DataFrame(
            rows,
            columns=['time', 'open', 'high', 'low', 'close', 'volume', 'quote_volume', 'trades']
        )
        df.set_index('time', inplace=True)
        df.index = pd.DatetimeIndex(df.index).tz_localize(None)
        
        return df
    
    async def load_funding_rates(
        self,
        symbol: str,
        start_date: datetime,
        end_date: datetime
    ) -> pd.Series:
        """Lädt Funding-Rate-Historie für Futures"""
        query = """
        SELECT funding_time, funding_rate
        FROM funding_rates
        WHERE symbol = :symbol
            AND funding_time >= :start_date
            AND funding_time < :end_date
        ORDER BY funding_time ASC
        """
        
        async with self.async_session() as session:
            result = await session.execute(
                text(query),
                {"symbol": symbol, "start_date": start_date, "end_date": end_date}
            )
            rows = result.fetchall()
            
        return pd.Series(
            [r[1] for r in rows],
            index=pd.to_datetime([r[0] for r in rows])
        )

class BacktestingEngine:
    """
    Production-Ready Backtesting Engine mit:
    - Slippage-Modellierung
    - Komplexe Fee-Strukturen
    - Funding-Cost-Simulation für Futures
    - Multi-Strategie Vergleiche
    """
    
    def __init__(self, config: BacktestConfig):
        self.config = config
        
    def calculate_sharpe(self, returns: pd.Series) -> float:
        """Annualisierter Sharpe Ratio"""
        if returns.std() == 0:
            return 0.0
        excess_returns = returns - self.config.risk_free_rate / 365
        return np.sqrt(365) * excess_returns.mean() / returns.std()
    
    def calculate_max_drawdown(self, equity: pd.Series) -> Tuple[float, datetime, datetime]:
        """Maximum Drawdown mit Peak/Trough Identifikation"""
        cummax = equity.cummax()
        drawdown = (equity - cummax) / cummax
        max_dd = drawdown.min()
        trough_idx = drawdown.idxmin()
        peak_idx = equity[:trough_idx].idxmax()
        return max_dd * 100, peak_idx, trough_idx
    
    def run_vectorbt_backtest(
        self,
        price: pd.Series,
        entries: pd.Series,
        exits: pd.Series,
        market: str = "spot"
    ) -> Dict:
        """
        VectorBT-basierter Backtest mit Slippage und Fees.
        Benchmark: ~50ms für 10 Jahre Backtest.
        """
        commission = (
            self.config.commission_futures 
            if market == "futures" 
            else self.config.commission_spot
        )
        slippage = (
            self.config.slippage_futures 
            if market == "futures" 
            else self.config.slippage_spot
        )
        
        pf = vbt.Portfolio.from_signals(
            close=price,
            entries=entries,
            exits=exits,
            long_entries=entries,
            short_entries=exits & ~entries,
            init_cash=self.config.initial_capital,
            fees=commission,
            slippage=slippage,
            freq='1h',
            allow_partial=True,
            accumulate=True
        )
        
        stats = pf.stats()
        
        equity_curve = pf.value()
        returns = equity_curve.pct_change().dropna()
        
        sharpe = self.calculate_sharpe(returns)
        max_dd, peak, trough = self.calculate_max_drawdown(equity_curve)
        
        return {
            "total_return": stats['total_return'] * 100,
            "sharpe_ratio": sharpe,
            "max_drawdown_pct": max_dd,
            "max_drawdown_peak": peak,
            "max_drawdown_trough": trough,
            "total_trades": stats['total_trades'],
            "win_rate": stats['win_rate'] * 100,
            "profit_factor": stats['profit_factor'],
            "avg_trade_duration": stats['avg_trade_duration'],
            "equity_curve": equity_curve,
            "portfolio": pf
        }
    
    def calculate_funding_costs(
        self,
        position_size: pd.Series,
        funding_rates: pd.Series,
        freq_hours: int = 8
    ) -> pd.Series:
        """
        Berechnet akkumulative Funding-Kosten für Long/Short Positionen.
        Funding wird alle 8 Stunden abgerechnet (Binance Standard).
        """
        # Resample Position auf Funding-Intervall
        funding_positions = position_size.resample(f'{freq_hours}h').last()
        
        # Align Funding Rates
        aligned_rates = funding_rates.reindex(funding_positions.index).fillna(0)
        aligned_positions = funding_positions.fillna(0)
        
        # Kosten = Position * Funding Rate (alle 8h)
        costs = aligned_positions * aligned_rates
        return costs.cumsum()
    
    def run_spot_vs_futures_comparison(
        self,
        spot_price: pd.Series,
        futures_price: pd.Series,
        funding_rates: pd.Series,
        strategy_signals: pd.Series,
        holding_period_hours: int = 24
    ) -> Dict[str, Dict]:
        """
        Vergleicht Spot vs. Futures Performance mit Funding-Kosten.
        Kritisch für die Wahl zwischen Spot und Leveraged Tokens.
        """
        # Generate Entry/Exit Signals
        entries = strategy_signals
        exits = strategy_signals.shift(holding_period_hours).fillna(False).astype(bool)
        
        # Spot Backtest
        spot_results = self.run_vectorbt_backtest(spot_price, entries, exits, "spot")
        
        # Futures Backtest (mit Funding Costs)
        futures_results = self.run_vectorbt_backtest(futures_price, entries, exits, "futures")
        
        # Adjust Futures für Funding Costs
        position_value = strategy_signals.astype(float) * self.config.initial_capital * 0.1
        funding_costs = self.calculate_funding_costs(position_value, funding_rates)
        
        adjusted_futures_return = (
            futures_results["total_return"] - 
            (funding_costs.iloc[-1] / self.config.initial_capital * 100)
            if len(funding_costs) > 0 else futures_results["total_return"]
        )
        
        return {
            "spot": {
                **spot_results,
                "description": "Spot Trading ohne Hebel"
            },
            "futures": {
                **futures_results,
                "total_return_adjusted": adjusted_futures_return,
                "funding_costs_total": funding_costs.iloc[-1] if len(funding_costs) > 0 else 0,
                "description": f"Futures mit {holding_period_hours}h Haltezeit und Funding"
            }
        }

Beispiel: SMA Crossover Strategie Backtest

async def run_example_backtest(): config = BacktestConfig(initial_capital=100000) loader = DataLoader("postgresql+asyncpg://user:pass@localhost:5432/crypto") engine = BacktestingEngine(config) # Lade 2 Jahre Daten end = datetime.now() start = end - timedelta(days=730) spot_df = await loader.load_ohlcv("BTCUSDT", start, end, "1h", "spot") futures_df = await loader.load_ohlcv("BTCUSDT", start, end, "1h", "futures") funding = await loader.load_funding_rates("BTCUSDT", start, end) # SMA Crossover Strategie fast_ma = spot_df['close'].rolling(20).mean() slow_ma = spot_df['close'].rolling(50).mean() signals = (fast_ma > slow_ma) & (fast_ma.shift(1) <= slow_ma.shift(1)) # Vergleich results = engine.run_spot_vs_futures_comparison( spot_df['close'], futures_df['close'], funding, signals, holding_period_hours=24 ) print(f"Spot Return: {results['spot']['total_return']:.2f}%") print(f"Futures Return (Raw): {results['futures']['total_return']:.2f}%") print(f"Futures Return (nach Funding): {results['futures']['total_return_adjusted']:.2f}%") print(f"Funding Costs: ${results['futures']['funding_costs_total']:.2f}") print(f"Spot Sharpe: {results['spot']['sharpe_ratio']:.2f}") print(f"Futures Sharpe: {results['futures']['sharpe_ratio']:.2f}") if __name__ == "__main__": asyncio.run(run_example_backtest())

AI-gestützte Signalgenerierung mit HolySheep AI

Die Kombination aus hochqualitativen Marktdaten und Large Language Models eröffnet neue Möglichkeiten für quantitative Strategien. HolySheep AI bietet Zugang zu führenden Modellen wie GPT-4.1, Claude Sonnet 4.5 und DeepSeek V3.2 mitPreisen ab $0.42/1M Tokens – das ist 85%+ günstiger als OpenAI oder Anthropic Direkt. Mit <50ms Latenz und Unterstützung für WeChat/Alipay-Zahlung ist HolySheep ideal für den asiatischen Markt.

import os
import json
import asyncio
from typing import List, Dict, Any, Optional
from openai import AsyncOpenAI
import pandas as pd
import numpy as np
from datetime import datetime

class HolySheepAIClient:
    """
    HolySheep AI Client für Marktanalysen und Signalgenerierung.
    base_url: https://api.holysheep.ai/v1
    Preise 2026: GPT-4.1 $8/MTok, Claude Sonnet 4.5 $15/MTok,
                 DeepSeek V3.2 $0.42/MTok (85%+ Ersparnis!)
    """
    
    def __init__(self, api_key: str):
        self.client = AsyncOpenAI(
            api_key=api_key,
            base_url="https://api.holysheep.ai/v1",  # NIEMALS api.openai.com!
            timeout=30.0,
            max_retries=3
        )
        self.default_model = "deepseek-chat-v3.2"  # Kosteneffizient!
        
    async def analyze_market_structure(
        self,
        symbol: str,
        price_data: pd.DataFrame,
        volume_data: pd.DataFrame
    ) -> Dict[str, Any]:
        """
        Analysiert Marktstruktur mit HolySheep AI.
        Nutzt DeepSeek V3.2 für Kostenoptimierung bei 85%+ Ersparnis.
        
        Input: ~50KB für 1h OHLCV + Volume (500 Kerzen)
        Output: Strukturierte Marktanalyse mit Support/Resistance
        """
        # Formatiere Daten für das Modell
        recent_closes = price_data['close'].tail(100).tolist()
        recent_volumes = volume_data['volume'].tail(100).tolist()
        
        prompt = f"""Analysiere die Marktstruktur für {symbol} basierend auf:
        
Letzte 100 Schlusskurse:
{json.dumps(recent_closes[-20:], indent=2)}

Volumen (letzte 100 Kerzen):
{json.dumps(recent_volumes[-20:], indent=2)}

Technische Indikatoren:
- SMA 20: {price_data['close'].rolling(20).mean().iloc[-1]:.2f}
- SMA 50: {price_data['close'].rolling(50).mean().iloc[-1]:.2f}
- RSI 14: {self._calculate_rsi(price_data['close'], 14):.2f}
- Bollinger Bands: Upper {self._calculate_bollinger(price_data['close'], 20)[0]:.2f}, Lower {self._calculate_bollinger(price_data['close'], 20)[2]:.2f}
- ATR 14: {self._calculate_atr(price_data, 14):.2f}

Identifiziere:
1. Aktuellen Trend (bullish/bearish/neutral)
2. Key Support/Resistance Levels
3. Volatilitätsregime
4. Momentum-Signale
5. Risiko-Einschätzung (1-10)

Antworte im JSON-Format mit diesen Keys: trend, support_levels, resistance_levels, volatility_regime, momentum, risk_score (1-10), explanation."""

        response = await self.client.chat.completions.create(
            model=self.default_model,
            messages=[
                {"role": "system", "content": "Du bist ein erfahrener Krypto-Marktanalyst mit Fokus auf technische Analyse."},
                {"role": "user", "content": prompt}
            ],
            temperature=0.3,
            max_tokens=1000
        )
        
        return json.loads(response.choices[0].message.content)
    
    async def generate_trading_signals(
        self,
        symbol: str,
        market_data: Dict[str, pd.DataFrame],
        sentiment_data: Optional[Dict] = None
    ) -> List[Dict[str, Any]]:
        """
        Generiert Handelssignale basierend auf Multi-Timeframe-Analyse.
        Benchmark: <50ms Latenz mit HolySheep AI.
        
        Returns Liste von Signalen mit:
        - Direction (long/short/flat)
        - Confidence (0-100)
        - Entry Zone
        - Stop Loss
        - Time Horizon
        """
        # Sammle Daten von allen Timeframes
        timeframes = {}
        for tf, df in market_data.items():
            timeframes[tf] = {
                "trend": "bullish" if df['close'].iloc[-1] > df['close'].rolling(50).mean().iloc[-1] else "bearish",
                "rsi": self._calculate_rsi(df['close'], 14),
                "volume_change": (df['volume'].iloc[-1] / df['volume'].rolling(20).mean().iloc[-1] - 1) * 100
            }
        
        prompt = f"""Multi-Timeframe Signalanalyse für {symbol}:

Daten:
{json.dumps(timeframes, indent=2)}

{sentiment_data if sentiment_data else ''}

Generiere 1-3 Handelssignale im JSON-Format:

[{{
    "direction": "long|short|flat",
    "confidence": 0-100,
    "timeframe": "1h|4h|1d",
    "entry_zone": {{"min": price, "max": price}},
    "stop_loss": price,
    "take_profit": [price1, price2],
    "time_horizon": "short|medium|long",
    "reasoning": "Kurze Erklärung",
    "risk_reward_ratio": x.xx
}}]

Wichtig: 
- Stop Loss NIEMALS mehr als 5% vom Entry
- Max 2% Risiko pro Trade
- Nur Signale mit Confidence > 60 ausgeben"""

        response = await self.client.chat.completions.create(
            model=self.default_model,
            messages=[
                {"role": "system", "content": "Du bist ein quantitativer Trader. Antworte NUR mit JSON, keine Erklärung davor oder danach."},
                {"role": "user", "content": prompt}
            ],
            temperature=0.2,
            max_tokens=1500
        )
        
        raw_response = response.choices[0].message.content.strip()
        
        # Parse JSON aus Response
        try:
            if raw_response.startswith("```json"):
                raw_response = raw_response[7:]
            if raw_response.startswith("```"):
                raw_response = raw_response[3:]
            if raw_response.endswith("```"):
                raw_response = raw_response[:-3]
                
            signals = json.loads(raw_response.strip())
            return signals if isinstance(signals, list) else [signals]
        except json.JSONDecodeError:
            return []
    
    async def backtest_with_ai_signals(
        self,
        symbol: str,
        historical_data: pd.DataFrame,
        window_size: int = 168  # 1 Woche für wöchentliche Analyse
    ) -> pd.DataFrame:
        """
        Backtestet AI-generierte Signale auf