105 lines
3.5 KiB
Python
105 lines
3.5 KiB
Python
import os
|
|
import pandas as pd
|
|
import pandas_ta as ta
|
|
from dotenv import load_dotenv
|
|
from datetime import datetime, timedelta
|
|
|
|
# Backtesting-Spezialisten
|
|
from backtesting import Backtest, Strategy
|
|
from backtesting.lib import crossover
|
|
|
|
# Alpaca API
|
|
from alpaca.data.historical import StockHistoricalDataClient
|
|
from alpaca.data.requests import StockBarsRequest
|
|
from alpaca.data.timeframe import TimeFrame
|
|
|
|
# --- CONFIG ---
|
|
load_dotenv()
|
|
API_KEY = os.getenv('ALPACA_API_KEY')
|
|
SECRET_KEY = os.getenv('ALPACA_SECRET_KEY')
|
|
|
|
# --- KLASSE: DATEN-ENGINE ---
|
|
class DataEngine:
|
|
@staticmethod
|
|
def get_alpaca_data(symbol, days=365):
|
|
client = StockHistoricalDataClient(API_KEY, SECRET_KEY)
|
|
start_date = datetime.now() - timedelta(days=days)
|
|
|
|
request = StockBarsRequest(
|
|
symbol_or_symbols=[symbol],
|
|
timeframe=TimeFrame.Day,
|
|
start=start_date
|
|
)
|
|
|
|
df = client.get_stock_bars(request).df
|
|
df = df.reset_index(level=0, drop=True)
|
|
|
|
# Formatierung für Backtesting.py (Spalten müssen groß geschrieben sein)
|
|
df.columns = [c.capitalize() for c in df.columns]
|
|
# Zeitzonen entfernen (wichtig für Excel!)
|
|
df.index = df.index.tz_localize(None)
|
|
return df
|
|
|
|
# --- KLASSE: STRATEGIE (RSI) ---
|
|
class MyRsiStrategy(Strategy):
|
|
# Parameter - diese können später optimiert werden
|
|
rsi_period = 25
|
|
rsi_low = 30
|
|
rsi_high = 70
|
|
|
|
def init(self):
|
|
# Indikator berechnen (self.I stellt sicher, dass er im Chart erscheint)
|
|
self.rsi = self.I(ta.rsi, pd.Series(self.data.Close), length=self.rsi_period)
|
|
|
|
def next(self):
|
|
# KAUFEN: Wenn RSI die untere Grenze von unten nach oben kreuzt
|
|
if crossover(self.rsi, self.rsi_low):
|
|
self.buy()
|
|
|
|
# VERKAUFEN: Wenn RSI die obere Grenze von oben nach unten kreuzt
|
|
elif crossover(self.rsi_high, self.rsi):
|
|
if self.position:
|
|
self.position.close()
|
|
|
|
# --- HAUPTPROGRAMM ---
|
|
def run_backtest(symbol="AAPL", cash=10000, commission=0.002):
|
|
# 1. Daten laden
|
|
print(f"Lade Daten für {symbol}...")
|
|
data = DataEngine.get_alpaca_data(symbol)
|
|
|
|
# 2. Backtest initialisieren
|
|
# commission=0.002 bedeutet 0.2% Gebühren pro Trade
|
|
bt = Backtest(data, MyRsiStrategy, cash=cash, commission=commission)
|
|
|
|
# 3. Backtest ausführen
|
|
stats = bt.run()
|
|
|
|
# --- AUSWERTUNG ---
|
|
print("\n" + "="*30)
|
|
print(f"BACKTEST ERGEBNISSE FÜR {symbol}")
|
|
print("="*30)
|
|
print(f"Startkapital: {cash}$")
|
|
print(f"Endkapital: {stats['Equity Final [$]']:.2f}$")
|
|
print(f"Rendite [%]: {stats['Return [%]']:.2f}%")
|
|
print(f"Max. Drawdown: {stats['Max. Drawdown [%]']:.2f}%")
|
|
print(f"Anzahl Trades: {stats['# Trades']}")
|
|
print(f"Win Rate [%]: {stats['Win Rate [%]']:.2f}%")
|
|
print("="*30)
|
|
|
|
# 4. EXPORT: Trades nach Excel
|
|
trades = stats['_trades']
|
|
if not trades.empty:
|
|
# Dauer der Trades berechnen
|
|
trades['Duration'] = trades['ExitTime'] - trades['EntryTime']
|
|
excel_name = f"Backtest_Trades_{symbol}.xlsx"
|
|
trades.to_excel(excel_name)
|
|
print(f"✅ Excel-Liste gespeichert: {excel_name}")
|
|
|
|
# 5. EXPORT: Interaktiver HTML Report
|
|
report_name = f"Backtest_Report_{symbol}.html"
|
|
bt.plot(filename=report_name, open_browser=False)
|
|
print(f"✅ Interaktiver Chart gespeichert: {report_name}")
|
|
|
|
if __name__ == "__main__":
|
|
# Starte den Backtest
|
|
run_backtest(symbol="AAPL", cash=10000) |