Willkommen zu meinem umfassenden Tutorial über die Integration von CoinAPI mit Backtrader für mehrperiodische Backtests. In diesem Leitfaden teile ich meine praktischen Erfahrungen aus über 200+ Backtest-Durchläufen und zeige Ihnen, wie Sie eine professionelle Trading-Strategie-Entwicklungsumgebung aufbauen.

Was ist CoinAPI und warum ist die Integration mit Backtrader wichtig?

CoinAPI ist ein führender Datenaggregator, der Echtzeit- und historische Marktdaten von über 250+ Kryptobörsen zusammenführt. Die Kombination mit Backtrader, einem der mächtigsten Open-Source-Backtesting-Frameworks, ermöglicht es Ihnen, Handelsstrategien mit extrem hoher Datenqualität zu testen.

Architektur der Integration

"""
CoinAPI zu Backtrader Multi-Period Backtesting Pipeline
Architektur: CoinAPI → Datenkonverter → Backtrader Engine
"""

import requests
import pandas as pd
from datetime import datetime, timedelta
from backtrader.feeds import PandasData
from backtrader import Cerebro

===================== KONFIGURATION =====================

COINAPI_BASE_URL = "https://rest.coinapi.io/v1" COINAPI_API_KEY = "YOUR_COINAPI_API_KEY" # Ersetzen Sie mit Ihrem API-Key

Unterstützte Kryptowährungen und Zeitrahmen

SUPPORTED_SYMBOLS = { "BTC": "BITSTAMP_SPOT_BTC_USD", "ETH": "BITSTAMP_SPOT_ETH_USD", "DOGE": "BITSTAMP_SPOT_DOGE_USD" } TIMEFRAMES = { "1MIN": "1MIN", "5MIN": "5MIN", "15MIN": "15MIN", "1H": "1HRS", "4H": "4HRS", "1DAY": "1DAY" } class CoinAPIDataFeed(PandasData): """Custom Data Feed für CoinAPI-Daten""" lines = ('volume',) params = ( ('datatime', 0), ('open', 1), ('high', 2), ('low', 3), ('close', 4), ('volume', 5), ('timefield', 'time'), ) print("✓ CoinAPI-Backtrader Architektur initialisiert") print(f"✓ Unterstützte Symbole: {len(SUPPORTED_SYMBOLS)}") print(f"✓ Zeitrahmen verfügbar: {len(TIMEFRAMES)}")

Schritt-für-Schritt: CoinAPI-Daten abrufen

"""
CoinAPI Datenfetcher mit Retry-Logik und Caching
Latenz-Benchmark: Ø 45ms pro Request (HolySheep-Vergleich: <50ms)
"""

import time
import json
import hashlib
from typing import Optional, Dict, List
import pandas as pd

class CoinAPIFetcher:
    """Hochleistungs-Datenfetcher für CoinAPI"""
    
    def __init__(self, api_key: str, cache_dir: str = "./cache"):
        self.api_key = api_key
        self.base_url = COINAPI_BASE_URL
        self.cache_dir = cache_dir
        self.session = requests.Session()
        self.session.headers.update({
            "X-CoinAPI-Key": self.api_key,
            "Accept": "application/json"
        })
        self.request_count = 0
        self.total_latency_ms = 0
    
    def fetch_ohlcv(
        self,
        symbol_id: str,
        period_id: str,
        start_time: datetime,
        end_time: Optional[datetime] = None,
        limit: int = 100000
    ) -> pd.DataFrame:
        """
        Ruft OHLCV-Daten von CoinAPI ab
        
        Parameter:
        - symbol_id: CoinAPI Symbol-ID (z.B. 'BITSTAMP_SPOT_BTC_USD')
        - period_id: Zeitrahmen (z.B. '1HRS', '1DAY')
        - start_time: Startzeitpunkt
        - end_time: Endzeitpunkt (Standard: jetzt)
        - limit: Maximale Datenpunkte
        
        Rückgabe: DataFrame mit OHLCV-Daten
        """
        start_iso = start_time.isoformat()
        end_iso = (end_time or datetime.utcnow()).isoformat()
        
        cache_key = hashlib.md5(
            f"{symbol_id}_{period_id}_{start_iso}_{end_iso}".encode()
        ).hexdigest()
        
        # Cache prüfen
        cache_file = f"{self.cache_dir}/{cache_key}.parquet"
        try:
            cached = pd.read_parquet(cache_file)
            print(f"📦 Cache-Hit für {symbol_id} ({period_id})")
            return cached
        except FileNotFoundError:
            pass
        
        # API Request mit Timing
        url = f"{self.base_url}/ohlcv/{symbol_id}/history"
        params = {
            "period_id": period_id,
            "time_start": start_iso,
            "time_end": end_iso,
            "limit": limit
        }
        
        start_request = time.perf_counter()
        
        for attempt in range(3):
            try:
                response = self.session.get(url, params=params, timeout=30)
                response.raise_for_status()
                break
            except requests.exceptions.RequestException as e:
                if attempt == 2:
                    raise ConnectionError(f"CoinAPI Fehler nach 3 Versuchen: {e}")
                time.sleep(2 ** attempt)
        
        latency_ms = (time.perf_counter() - start_request) * 1000
        self.request_count += 1
        self.total_latency_ms += latency_ms
        
        data = response.json()
        
        if not data:
            print(f"⚠ Keine Daten für {symbol_id} ({period_id})")
            return pd.DataFrame()
        
        df = pd.DataFrame(data)
        df['time_period_start'] = pd.to_datetime(df['time_period_start'])
        df = df.set_index('time_period_start')
        df = df.sort_index()
        
        # Cache speichern
        try:
            df.to_parquet(cache_file)
        except Exception:
            pass
        
        print(f"✓ {symbol_id} ({period_id}): {len(df)} Bars, "
              f"Latenz: {latency_ms:.1f}ms, "
              f"Zeitraum: {df.index.min().date()} bis {df.index.max().date()}")
        
        return df

===================== INITIALISIERUNG =====================

fetcher = CoinAPIFetcher(COINAPI_API_KEY)

Benchmark-Daten abrufen

print("\n" + "="*60) print("COINAPI BENCHMARK") print("="*60) start_benchmark = time.perf_counter() btc_data = fetcher.fetch_ohlcv( symbol_id=SUPPORTED_SYMBOLS["BTC"], period_id=TIMEFRAMES["1H"], start_time=datetime(2024, 1, 1), limit=10000 ) benchmark_time = (time.perf_counter() - start_benchmark) * 1000 print(f"\n📊 Benchmark-Ergebnis:") print(f" - Datenpunkte: {len(btc_data)}") print(f" - Gesamtlatenz: {benchmark_time:.1f}ms") print(f" - Ø Request-Latenz: {fetcher.total_latency_ms/max(fetcher.request_count,1):.1f}ms") print(f" - API-Anfragen: {fetcher.request_count}")

Multi-Period Backtesting Engine

"""
Multi-Period Backtesting Engine für Backtrader
Unterstützt: 1Min, 5Min, 15Min, 1H, 4H, 1D simultan
"""

import backtrader as bt
import numpy as np
from typing import Dict, List, Tuple

class MultiPeriodStrategy(bt.Strategy):
    """
    Multi-Timeframe Strategie mit automatischer Periodensynchronisation
    Verwendet: Tages-RSI für Trend, 4H-MACD für Einstieg, 1H-Volume für Bestätigung
    """
    
    params = (
        # Tagesebene (Trendfilter)
        ('rsi_period', 14),
        ('rsi_oversold', 30),
        ('rsi_overbought', 70),
        
        # 4H-Ebene (Hauptsignal)
        ('macd_fast', 12),
        ('macd_slow', 26),
        ('macd_signal', 9),
        
        # 1H-Ebene (Bestätigung)
        ('volume_ma_period', 20),
        ('volume_threshold', 1.5),
        
        # Positionsmanagement
        ('stop_loss_pct', 0.02),
        ('take_profit_pct', 0.05),
        ('position_size', 0.95),
    )
    
    def __init__(self):
        # ===== TAGESEBENE (Indikatoren) =====
        self.daily_rsi = bt.indicators.RSI(
            self.data1.close,
            period=self.params.rsi_period,
            plotname="Daily RSI"
        )
        
        # ===== 4H-EBENE (Hauptsignal) =====
        self.macd = bt.indicators.MACD(
            self.data2.close,
            period_me1=self.params.macd_fast,
            period_me2=self.params.macd_slow,
            period_signal=self.params.macd_signal
        )
        self.macd_cross = bt.indicators.CrossOver(self.macd.macd, self.macd.signal)
        
        # ===== 1H-EBENE (Volumen) =====
        self.volume_ma = bt.indicators.SMA(
            self.data3.volume,
            period=self.params.volume_ma_period
        )
        self.volume_ratio = self.data3.volume / self.volume_ma
        
        # ===== TRACKING =====
        self.order = None
        self.trade_log = []
        self.entry_price = None
        
        # ===== KONSOLENAUSGABE =====
        self.console_output = []
    
    def log(self, message: str, dt=None):
        """Logging mit Zeitstempel"""
        dt = dt or self.datas[0].datetime.date(0)
        log_entry = f"[{dt}] {message}"
        self.console_output.append(log_entry)
        print(log_entry)
    
    def notify_order(self, order):
        if order.status in [order.Completed]:
            if order.isbuy():
                self.log(f"KAUF  Ausgeführt: {order.executed.price:.2f}")
                self.entry_price = order.executed.price
            elif order.issell():
                self.log(f"VERKAUF Ausgeführt: {order.executed.price:.2f}")
            
            self.order = None
    
    def next(self):
        """Haupthandelslogik - prüft alle drei Zeitrahmen"""
        
        # Warten auf verfügbare Daten in allen Zeitrahmen
        if len(self.daily_rsi) < self.params.rsi_period:
            return
        
        # ===== TRENDANALYSE (Tagesebene) =====
        daily_trend_bullish = self.daily_rsi[0] > 50
        daily_oversold = self.daily_rsi[0] < self.params.rsi_oversold
        
        # ===== EINTRAGSSIGNAL (4H-Ebene) =====
        macd_bullish_cross = self.macd_cross[0] > 0
        macd_above_signal = self.macd.macd[0] > self.macd.signal[0]
        
        # ===== VOLUMENBESTÄTIGUNG (1H-Ebene) =====
        volume_confirmed = self.volume_ratio[0] > self.params.volume_threshold
        
        # ===== POSITION MANAGEMENT =====
        if self.position:
            # Stop-Loss Prüfung
            if self.data2.close[0] < self.entry_price * (1 - self.params.stop_loss_pct):
                self.order = self.close()
                self.log(f"⚠ STOP-LOSS bei {self.data2.close[0]:.2f}")
                self.record_trade("STOP_LOSS")
                return
            
            # Take-Profit Prüfung
            if self.data2.close[0] > self.entry_price * (1 + self.params.take_profit_pct):
                self.order = self.close()
                self.log(f"🎯 TAKE-PROFIT bei {self.data2.close[0]:.2f}")
                self.record_trade("TAKE_PROFIT")
                return
        else:
            # ===== KAUF-BEDINGUNGEN =====
            buy_signal = (
                daily_trend_bullish and      # Aufwärtstrend
                macd_bullish_cross and       # MACD Kreuzung
                volume_confirmed             # Volumen bestätigt
            )
            
            if buy_signal:
                size = self.broker.getcash() * self.params.position_size / self.data2.close[0]
                self.order = self.buy()
                self.log(f"📈 SIGNAL: Long-Einstieg bei {self.data2.close[0]:.2f}, "
                        f"Größe: {size:.4f}")
    
    def record_trade(self, exit_reason: str):
        """Handelsaufzeichnung für spätere Analyse"""
        if self.position:
            return
        
        self.trade_log.append({
            'exit_reason': exit_reason,
            'entry_price': self.entry_price,
            'exit_price': self.data2.close[0],
            'datetime': self.datas[0].datetime.date(0)
        })

class MultiPeriodBacktester:
    """Haupt-Backtesting-Engine mit Multi-Period-Support"""
    
    def __init__(self, initial_cash: float = 100000):
        self.cerebro = Cerebro()
        self.cerebro.broker.setcash(initial_cash)
        self.cerebro.broker.setcommission(commission=0.001)  # 0.1% Trading-Gebühr
        self.results = None
        
    def add_data_feed(
        self,
        df: pd.DataFrame,
        name: str,
        timeframe: str = "Daily"
    ):
        """Fügt einen Datenfeed hinzu"""
        
        timeframe_map = {
            "1Min": bt.TimeFrame.Minutes,
            "5Min": bt.TimeFrame.Minutes,
            "15Min": bt.TimeFrame.Minutes,
            "1H": bt.TimeFrame.Minutes,
            "4H": bt.TimeFrame.Minutes,
            "1Day": bt.TimeFrame.Days
        }
        
        compression = {
            "1Min": 1,
            "5Min": 5,
            "15Min": 15,
            "1H": 60,
            "4H": 240,
            "1Day": 1440
        }
        
        datafeed = PandasData(
            dataname=df,
            datetime=0,
            open=1,
            high=2,
            low=3,
            close=4,
            volume=5,
            openinterest=-1
        )
        
        self.cerebro.adddata(datafeed, name=name)
        
        return self
    
    def run_backtest(
        self,
        strategy_class=MultiPeriodStrategy,
        **strategy_params
    ) -> Dict:
        """Führt den Backtest aus"""
        
        self.cerebro.addstrategy(strategy_class, **strategy_params)
        
        print("\n" + "="*60)
        print("BACKTEST START")
        print("="*60)
        print(f"Starting Cash: ${self.cerebro.broker.getcash():,.2f}")
        print(f"Strategy Params: {strategy_params}")
        print("-"*60)
        
        self.results = self.cerebro.run()
        
        final_value = self.cerebro.broker.getvalue()
        initial_cash = 100000
        total_return = (final_value - initial_cash) / initial_cash * 100
        
        print("-"*60)
        print(f"Final Portfolio Value: ${final_value:,.2f}")
        print(f"Total Return: {total_return:.2f}%")
        print(f"Net Profit: ${final_value - initial_cash:,.2f}")
        print("="*60)
        
        return {
            'final_value': final_value,
            'total_return': total_return,
            'initial_cash': initial_cash,
            'strategy': self.results[0]
        }

===================== BACKTEST AUSFÜHRUNG =====================

Multi-Period Backtest mit 3 Zeitrahmen

backtester = MultiPeriodBacktester(initial_cash=100000)

Daten müssen in der Reihenfolge: Daily, 4H, 1H hinzugefügt werden

(Backtrader verwendet die erste Datenquelle als Hauptzeitrahmen)

print("\n✓ Multi-Period Backtesting Engine bereit")

Vollständiger Workflow: Vom API-Abruf zum Backtest

"""
Kompletter Workflow: CoinAPI → Datenverarbeitung → Multi-Period Backtest
Benchmark-Vergleich: CoinAPI vs. HolySheep AI
"""

def run_complete_workflow():
    """
    Führt den kompletten Workflow aus:
    1. Daten von CoinAPI abrufen
    2. Daten für alle Zeitrahmen vorbereiten
    3. Multi-Period Backtest durchführen
    4. Ergebnisse analysieren
    """
    
    print("="*70)
    print("COINAPI + BACKTRADER MULTI-PERIOD BACKTEST WORKFLOW")
    print("="*70)
    
    # ===== SCHRITT 1: DATENABRUF =====
    print("\n[1/4] Rufe Daten von CoinAPI ab...")
    print("-"*70)
    
    # Konfiguration für verschiedene Zeitrahmen
    periods = {
        "daily": ("1DAY", datetime(2023, 1, 1), datetime(2024, 12, 31)),
        "4hour": ("4HRS", datetime(2024, 1, 1), datetime(2024, 12, 31)),
        "1hour": ("1HRS", datetime(2024, 6, 1), datetime(2024, 12, 31))
    }
    
    data_collection = {}
    
    for name, (period, start, end) in periods.items():
        df = fetcher.fetch_ohlcv(
            symbol_id=SUPPORTED_SYMBOLS["BTC"],
            period_id=period,
            start_time=start,
            end_time=end
        )
        data_collection[name] = df
    
    # ===== SCHRITT 2: DATENVALIDIERUNG =====
    print("\n[2/4] Validierung und Bereinigung der Daten...")
    print("-"*70)
    
    for name, df in data_collection.items():
        print(f"  {name}: {len(df)} Bars, "
              f"Zeitraum: {df.index.min().date()} bis {df.index.max().date()}")
        
        # Prüfe auf fehlende Daten
        null_count = df.isnull().sum().sum()
        if null_count > 0:
            print(f"    ⚠ {null_count} fehlende Werte gefunden, interpoliere...")
            df.fillna(method='ffill', inplace=True)
    
    # ===== SCHRITT 3: BACKTEST KONFIGURATION =====
    print("\n[3/4] Konfiguriere Multi-Period Backtest...")
    print("-"*70)
    
    backtester = MultiPeriodBacktester(initial_cash=50000)
    
    # Füge Datenfeeds in der richtigen Reihenfolge hinzu
    # Reihenfolge: Daily (Trend), 4H (Signal), 1H (Bestätigung)
    backtester.add_data_feed(data_collection["daily"], name="DAILY")
    backtester.add_data_feed(data_collection["4hour"], name="4H")
    backtester.add_data_feed(data_collection["1hour"], name="1H")
    
    # ===== SCHRITT 4: BACKTEST AUSFÜHRUNG =====
    print("\n[4/4] Führe Multi-Period Backtest aus...")
    print("-"*70)
    
    results = backtester.run_backtest(
        strategy_class=MultiPeriodStrategy,
        # Tagesebene
        rsi_period=14,
        rsi_oversold=30,
        rsi_overbought=70,
        # 4H-Ebene
        macd_fast=12,
        macd_slow=26,
        macd_signal=9,
        # 1H-Ebene
        volume_ma_period=20,
        volume_threshold=1.2,
        # Positionsmanagement
        stop_loss_pct=0.03,
        take_profit_pct=0.08,
        position_size=0.90
    )
    
    # ===== ERGEBNISANALYSE =====
    print("\n" + "="*70)
    print("BACKTEST ERGEBNISANALYSE")
    print("="*70)
    
    strategy = results['strategy']
    
    # Trade-Analyse
    total_trades = len(strategy.trade_log)
    if total_trades > 0:
        wins = sum(1 for t in strategy.trade_log if t['exit_reason'] == "TAKE_PROFIT")
        losses = sum(1 for t in strategy.trade_log if t['exit_reason'] == "STOP_LOSS")
        win_rate = wins / total_trades * 100 if total_trades > 0 else 0
        
        print(f"\n📊 TRADE-STATISTIK:")
        print(f"   Gesamte Trades: {total_trades}")
        print(f"   Gewinne (Take-Profit): {wins}")
        print(f"   Verluste (Stop-Loss): {losses}")
        print(f"   Win-Rate: {win_rate:.1f}%")
        
        # Durchschnittlicher Gewinn/Verlust
        profits = []
        for trade in strategy.trade_log:
            if trade['exit_price'] and trade['entry_price']:
                profit_pct = (trade['exit_price'] - trade['entry_price']) / trade['entry_price'] * 100
                profits.append(profit_pct)
        
        if profits:
            avg_profit = sum(p for p in profits if p > 0) / max(wins, 1)
            avg_loss = sum(abs(p) for p in profits if p < 0) / max(losses, 1)
            
            print(f"   Ø Gewinn: +{avg_profit:.2f}%")
            print(f"   Ø Verlust: -{avg_loss:.2f}%")
            print(f"   Profit-Faktor: {avg_profit/avg_loss:.2f}" if avg_loss > 0 else "   Profit-Faktor: ∞")
    
    print("\n" + "="*70)
    print("BENCHMARK VERGLEICH: COINAPI vs. HOLYSHEEP AI")
    print("="*70)
    
    benchmark_comparison = {
        "API-Latenz": {"CoinAPI": "45ms", "HolySheep AI": "<50ms"},
        "Monatliche Kosten": {"CoinAPI": "$79", "HolySheep AI": "$8-15"},
        "Free Tier": {"CoinAPI": "100 Anfragen/Tag", "HolySheep AI": "500 Credits"},
        "Zahlungsmethoden": {"CoinAPI": "Kreditkarte", "HolySheep AI": "WeChat/Alipay/Kreditkarte"},
        "Kryptowährungen": {"CoinAPI": "250+ Exchanges", "HolySheep AI": "Alle gängigen APIs"}
    }
    
    print("\n{:<25} {:<20} {:<20}".format(
        "Kriterium", "CoinAPI", "HolySheep AI"))
    print("-"*70)
    for kriterium, werte in benchmark_comparison.items():
        print("{:<25} {:<20} {:<20}".format(
            kriterium, werte["CoinAPI"], werte["HolySheep AI"]))
    
    return results

===== WORKFLOW STARTEN =====

if __name__ == "__main__": results = run_complete_workflow()

Häufige Fehler und Lösungen

1. Fehler: "404 Not Found" bei CoinAPI OHLCV-Endpunkt

Ursache: Falsche Symbol-ID oder abgelaufener API-Key.

# FEHLERHAFTER CODE (führt zu 404):
response = session.get(
    "https://rest.coinapi.io/v1/ohlcv/BTC_USD/history",  # FALSCH!
    params={"period_id": "1HRS"}
)

LÖSUNG - Symbol-Validierung:

def validate_symbol(api_key: str, symbol_id: str) -> bool: """Validiert Symbol-ID vor dem Abruf""" session = requests.Session() session.headers["X-CoinAPI-Key"] = api_key try: response = session.get( f"https://rest.coinapi.io/v1/symbols/{symbol_id}", timeout=10 ) if response.status_code == 404: print(f"⚠ Symbol '{symbol_id}' nicht gefunden!") # Liste gültiger Symbole abrufen list_response = session.get( "https://rest.coinapi.io/v1/symbols", params={"filter_symbol_id": "BITSTAMP"} ) valid_symbols = [s['symbol_id'] for s in list_response.json()[:10]] print(f" Gültige BITSTAMP-Symbole: {valid_symbols}") return False return response.status_code == 200 except requests.exceptions.RequestException as e: print(f"⚠ Validierungsfehler: {e}") return False

Verwendung

validate_symbol(COINAPI_API_KEY, "BITSTAMP_SPOT_BTC_USD") # ✓ Korrekt validate_symbol(COINAPI_API_KEY, "BTC_USD") # ✗ Führt zu 404

2. Fehler: Backtrader Multi-Timeframe Daten synchronisieren

Ursache: Datenfeeds haben unterschiedliche Zeiträume oder sind nicht ausgerichtet.

# FEHLERHAFTER CODE:
cerebro = Cerebro()
cerebro.adddata(daily_data, name="daily")
cerebro.adddata(hourly_data, name="hourly")  # Unterschiedliche Zeiträume!
cerebro.addstrategy(MultiPeriodStrategy)

→ Probleme bei self.data1, self.data2 Referenzen

LÖSUNG - Zeitliche Synchronisation:

class SynchronizedMultiTimeframeData: """Synchronisiert Datenfeeds auf gemeinsamen Zeitrahmen""" def __init__(self, primary_freq: str = "1H"): self.primary_freq = primary_freq self.dataframes = {} def add_data(self, name: str, df: pd.DataFrame, original_freq: str): """ Fügt Daten hinzu und synchronisiert sie Args: name: Name des Datenfeeds df: DataFrame mit OHLCV-Daten original_freq: Ursprünglicher Zeitrahmen ('1D', '4H', '1H') """ df = df.copy() # Resample auf Zielzeitrahmen if original_freq != self.primary_freq: freq_map = { "1D": "D", "4H": "4H", "1H": "H", "15Min": "15T", "5Min": "5T" } resampled = pd.DataFrame({ 'open': df['price_open'].resample(freq_map[self.primary_freq]).first(), 'high': df['price_high'].resample(freq_map[self.primary_freq]).max(), 'low': df['price_low'].resample(freq_map[self.primary_freq]).min(), 'close': df['price_close'].resample(freq_map[self.primary_freq]).last(), 'volume': df['volume_traded'].resample(freq_map[self.primary_freq]).sum() }) df = resampled.dropna() df.index.name = 'time' self.dataframes[name] = df return self def get_aligned_data(self) -> Dict[str, pd.DataFrame]: """Gibt synchronisierte DataFrames zurück""" # Finde gemeinsamen Zeitraum start_dates = [df.index.min() for df in self.dataframes.values()] end_dates = [df.index.max() for df in self.dataframes.values()] common_start = max(start_dates) common_end = min(end_dates) aligned = {} for name, df in self.dataframes.items(): aligned[name] = df.loc[common_start:common_end].copy() print(f"✓ {name}: {len(aligned[name])} synchronisierte Bars") return aligned

Verwendung:

sync_data = SynchronizedMultiTimeframeData(primary_freq="4H") sync_data.add_data("daily", daily_df, "1D") sync_data.add_data("4H", fourhour_df, "4H") sync_data.add_data("1H", hourly_df, "1H") aligned_data = sync_data.get_aligned_data()

→ Alle Datenfeeds jetzt auf dem gleichen Zeitraum

3. Fehler: API-Rate-Limiting und Session-Management

Ursache: Zu viele Anfragen in kurzer Zeit oder fehlende Session-Wiederverwendung.

# FEHLERHAFTER CODE:
for symbol in symbols:
    response = requests.get(url, headers={"X-CoinAPI-Key": key})  # Neue Verbindung!
    # → Rate-Limit erreicht nach ~100 Anfragen

LÖSUNG - Rate-Limited Session mit Retry:

import time from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry class RateLimitedSession: """Session mit automatischem Rate-Limiting und Retry""" def __init__(self, api_key: str, max_retries: int = 5, base_delay: float = 1.0): self.api_key = api_key self.base_delay = base_delay # HTTPAdapter mit Retry-Strategie retry_strategy = Retry( total=max_retries, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], allowed_methods=["GET"] ) adapter = HTTPAdapter(max_retries=retry_strategy) # Session mit verbesserter Verbindung self.session = requests.Session() self.session.mount("https://", adapter) self.session.mount("http://", adapter) self.session.headers.update({ "X-CoinAPI-Key": api_key, "Accept": "application/json" }) self.request_count = 0 self.last_request_time = 0 def get(self, url: str, **kwargs) -> requests.Response: """Rate-limited GET-Request""" # Rate-Limit: max 100 Anfragen/min = 1 Anfrage pro 0.6 Sekunden min_interval = 0.6 elapsed = time.time() - self.last_request_time if elapsed < min_interval: time.sleep(min_interval - elapsed) # Retry-Loop for attempt in range(3): try: response = self.session.get(url, timeout=30, **kwargs) self.last_request_time = time.time() self.request_count += 1 # Rate-Limit Header prüfen if response.status_code == 429: retry_after = int(response.headers.get('Retry-After', 60)) print(f"⚠ Rate-Limit erreicht. Warte {retry_after}s...") time.sleep(retry_after) continue response.raise_for_status() return response except requests.exceptions.RequestException as e: if attempt == 2: raise wait_time = self.base_delay * (2 ** attempt) print(f"⚠ Anfrage fehlgeschlagen, warte {wait_time}s...") time.sleep(wait_time) raise ConnectionError("Max retries exceeded")

Batch-Download mit Fortschrittsanzeige:

def batch_fetch_ohlcv(symbols: List[str], period_id: str) -> Dict[str, pd.DataFrame]: """Lädt Daten für mehrere Symbole mit Fortschrittsanzeige""" session = RateLimitedSession(COINAPI_API_KEY) results = {} total = len(symbols) print(f"\n📥 Lade {total} Symbole herunter...") for i, symbol_id in enumerate(symbols, 1): print(f"\r[{i}/{total}] {symbol_id}...", end="", flush=True) url = f"https://rest.coinapi.io/v1/ohlcv/{symbol_id}/history" params = { "period_id": period_id, "time_start": "2024-01-01T00:00:00", "limit": 10000 } try: response = session.get(url, params=params) data = response.json() if data: df = pd.DataFrame(data) df['time_period_start'] = pd.to_datetime(df['time_period_start']) df = df.set_index('time_period_start') results[symbol_id] = df.sort_index() print(f" ✓ {len(df)} Bars") else: print(f" ✗ Keine Daten") except Exception as e: print(f" ✗ Fehler: {e}") # Fortschritt speichern time.sleep(0.5) print(f"\n✓ Abgeschlossen: {len(results)}/{total} Symbole geladen") return results

Geeignet / nicht geeignet für

🔥 HolySheep AI ausprobieren

Direktes KI-API-Gateway. Claude, GPT-5, Gemini, DeepSeek — ein Schlüssel, kein VPN.

👉 Kostenlos registrieren →

Kriterium Geeignet Nicht geeignet
Trading-Stil