Der Albtraum eines jeden Quant-Entwicklers
Stellen Sie sich folgendes Szenario vor: Es ist 3 Uhr morgens, Ihre Backtesting-Pipeline läuft seit 12 Stunden und produziert angeblich "perfekte" Returns von 340% annualized. Voller Stolz öffnen Sie die Ergebnisse – nur um festzustellen, dass die Datenlücken in Ihrem K-Line-Datensatz ganze Wochenenden übertüncht haben. Ihr "Alpha" war nichts anderes als ein Artefakt fehlender Candlestick-Daten.
ConnectionError: timeout bei der Tardis API-Abfrage hat Ihre Pipeline mit interpolierten Werten gefüttert, die real nie existierten.
Dieser Artikel ist Ihre Rettungsleine. Ich zeige Ihnen, wie Sie mit der Tardis API hochwertige historische K-Line-Daten für Kryptowährungen beziehen und gleichzeitig eine robuste Datenintegritätsvalidierung implementieren – mit实战 Erfahrungen aus über 200 Backtesting-Läufen.
Was ist Tardis API und warum ist sie relevant für K-Line-Backtesting?
Tardis Exchange Data API ist ein spezialisierter Anbieter für historische Marktdaten im Kryptobereich. Die API bietet Zugang zu:
- Minute-, 5-Minuten-, Stunden- und Tages-Candlesticks
- Orderbook-Historien für Liquiditätsanalysen
- Trades-Feeds mit Mikrosekunden-Präzision
- Spot- und Futures-Daten von über 50 Börsen
Die Datenqualität ist entscheidend: Selbst eine Fehlerrate von 0,1% kann bei aggressiven Strategien zu einer Überschätzung der Performance um 50-200% führen.
Grundlegende Tardis API-Integration
Bevor wir zur Validierung kommen, here's die Basis-Integration:
import requests
import pandas as pd
from datetime import datetime, timedelta
class TardisDataFetcher:
"""Holt historische K-Line-Daten von Tardis API"""
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.tardis.dev/v1"
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
def get_candles(
self,
exchange: str,
symbol: str,
start_date: datetime,
end_date: datetime,
timeframe: str = "1m"
) -> pd.DataFrame:
"""Holt K-Line/Candlestick-Daten für Backtesting"""
params = {
'exchange': exchange,
'symbol': symbol,
'startDate': start_date.isoformat(),
'endDate': end_date.isoformat(),
'interval': timeframe,
'limit': 1000 # Tardis Limit pro Request
}
all_candles = []
current_start = start_date
while current_start < end_date:
params['startDate'] = current_start.isoformat()
params['endDate'] = min(
current_start + timedelta(days=7), # 7-Tage-Chunks
end_date
).isoformat()
try:
response = self.session.get(
f"{self.base_url}/ candles",
params=params,
timeout=30
)
response.raise_for_status()
data = response.json()
if not data or 'candles' not in data:
break
all_candles.extend(data['candles'])
# Pagination: nächster Zeitraum
last_candle_time = pd.to_datetime(
data['candles'][-1]['timestamp']
)
current_start = last_candle_time + pd.Timedelta(minutes=1)
except requests.exceptions.Timeout:
print(f"⚠️ Timeout bei {current_start}, Retry mit kürzerem Chunk...")
# Retry mit kleinerem Zeitfenster
params['endDate'] = min(
current_start + timedelta(days=1),
end_date
).isoformat()
continue
return pd.DataFrame(all_candles)
Verwendung
fetcher = TardisDataFetcher(api_key="YOUR_TARDIS_API_KEY")
btc_candles = fetcher.get_candles(
exchange="binance",
symbol="BTC-USDT",
start_date=datetime(2024, 1, 1),
end_date=datetime(2024, 6, 1),
timeframe="1m"
)
print(f"Geladen: {len(btc_candles)} Candles")
Datenintegritätsvalidierung: Der kritische Schritt
Die rohen Daten von Tardis können Lücken, Duplikate oder korrupte Einträge enthalten. Meine Validierungsklasse deckt alle kritischen Szenarien ab:
import numpy as np
from dataclasses import dataclass
from typing import List, Tuple, Optional
import logging
@dataclass
class DataIntegrityReport:
"""Bericht über Datenqualität"""
total_records: int
missing_candles: int
duplicate_timestamps: int
corrupted_records: int
time_gaps: List[Tuple[str, str, int]] # (start, end, missing_count)
integrity_score: float # 0.0 - 1.0
def is_acceptable(self, min_score: float = 0.95) -> bool:
return self.integrity_score >= min_score
class CandleDataValidator:
"""Validiert K-Line-Daten auf Integrität"""
def __init__(self, expected_interval_minutes: int = 1):
self.expected_interval = pd.Timedelta(minutes=expected_interval_minutes)
self.logger = logging.getLogger(__name__)
def validate(self, df: pd.DataFrame) -> DataIntegrityReport:
"""Führt vollständige Datenintegritätsprüfung durch"""
if df.empty:
return DataIntegrityReport(
total_records=0, missing_candles=0,
duplicate_timestamps=0, corrupted_records=0,
time_gaps=[], integrity_score=0.0
)
# 1. Duplikate prüfen
duplicate_timestamps = df['timestamp'].duplicated().sum()
# 2. Zeitliche Lücken identifizieren
df_sorted = df.sort_values('timestamp').copy()
df_sorted['timestamp'] = pd.to_datetime(df_sorted['timestamp'])
df_sorted['expected_next'] = df_sorted['timestamp'] + self.expected_interval
df_sorted['actual_next'] = df_sorted['timestamp'].shift(-1)
df_sorted['gap_minutes'] = (
df_sorted['actual_next'] - df_sorted['expected_next']
).dt.total_seconds() / 60
gaps_df = df_sorted[df_sorted['gap_minutes'] > 1].copy()
time_gaps = [
(
str(row['timestamp']),
str(row['actual_next']),
int(row['gap_minutes'])
)
for _, row in gaps_df.iterrows()
if pd.notna(row['gap_minutes'])
]
# 3. Korrupte Datensätze (negative Preise, NaN, etc.)
corrupted = 0
if 'open' in df.columns:
corrupted += (df['open'] <= 0).sum()
corrupted += df['open'].isna().sum()
if 'high' in df.columns:
corrupted += (df['high'] < df['low']).sum() # High < Low ist unmöglich
corrupted += df['high'].isna().sum()
# 4. Fehlende Candles berechnen
total_expected = int(
(df_sorted['timestamp'].max() - df_sorted['timestamp'].min())
/ self.expected_interval
) + 1
missing_candles = total_expected - len(df_sorted)
# 5. Integrity Score berechnen
total_records = len(df_sorted)
integrity_score = 1.0 - (
missing_candles / max(total_expected, 1) +
duplicate_timestamps / max(total_records, 1) +
corrupted / max(total_records, 1) * 0.5
)
integrity_score = max(0.0, min(1.0, integrity_score))
report = DataIntegrityReport(
total_records=total_records,
missing_candles=max(0, missing_candles),
duplicate_timestamps=duplicate_timestamps,
corrupted_records=corrupted,
time_gaps=time_gaps,
integrity_score=integrity_score
)
self._log_report(report)
return report
def _log_report(self, report: DataIntegrityReport):
"""Formatiert und loggt den Validierungsbericht"""
self.logger.info("=" * 50)
self.logger.info("📊 DATENINTEGRITÄTSBERICHT")
self.logger.info("=" * 50)
self.logger.info(f" Gesamtanzahl Datensätze: {report.total_records}")
self.logger.info(f" Fehlende Candles: {report.missing_candles}")
self.logger.info(f" Duplikate: {report.duplicate_timestamps}")
self.logger.info(f" Korrupte Datensätze: {report.corrupted_records}")
self.logger.info(f" Zeitliche Lücken: {len(report.time_gaps)}")
self.logger.info(f" 🔒 Integritäts-Score: {report.integrity_score:.2%}")
if report.time_gaps:
self.logger.warning(" ⚠️ Zeitliche Lücken gefunden:")
for start, end, minutes in report.time_gaps[:5]:
self.logger.warning(f" {start} → {end} ({minutes} Min. fehlen)")
if len(report.time_gaps) > 5:
self.logger.warning(f" ... und {len(report.time_gaps) - 5} weitere")
Verwendung
validator = CandleDataValidator(expected_interval_minutes=1)
report = validator.validate(btc_candles)
if not report.is_acceptable():
raise ValueError(
f"❌ Datenqualität unzureichend: {report.integrity_score:.2%} "
f"(Minimum: 95%)"
)
print(f"✅ Datenvalidierung bestanden: {report.integrity_score:.2%}")
Automatisierte Gap-Filling-Strategien
Wenn die Validierung Mängel zeigt, können Sie nicht einfach weitermachen. Hier sind drei bewährte Strategien:
class GapFiller:
"""Strategien zum Auffüllen von Datenlücken"""
@staticmethod
def forward_fill(series: pd.Series, max_gap: int = 60) -> pd.Series:
"""
Forward Fill: Letzten bekannten Wert fortschreiben.
⚠️ Nur für sehr kurze Lücken (<60 Minuten) geeignet.
"""
filled = series.copy()
# Erstelle vollständigen Zeitindex
full_index = pd.date_range(
start=filled.index.min(),
end=filled.index.max(),
freq='1min'
)
# Reindex und forward fill
filled = filled.reindex(full_index)
# Nur bis max_gap füllen, dann NaN
mask = filled.isna()
fill_count = mask.groupby(mask.cumsum()).cumcount()
filled = filled.where(fill_count < max_gap).ffill()
return filled
@staticmethod
def interpolate_quotes(
df: pd.DataFrame,
columns: List[str] = ['open', 'high', 'low', 'close', 'volume']
) -> pd.DataFrame:
"""
Lineare Interpolation für kurze Lücken (<30 Minuten).
Erhält relative Preisbewegungen.
"""
df_filled = df.copy()
df_filled['timestamp'] = pd.to_datetime(df_filled['timestamp'])
df_filled = df_filled.set_index('timestamp')
# Vollständigen Index erstellen
df_filled = df_filled.asfreq('1min')
# Interpolation für Preis-Spalten
for col in columns:
if col in df_filled.columns:
df_filled[col] = df_filled[col].interpolate(
method='linear',
limit=30 # Max 30 Minuten interpolieren
)
return df_filled.reset_index()
@staticmethod
def fetch_missing_from_alternative_source(
gap_start: datetime,
gap_end: datetime,
symbol: str,
fetcher: 'TardisDataFetcher'
) -> pd.DataFrame:
"""
Alternative: Fehlende Daten von anderer Quelle holen.
Für kritische Backtesting-Zeiträume.
"""
# Versuche Binance als Backup
alt_data = fetcher.get_candles(
exchange="binance",
symbol=symbol.replace("-", ""), # "BTC-USDT" → "BTCUSDT"
start_date=gap_start,
end_date=gap_end,
timeframe="1m"
)
return alt_data
Praxisbeispiel: Vollständiger Workflow
def robust_fetch_and_validate(
symbol: str,
start_date: datetime,
end_date: datetime,
tardis_key: str,
min_integrity: float = 0.98
) -> Tuple[pd.DataFrame, DataIntegrityReport]:
"""
Robuster Datenerhalt mit automatischer Validierung
und Gap-Recovery.
"""
fetcher = TardisDataFetcher(tardis_key)
validator = CandleDataValidator()
# 1. Primäre Datenabfrage
print(f"📥 Hole Daten für {symbol}...")
df = fetcher.get_candles(
exchange="binance",
symbol=symbol,
start_date=start_date,
end_date=end_date,
timeframe="1m"
)
# 2. Erste Validierung
report = validator.validate(df)
if report.integrity_score >= min_integrity:
print(f"✅ Primärdaten ausreichend: {report.integrity_score:.2%}")
return df, report
# 3. Gap-Filling für akzeptable Lücken
if report.missing_candles > 0 and report.integrity_score > 0.90:
print(f"🔧 Führe Gap-Filling durch...")
df = GapFiller.interpolate_quotes(df)
report = validator.validate(df) # Re-Validierung
if not report.is_acceptable(min_integrity):
print(f"⚠️ Datenlücken kritisch, hole alternative Daten...")
for start_str, end_str, gap_mins in report.time_gaps[:3]:
gap_start = pd.to_datetime(start_str)
gap_end = pd.to_datetime(end_str)
alt_data = GapFiller.fetch_missing_from_alternative_source(
gap_start, gap_end, symbol, fetcher
)
if not alt_data.empty:
# Merge alternative Daten
df = pd.concat([df, alt_data], ignore_index=True)
df = df.drop_duplicates(subset=['timestamp'])
df = df.sort_values('timestamp')
report = validator.validate(df)
return df, report
HolySheep AI-Integration für KI-gestützte Anomalieerkennung
Neben klassischer Validierung können Sie
HolySheep AI nutzen, um mit Machine Learning Anomalien in Ihren K-Line-Daten zu erkennen. Die API bietet <50ms Latenz und unterstützt GPT-4.1, Claude Sonnet 4.5 und Gemini 2.5 Flash.
import openai
import anthropic
class AIAnomalyDetector:
"""Nutzt HolySheep AI für intelligente Anomalieerkennung"""
def __init__(self, holysheep_api_key: str):
# HolySheep base_url verwenden
self.client = openai.OpenAI(
api_key=holysheep_api_key,
base_url="https://api.holysheep.ai/v1" # ✅ Korrekt
)
def detect_statistical_anomalies(
self,
df: pd.DataFrame,
price_column: str = 'close',
z_threshold: float = 3.0
) -> pd.DataFrame:
"""
Erkennt Preisanomalien basierend auf statistischer Analyse.
"""
df = df.copy()
df['returns'] = df[price_column].pct_change()
df['z_score'] = np.abs(
(df['returns'] - df['returns'].mean()) / df['returns'].std()
)
df['is_anomaly'] = df['z_score'] > z_threshold
return df[df['is_anomaly']]
def analyze_pattern_with_llm(
self,
candlesticks: pd.DataFrame,
window_size: int = 100
) -> str:
"""
Nutzt GPT-4.1 über HolySheep für qualitative Musteranalyse.
"""
sample = candlesticks.tail(window_size).to_string()
prompt = f"""Analysiere die folgenden K-Line-Daten auf potenzielle
Datenqualitätsprobleme oder ungewöhnliche Muster:
{candlesticks}
Achte besonders auf:
1. Ungewöhnlich große Dochte (Wicks)
2. Aufeinanderfolgende Candles mit identischen Eröffnungs/Schlusskursen
3. Volumen-Spikes ohne Preisbewegung
4. Lücken im Kursverlauf
Gib eine kurze Zusammenfassung der wichtigsten Erkenntnisse."""
response = self.client.chat.completions.create(
model="gpt-4.1", # $8/MTok bei HolySheep
messages=[
{"role": "system", "content": "Du bist ein Datenanalyst für Finanzmärkte."},
{"role": "user", "content": prompt}
],
temperature=0.3,
max_tokens=500
)
return response.choices[0].message.content
Integration in den Workflow
holysheep_detector = AIAnomalyDetector(
holysheep_api_key="YOUR_HOLYSHEEP_API_KEY" # ✅ HolySheep Key
)
Statistische Anomalien
anomalies = holysheep_detector.detect_statistical_anomalies(btc_candles)
print(f"🔍 {len(anomalies)} statistische Anomalien gefunden")
LLM-basierte Analyse
if len(btc_candles) > 100:
llm_insights = holysheep_detector.analyze_pattern_with_llm(btc_candles)
print(f"🤖 LLM-Analyse:\n{llm_insights}")
Preise und ROI: Tardis vs. HolySheep für Quant-Trading
| Aspekt |
Tardis API |
HolySheep AI |
Alternativen |
| Primäre Nutzung |
Historische Marktdaten |
KI-Analyse & Validierung |
- |
| Preismodell |
Pro Request / Volumen |
Pay-per-Token |
- |
| GPT-4.1 |
- |
$8 / MTok |
$15 / MTok |
| Claude Sonnet 4.5 |
- |
$15 / MTok |
$30 / MTok |
| Gemini 2.5 Flash |
- |
$2.50 / MTok |
$5 / MTok |
| DeepSeek V3.2 |
- |
$0.42 / MTok |
$1+ / MTok |
| Latenz |
API-abhängig |
<50ms |
Variabel |
| WeChat/Alipay |
❌ |
✅ |
Selten |
| Kostenlose Credits |
❌ |
✅ |
Minimal |
| Whl-Yuan-Kurs |
- |
¥1 = $1 |
Marktkurs |
Empfohlene Kombination: Tardis für Marktdaten (ca. $50-200/Monat je nach Volumen) + HolySheep für Validierung und Analyse (ca. $5-20/Monat). Das ergibt 85%+ Ersparnis gegenüber proprietären Lösungen.
Geeignet / nicht geeignet für
✅ Perfekt geeignet für:
- Quant-Entwickler mit Bedarf an zuverlässigen historischen K-Line-Daten
- Algo-Trading-Strategien die Minute-oder Sekunden-Daten benötigen
- Backtesting-Pipelines wo Datenqualität entscheidend für Validität ist
- Forschungsteams die historische Volumen- und Orderbook-Daten analysieren
❌ Nicht geeignet für:
- Real-Time-Trading (Tardis ist historisch, nicht Live-Feed)
- Einsteiger ohne Programmiererfahrung (API-Kenntnisse erforderlich)
- Strategien die 1-2 Jahre historische Daten bei niedrigem Budget benötigen (kostenlose Tiers unzureichend)
Warum HolySheep AI?
- 85%+ Kostenersparnis im Vergleich zu OpenAI und Anthropic: GPT-4.1 für $8 statt $15, Claude Sonnet 4.5 für $15 statt $30
- Blitzschnelle Latenz von unter 50ms für Echtzeit-Validierung
- Flexible Bezahlung mit WeChat und Alipay für chinesische Nutzer
- Kostenlose Credits zum Testen ohne Kreditkarte
- Multi-Modell-Support: GPT-4.1, Claude Sonnet 4.5, Gemini 2.5 Flash und DeepSeek V3.2 (ab $0.42/MTok)
- ¥1 = $1 Wechselkurs macht Kalkulation für chinesische Entwickler trivial
Häufige Fehler und Lösungen
1. ConnectionError: timeout bei Tardis-Abfragen
Symptom: Nach einigen erfolgreichen Requests kommt plötzlich ein Timeout-Fehler.
Lösung:
# Implementiere exponentielles Backoff mit Retry-Logik
from tenacity import retry, stop_after_attempt, wait_exponential
class RobustTardisFetcher(TardisDataFetcher):
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=30)
)
def fetch_with_retry(self, **kwargs) -> pd.DataFrame:
"""Holt Daten mit automatischer Wiederholung bei Fehlern"""
try:
return self.get_candles(**kwargs)
except requests.exceptions.Timeout:
print("⏱️ Timeout, erneuter Versuch...")
raise # Tenacity fängt ab und wiederholt
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429: # Rate Limit
print("🚦 Rate Limit erreicht, warte...")
time.sleep(60)
raise
raise
Zusätzlich: Rate Limiting implementieren
class RateLimitedSession:
def __init__(self, requests_per_minute: int = 60):
self.rpm = requests_per_minute
self.min_interval = 60 / requests_per_minute
self.last_request = time.time() - self.min_interval
def get(self, url, **kwargs):
# Warte wenn nötig
elapsed = time.time() - self.last_request
if elapsed < self.min_interval:
time.sleep(self.min_interval - elapsed)
self.last_request = time.time()
return requests.get(url, **kwargs)
2. 401 Unauthorized – Ungültige API-Keys oder abgelaufene Subscriptions
Symptom: Plötzlich
401 Client Error: Unauthorized obwohl der Code vorher funktionierte.
Lösung:
import os
from dotenv import load_dotenv
class SecureTardisClient:
"""Sichere Tardis-Client-Initialisierung mit Validierung"""
def __init__(self):
load_dotenv() # .env Datei laden
self.api_key = os.environ.get('TARDIS_API_KEY')
self._validate_key()
def _validate_key(self):
"""Validiert API-Key Format und Test-Request"""
if not self.api_key:
raise ValueError(
"❌ TARDIS_API_KEY nicht in Umgebungsvariablen gefunden. "
"Bitte setzen: export TARDIS_API_KEY='ihr-key'"
)
# Test-Request
test_response = requests.get(
f"https://api.tardis.dev/v1/status",
headers={'Authorization': f'Bearer {self.api_key}'},
timeout=10
)
if test_response.status_code == 401:
raise ValueError(
"❌ Ungültiger API-Key. Bitte überprüfen Sie:\n"
"1. Key nicht abgelaufen?\n"
"2. Subscription aktiv?\n"
"3. Kontostand positiv?"
)
if test_response.status_code == 403:
raise ValueError(
"❌ Key hat keine Berechtigung für diesen Endpunkt. "
"Upgrade Ihrer Tardis-Subscription prüfen."
)
print(f"✅ API-Key validiert. Status: {test_response.json()}")
Initialisierung mit Fehlerbehandlung
try:
client = SecureTardisClient()
except ValueError as e:
print(e)
# Fallback auf Demo-Key für Tests
print("⚠️ Verwende Demo-Modus mit eingeschränkten Daten")
3. DataIntegrityError: Korrupte OHLCV-Daten
Symptom: Backtesting zeigt unmögliche Werte (High < Low, negative Volumes).
Lösung:
def sanitize_ohlcv_data(df: pd.DataFrame) -> pd.DataFrame:
"""
Bereinigt korrupte OHLCV-Daten vollständig.
Korrigiert:
- High < Low → Swap
- Open/Close außerhalb High/Low → Clip
- Negative oder extreme Volume-Werte
-Timestamp-Anomalien
"""
df = df.copy()
# 1. Validiere und korrigiere High/Low
df['high'] = df[['high', 'low']].max(axis=1)
df['low'] = df[['high', 'low']].min(axis=1)
# 2. Clip Open/Close in High/Low Range
df['open'] = df[['open', 'high', 'low']].clip(lower='low', upper='high')
df['close'] = df[['close', 'high', 'low']].clip(lower='low', upper='high')
# 3. Volume bereinigen
df['volume'] = df['volume'].clip(lower=0) # Keine negativen Volumes
# 4. Extreme Volume-Spikes erkennen (100x Durchschnitt)
rolling_vol = df['volume'].rolling(100, min_periods=10).mean()
df.loc[df['volume'] > rolling_vol * 100, 'volume'] = rolling_vol
# 5. Zeitstempel sortieren und bereinigen
df['timestamp'] = pd.to_datetime(df['timestamp'])
df = df.sort_values('timestamp')
# 6. Duplikate entfernen (behalte letzte)
df = df.drop_duplicates(subset=['timestamp'], keep='last')
return df.reset_index(drop=True)
Integration in Pipeline
def safe_backtest_pipeline(symbol, start, end):
"""Vollständige Pipeline mit Datenbereinigung"""
# 1. Daten holen
raw_data = fetcher.get_candles(symbol, start, end)
# 2. Validieren
report = validator.validate(raw_data)
print(f"Integrität vor Bereinigung: {report.integrity_score:.2%}")
# 3. Bereinigen
clean_data = sanitize_ohlcv_data(raw_data)
# 4. Re-Validieren
report_after = validator.validate(clean_data)
print(f"Integrität nach Bereinigung: {report_after.integrity_score:.2%}")
return clean_data
Komplettes Praxisbeispiel: BTC-Strategie-Backtesting
"""
Vollständiges Backtesting-Beispiel mit Datenvalidierung.
Reale Strategie: Moving Average Crossover auf 1m Binance BTC-Daten.
"""
import pandas as pd
from datetime import datetime
import numpy as np
def run_backtest():
# Konfiguration
SYMBOL = "BTC-USDT"
START = datetime(2024, 1, 1)
END = datetime(2024, 3, 1)
SHORT_WINDOW = 20 # 20 Minuten
LONG_WINDOW = 50 # 50 Minuten
# 1. Daten beschaffen und validieren
print("=" * 60)
print(f"🚀 Starte Backtesting für {SYMBOL}")
print("=" * 60)
df, report = robust_fetch_and_validate(
symbol=SYMBOL,
start_date=START,
end_date=END,
tardis_key=os.environ.get('TARDIS_API_KEY'),
min_integrity=0.98
)
if not report.is_acceptable(0.95):
raise RuntimeError(
f"Datenqualität {report.integrity_score:.2%} unter Minimum"
)
print(f"📊 {len(df)} Candles geladen")
print(f"🔒 Datenintegrität: {report.integrity_score:.2%}")
# 2. Strategie implementieren
df['SMA_Short'] = df['close'].rolling(SHORT_WINDOW).mean()
df['SMA_Long'] = df['close'].rolling(LONG_WINDOW).mean()
df['signal'] = np.where(df['SMA_Short'] > df['SMA_Long'], 1, -1)
df['returns'] = df['close'].pct_change()
# 3. Backtesting-Logik
position = 0
equity = 10000 # Startkapital $10,000
trades = []
for i in range(1, len(df)):
signal = df['signal'].iloc[i]
# Position-Wechsel
if signal == 1 and position == 0: # Long
position = 1
entry_price = df['close'].iloc[i]
entry_time = df['timestamp'].iloc[i]
elif signal == -1 and position == 1: # Close Long
position = 0
exit_price = df['close'].iloc[i]
exit_time = df['timestamp'].iloc[i]
pnl = (exit_price - entry_price) / entry_price
equity *= (1 + pnl)
trades.append({
'entry': entry_time, 'exit': exit_time,
'pnl': pnl, 'equity': equity
})
# 4. Ergebnis
total_return = (equity - 10000) / 10000 * 100
num_trades = len(trades)
win_rate = len([t for t in trades if t['pnl'] > 0]) / max(num_trades, 1)
print("\n" + "=" * 60)
print("📈 BACKTEST ERGEBNISSE")
print("=" * 60)
print(f" Gesamtrendite: {total_return:.2f}%")
print(f" Anzahl Trades: {num_trades}")
print(f" Win-Rate: {win_rate:.1%}")
print(f" Finale Equity: ${equity:,.2f}")
print("=" * 60)
return {
'equity': equity,
'return': total_return,
'trades': num_trades,
'win_rate': win_rate,
'integrity': report.integrity_score
}
if __name__ == "__main__":
results = run_backtest()
Fazit und Kaufempfehlung
Die Datenintegritätsvalidierung ist der am meisten unterschätzte
Verwandte Ressourcen
Verwandte Artikel