| 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)