diff --git a/Backtest_Report_AAPL.html b/Backtest_Report_AAPL.html new file mode 100644 index 0000000..f9686a8 --- /dev/null +++ b/Backtest_Report_AAPL.html @@ -0,0 +1,61 @@ + + + + + Backtest_Report_AAPL.html + + + + + +
+ + + + + \ No newline at end of file diff --git a/Backtest_Report_TSLA.html b/Backtest_Report_TSLA.html new file mode 100644 index 0000000..c3be272 --- /dev/null +++ b/Backtest_Report_TSLA.html @@ -0,0 +1,61 @@ + + + + + Backtest_Report_TSLA.html + + + + + +
+ + + + + \ No newline at end of file diff --git a/Backtest_Trades_AAPL.xlsx b/Backtest_Trades_AAPL.xlsx new file mode 100644 index 0000000..45be3f3 Binary files /dev/null and b/Backtest_Trades_AAPL.xlsx differ diff --git a/Backtest_Trades_TSLA.xlsx b/Backtest_Trades_TSLA.xlsx new file mode 100644 index 0000000..3b21ccc Binary files /dev/null and b/Backtest_Trades_TSLA.xlsx differ diff --git a/backtester.py b/backtester.py new file mode 100644 index 0000000..f98b3e1 --- /dev/null +++ b/backtester.py @@ -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) \ No newline at end of file diff --git a/checkStocks.py b/checkStocks.py new file mode 100644 index 0000000..4f4564f --- /dev/null +++ b/checkStocks.py @@ -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.") \ No newline at end of file diff --git a/live_bot.py b/live_bot.py new file mode 100644 index 0000000..517ca96 --- /dev/null +++ b/live_bot.py @@ -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() \ No newline at end of file diff --git a/markt_scan_20260121.xlsx b/markt_scan_20260121.xlsx new file mode 100644 index 0000000..21a0693 Binary files /dev/null and b/markt_scan_20260121.xlsx differ diff --git a/optimized_report_AAPL.html b/optimized_report_AAPL.html new file mode 100644 index 0000000..7ebc345 --- /dev/null +++ b/optimized_report_AAPL.html @@ -0,0 +1,61 @@ + + + + + optimized_report_AAPL.html + + + + + +
+ + + + + \ No newline at end of file diff --git a/optimizer.py b/optimizer.py new file mode 100644 index 0000000..a30a978 --- /dev/null +++ b/optimizer.py @@ -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") \ No newline at end of file