First Commit

This commit is contained in:
hwinkel
2026-01-23 15:39:50 +01:00
parent 9b1a3ec147
commit da68106666
10 changed files with 533 additions and 0 deletions

61
Backtest_Report_AAPL.html Normal file

File diff suppressed because one or more lines are too long

61
Backtest_Report_TSLA.html Normal file

File diff suppressed because one or more lines are too long

BIN
Backtest_Trades_AAPL.xlsx Normal file

Binary file not shown.

BIN
Backtest_Trades_TSLA.xlsx Normal file

Binary file not shown.

105
backtester.py Normal file
View File

@@ -0,0 +1,105 @@
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)

83
checkStocks.py Normal file
View File

@@ -0,0 +1,83 @@
import os
import pandas as pd
import pandas_ta as ta
from dotenv import load_dotenv
from datetime import datetime, timedelta
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame
# --- SETUP ---
load_dotenv()
API_KEY = os.getenv('ALPACA_API_KEY')
SECRET_KEY = os.getenv('ALPACA_SECRET_KEY')
data_client = StockHistoricalDataClient(API_KEY, SECRET_KEY)
def scan_markets(tickers, rsi_period=25):
results = []
print(f"--- Scanne {len(tickers)} Aktien auf Signale ---")
for symbol in tickers:
try:
# Daten für die letzten 30 Tage holen (reicht für RSI 14)
start_date = datetime.now() - timedelta(days=50)
request_params = StockBarsRequest(
symbol_or_symbols=[symbol],
timeframe=TimeFrame.Day,
start=start_date
)
bars = data_client.get_stock_bars(request_params).df
if bars.empty: continue
df = bars.reset_index(level=0, drop=True)
# RSI berechnen
df['RSI'] = ta.rsi(df['close'], length=rsi_period)
# Letzte Werte extrahieren
current_price = df['close'].iloc[-1]
current_rsi = df['RSI'].iloc[-1]
# Signal-Logik
signal = "NEUTRAL"
if current_rsi < 30:
signal = "KAUFEN (Überverkauft)"
elif current_rsi > 70:
signal = "VERKAUFEN (Überkauft)"
results.append({
'Symbol': symbol,
'Preis': round(current_price, 2),
'RSI': round(current_rsi, 2),
'Signal': signal,
'Zeitpunkt': datetime.now().strftime('%Y-%m-%d %H:%M')
})
print(f"{symbol}: RSI {current_rsi:.2f} -> {signal}")
except Exception as e:
print(f"Fehler beim Scannen von {symbol}: {e}")
# Ergebnisse in DataFrame umwandeln
scan_df = pd.DataFrame(results)
# Als Excel speichern
filename = f"markt_scan_{datetime.now().strftime('%Y%m%d')}.xlsx"
scan_df.to_excel(filename, index=False)
print(f"\n✅ Scan abgeschlossen. Ergebnisse gespeichert in: {filename}")
return scan_df
if __name__ == "__main__":
# Liste der zu scannenden Aktien
my_watchlist = ["AAPL", "TSLA", "MSFT", "AMZN", "GOOGL", "NVDA", "AMD", "META"]
scan_results = scan_markets(my_watchlist)
# Optional: Nur die echten Signale anzeigen
hits = scan_results[scan_results['Signal'] != "NEUTRAL"]
if not hits.empty:
print("\n🔥 AKTUELLE SIGNALE:")
print(hits)
else:
print("\nKeine extremen RSI-Werte gefunden.")

89
live_bot.py Normal file
View File

@@ -0,0 +1,89 @@
import os
import time
import pandas as pd
import pandas_ta as ta
from dotenv import load_dotenv
from datetime import datetime, timedelta, timezone
from colorama import Fore, Style, init
from alpaca.trading.client import TradingClient
from alpaca.trading.requests import MarketOrderRequest, StopLossRequest, TakeProfitRequest
from alpaca.trading.enums import OrderSide, TimeInForce, OrderClass
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame
init(autoreset=True)
load_dotenv()
# --- PARAMETER ---
SYMBOL = "AAPL"
QTY_PER_TRADE = 10 # Anzahl der Aktien
RSI_LOWER = 30
RSI_UPPER = 70
STOP_LOSS_PCT = 0.05 # 5% Stop Loss
TAKE_PROFIT_PCT = 0.10 # 10% Take Profit (Optionales Ziel)
# Clients
trading_client = TradingClient(os.getenv('ALPACA_API_KEY'), os.getenv('ALPACA_SECRET_KEY'), paper=True)
data_client = StockHistoricalDataClient(os.getenv('ALPACA_API_KEY'), os.getenv('ALPACA_SECRET_KEY'))
def get_rsi():
try:
start_time = datetime.now(timezone.utc) - timedelta(days=50)
bars = data_client.get_stock_bars(StockBarsRequest(symbol_or_symbols=[SYMBOL], timeframe=TimeFrame.Hour, start=start_time))
df = bars.df.xs(SYMBOL)
rsi = ta.rsi(df['close'], length=14)
return rsi.iloc[-1], df['close'].iloc[-1]
except:
return None, None
def execute_trade(side, price):
"""Sendet eine automatisierte Order mit Stop-Loss an Alpaca"""
if side == "BUY":
# Berechnung der Stop-Loss und Take-Profit Preise
sl_price = price * (1 - STOP_LOSS_PCT)
tp_price = price * (1 + TAKE_PROFIT_PCT)
# Erstellung einer "Bracket Order" (Kauf + SL + TP gleichzeitig)
order_request = MarketOrderRequest(
symbol=SYMBOL,
qty=QTY_PER_TRADE,
side=OrderSide.BUY,
time_in_force=TimeInForce.GTC,
order_class=OrderClass.BRACKET, # Verbindet Kauf mit SL/TP
stop_loss=StopLossRequest(stop_price=round(sl_price, 2)),
take_profit=TakeProfitRequest(limit_price=round(tp_price, 2))
)
trading_client.submit_order(order_request)
print(f"{Fore.GREEN}>>> AUTOMATISCHER KAUF: {QTY_PER_TRADE} {SYMBOL} @ {price}$")
print(f"{Fore.YELLOW}>>> STOP-LOSS GESETZT BEI: {sl_price:.2f}$")
def main_loop():
print(f"{Fore.CYAN}=== BOT STARTET IM AUTOMATIK-MODUS (PAPER) ===")
while True:
rsi, price = get_rsi()
# Check ob bereits eine Position offen ist
try:
position = trading_client.get_open_position(SYMBOL)
has_position = True
except:
has_position = False
if rsi:
print(f"[{datetime.now().strftime('%H:%M:%S')}] {SYMBOL} Preis: {price}$ | RSI: {rsi:.2f}")
# KAUFEN: RSI zu niedrig & wir haben noch nichts gekauft
if rsi < RSI_LOWER and not has_position:
execute_trade("BUY", price)
# VERKAUFEN: RSI zu hoch & wir haben eine Position
elif rsi > RSI_UPPER and has_position:
trading_client.close_position(SYMBOL)
print(f"{Fore.RED}>>> AUTOMATISCHER VERKAUF (RSI TARGET ERREICHT)")
time.sleep(60)
if __name__ == "__main__":
main_loop()

BIN
markt_scan_20260121.xlsx Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

73
optimizer.py Normal file
View File

@@ -0,0 +1,73 @@
import os
import pandas as pd
import pandas_ta as ta
from dotenv import load_dotenv
from datetime import datetime, timedelta
from backtesting import Backtest, Strategy
from backtesting.lib import crossover
from alpaca.data.historical import StockHistoricalDataClient
from alpaca.data.requests import StockBarsRequest
from alpaca.data.timeframe import TimeFrame
# --- 1. DATEN HOLEN ---
load_dotenv()
API_KEY = os.getenv('ALPACA_API_KEY')
SECRET_KEY = os.getenv('ALPACA_SECRET_KEY')
def get_data(symbol, days=365):
client = StockHistoricalDataClient(API_KEY, SECRET_KEY)
start_date = datetime.now() - timedelta(days=days)
request_params = StockBarsRequest(symbol_or_symbols=[symbol], timeframe=TimeFrame.Day, start=start_date)
df = client.get_stock_bars(request_params).df
df = df.reset_index(level=0, drop=True)
df.columns = [c.capitalize() for c in df.columns]
df.index = df.index.tz_localize(None)
return df
# --- 2. STRATEGIE MIT OPTIMIERBAREN PARAMETERN ---
class RsiStrategy(Strategy):
# Diese Klassenvariablen werden von der Optimize-Funktion überschrieben
rsi_period = 14
rsi_lower = 30
rsi_upper = 70
def init(self):
# Wichtig: Wir übergeben die Parameter an pandas_ta
self.rsi = self.I(ta.rsi, pd.Series(self.data.Close), length=self.rsi_period)
def next(self):
if crossover(self.rsi, self.rsi_lower):
self.buy()
elif crossover(self.rsi_upper, self.rsi):
if self.position:
self.position.close()
# --- 3. OPTIMIERUNGS-ENGINE ---
def run_optimized_backtest(symbol):
data = get_data(symbol)
bt = Backtest(data, RsiStrategy, cash=10000, commission=0.001)
print(f"--- Starte Optimierung für {symbol} ---")
# Hier passiert die Magie:
stats = bt.optimize(
rsi_period=range(7, 30, 2), # Teste Perioden von 7 bis 29 in 2er Schritten
rsi_lower=range(20, 40, 5), # Teste Kaufsignale von 20 bis 35
rsi_upper=range(60, 80, 5), # Teste Verkaufsignale von 60 bis 75
maximize='Return [%]', # Wir wollen den höchsten Gewinn (oder 'Sharpe Ratio')
constraint=lambda p: p.rsi_upper > p.rsi_lower # Logik-Check
)
print("\n--- BESTE PARAMETER GEFUNDEN ---")
print(stats)
print("\nDetails der besten Strategie:")
print(f"RSI Periode: {stats._strategy.rsi_period}")
print(f"RSI Untergrenze: {stats._strategy.rsi_lower}")
print(f"RSI Obergrenze: {stats._strategy.rsi_upper}")
# Speichere den Chart der besten Strategie
bt.plot(filename=f"optimized_report_{symbol}.html", open_browser=False)
if __name__ == "__main__":
run_optimized_backtest("AAPL")