Overall Statistics |
Total Orders 2221 Average Win 0.41% Average Loss -0.41% Compounding Annual Return 10.269% Drawdown 40.000% Expectancy 0.208 Net Profit 165.795% Sharpe Ratio 0.394 Sortino Ratio 0.359 Probabilistic Sharpe Ratio 2.517% Loss Rate 40% Win Rate 60% Profit-Loss Ratio 1.01 Alpha 0.005 Beta 0.856 Annual Standard Deviation 0.175 Annual Variance 0.031 Information Ratio -0.048 Tracking Error 0.124 Treynor Ratio 0.081 Total Fees $2169.00 Estimated Strategy Capacity $170000000.00 Lowest Capacity Asset BA R735QTJ8XC9X Portfolio Turnover 2.69% |
from AlgorithmImports import * class MinerviniSEPAStrategy(QCAlgorithm): def Initialize(self): self.SetStartDate(2014, 1, 1) # Start date self.SetEndDate(2024, 1, 1) # End date self.SetCash(10000) # Starting cash # Add SPY to the algorithm's data subscriptions self.spy = self.AddEquity("SPY").Symbol self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction) self.ma_short_period = 150 self.ma_long_period = 200 self.volume_multiplier = 1.5 self.max_position_size = 0.10 # Max size of any position relative to the portfolio self.symbols = {} self.next_rebalance_date = self.Time + timedelta(days=7) # Initialize next rebalance date # Now that SPY is added, schedule the rebalance check using SPY's market open time self.Schedule.On(self.DateRules.EveryDay(self.spy), self.TimeRules.AfterMarketOpen(self.spy, 10), self.RebalanceIfNeeded) def CoarseSelectionFunction(self, coarse): sorted_coarse = sorted([x for x in coarse if x.HasFundamentalData and x.Price > 5], key=lambda x: x.DollarVolume, reverse=True)[:100] return [x.Symbol for x in sorted_coarse] def OnSecuritiesChanged(self, changes): for security in changes.RemovedSecurities: if security.Symbol in self.symbols: self.Liquidate(security.Symbol) del self.symbols[security.Symbol] for security in changes.AddedSecurities: symbol = security.Symbol if symbol not in self.symbols: self.symbols[symbol] = { "150MA": self.SMA(symbol, self.ma_short_period, Resolution.Daily), "200MA": self.SMA(symbol, self.ma_long_period, Resolution.Daily), "VolumeSMA": self.SMA(symbol, 30, Resolution.Daily), } def RebalanceIfNeeded(self): if self.Time < self.next_rebalance_date: return for symbol, indicators in self.symbols.items(): if not (indicators["150MA"].IsReady and indicators["200MA"].IsReady): continue if symbol not in self.Securities: continue price = self.Securities[symbol].Price volume = self.Securities[symbol].Volume avg_volume = indicators["VolumeSMA"].Current.Value if self.IsStage2(indicators, price, volume, avg_volume): size = self.PositionSizing(symbol) self.SetHoldings(symbol, size) else: if self.Portfolio[symbol].Invested: self.Liquidate(symbol) # Update next rebalance date self.next_rebalance_date = self.Time + timedelta(days=7) def IsStage2(self, indicators, price, volume, avg_volume): avg150 = indicators["150MA"].Current.Value avg200 = indicators["200MA"].Current.Value return price > avg150 > avg200 and volume > avg_volume * self.volume_multiplier def PositionSizing(self, symbol): history = self.History(symbol, 31, Resolution.Daily).close if history.empty or len(history) < 31: return self.max_position_size daily_returns = history.pct_change().dropna() volatility = daily_returns.std() if volatility == 0: return self.max_position_size size = min(self.max_position_size, 1 / (volatility * 10)) # Adjusted for volatility return size # Remember, this is a starting point. You may need to adjust your universe selection criteria, # add more complex entry and exit criteria, incorporate earnings data, and refine the position sizing # based on your risk tolerance and strategy goals.