Overall Statistics
Total Orders
1029
Average Win
0.62%
Average Loss
-0.34%
Compounding Annual Return
36.983%
Drawdown
29.600%
Expectancy
0.932
Start Equity
13000
End Equity
55665.31
Net Profit
328.195%
Sharpe Ratio
1.165
Sortino Ratio
1.308
Probabilistic Sharpe Ratio
63.417%
Loss Rate
32%
Win Rate
68%
Profit-Loss Ratio
1.85
Alpha
0
Beta
0
Annual Standard Deviation
0.211
Annual Variance
0.045
Information Ratio
1.281
Tracking Error
0.211
Treynor Ratio
0
Total Fees
$1029.00
Estimated Strategy Capacity
$170000000.00
Lowest Capacity Asset
JNJ R735QTJ8XC9X
Portfolio Turnover
3.25%
from AlgorithmImports import *

class QuarterlyRebalanceAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 1, 1)  # Startdatum
        self.SetEndDate(2024, 12, 12)  # Enddatum
        self.SetCash(13000)            # Startkapital
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)

        # Setze eine Warm-up-Phase auf 200 Tage
        self.SetWarmUp(timedelta(days=200))

        # Universe Selection: Wählen Sie die 10 Aktien mit der größten Marktkapitalisierung
        self.num_stocks = 10
        self.UniverseSettings.Resolution = Resolution.Daily
        self.AddUniverse(self.CoarseSelectionFunction)

        # SMA für den S&P 500
        self.sp500 = self.AddEquity("SPY", Resolution.Daily).Symbol
        self.sma200 = self.SMA(self.sp500, 100, Resolution.Daily)
        
        # Statusvariablen für den S&P 500 SMA Kreuz
        self.last_sma200_cross = None

        # Quartalsweises Rebalancing (am letzten Handelstag eines jeden Quartals)
        self.Schedule.On(self.DateRules.MonthEnd(self.sp500, 3), 
                         self.TimeRules.At(15, 50),  # Rebalance 10 Minuten vor Marktclose
                         self.Rebalance)

    def OnWarmupFinished(self):
        # Diese Methode wird aufgerufen, nachdem die Warm-up-Phase abgeschlossen ist.
        self.Debug("Warm-up abgeschlossen. Der Algorithmus ist bereit für den Handel.")
        
        # Führe ein initiales Rebalancing durch, falls das Portfolio leer ist
        if not any(x.Invested for x in self.Portfolio.Values):
            self.Rebalance()

    def CoarseSelectionFunction(self, coarse):
        # Sortiere Aktien nach Marktkapitalisierung und nimm die obersten 10
        selected = sorted([x for x in coarse if x.HasFundamentalData and x.MarketCap > 0], 
                          key=lambda x: x.MarketCap, 
                          reverse=True)[:self.num_stocks]
        return [x.Symbol for x in selected]

    def OnSecuritiesChanged(self, changes):
        pass  # Keine spezielle Logik für hinzugefügte oder entfernte Wertpapiere

    def OnData(self, data):
        # Überprüfe, ob der Warm-up abgeschlossen ist
        if self.IsWarmingUp:
            return

        if not self.sma200.IsReady:
            return

        current_sma200 = self.sma200.Current.Value
        current_price = data[self.sp500].Close if self.sp500 in data else None
        
        if current_price is None:
            return

        if current_price < current_sma200 and (self.last_sma200_cross is None or self.last_sma200_cross):
            # Reduziert die Gewichtung auf 2% pro Aktie
            self.ReduceHoldingsTo(0.02)
            self.last_sma200_cross = False

        elif current_price > current_sma200 and (self.last_sma200_cross is None or not self.last_sma200_cross):
            # Erhöht die Gewichtung auf 10% pro Aktie
            self.Rebalance()
            self.last_sma200_cross = True

    def Rebalance(self):
        # Überprüfe, ob der Warm-up abgeschlossen ist
        if self.IsWarmingUp:
            return

        # Universum der obersten 10 Aktien abrufen
        selected_symbols = [x.Symbol for x in self.UniverseManager.ActiveSecurities.Values]
        
        # SPY aus dem Handelsuniversum entfernen
        selected_symbols = [symbol for symbol in selected_symbols if symbol != self.sp500]
        
        # Setze alle Positionen auf 10% Gewichtung
        for symbol in selected_symbols:
            self.SetHoldings(symbol, 0.10)
        
        # Liquidieren von Positionen, die nicht mehr im aktuellen Universum sind
        for symbol in self.Portfolio.Keys:
            if symbol not in selected_symbols and self.Portfolio[symbol].Invested:
                self.Liquidate(symbol)

    def ReduceHoldingsTo(self, weight):
        for symbol in self.Portfolio.Keys:
            if symbol != self.sp500 and self.Portfolio[symbol].Invested:
                self.SetHoldings(symbol, weight)