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.