Overall Statistics
Total Orders
385
Average Win
1.03%
Average Loss
-0.99%
Compounding Annual Return
8.475%
Drawdown
20.600%
Expectancy
0.057
Start Equity
100000
End Equity
108450.76
Net Profit
8.451%
Sharpe Ratio
0.305
Sortino Ratio
0.255
Probabilistic Sharpe Ratio
23.165%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.04
Alpha
-0
Beta
-0.461
Annual Standard Deviation
0.196
Annual Variance
0.039
Information Ratio
0.558
Tracking Error
0.342
Treynor Ratio
-0.13
Total Fees
$1272.87
Estimated Strategy Capacity
$120000000.00
Lowest Capacity Asset
JNJ R735QTJ8XC9X
Portfolio Turnover
48.36%
from AlgorithmImports import *

class MeanReversionShortAlgorithm(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2022, 1, 1)
        self.SetEndDate(2023, 1, 1)
        self.SetCash(100000)
        
        # Define a list of 10 well-known, highly liquid stocks
        self.tickers = [
            'AAPL', 'MSFT', 'GOOGL', 'AMZN', 'NVDA', 'META', 'TSLA', 'JNJ', 'JPM', 'V'
        ]

        # Add Equity data for the tickers
        self.symbols = []
        for ticker in self.tickers:
            self.symbols.append(self.AddEquity(ticker, Resolution.Daily).Symbol)
        
        # Add SPY for scheduling and market checks
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        
        # Dictionary to store previous close prices
        self.previous_close = {}
        
        # Warm-up the algorithm to get historical data for previous close prices
        self.SetWarmup(1)
        
        # Schedule event to sell all positions at 3:55 PM
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.At(15, 55), self.Rebalance)
        
    def OnData(self, data):
        if self.IsWarmingUp:
            return
        
        # Avoid trading near market close
        if self.Time.hour == 15 and self.Time.minute >= 55:
            return
        
        # Collect previous close prices
        for symbol in self.symbols:
            if symbol in data.Bars:
                self.previous_close[symbol] = data.Bars[symbol].Close
        
        # Calculate overnight percentage change
        overnight_changes = {}
        for symbol in self.symbols:
            if symbol in data.Bars and symbol in self.previous_close:
                open_price = data.Bars[symbol].Open
                prev_close_price = self.previous_close[symbol]
                overnight_change_pct = (open_price - prev_close_price) / prev_close_price * 100
                overnight_changes[symbol] = overnight_change_pct
        
        # Find stocks with greater than 2% but less than 3% overnight upmove
        top_movers = [symbol for symbol, change in overnight_changes.items() if 2 < change < 3]
        
        # Open short positions for the stocks with greater than 2% but less than 3% upmove
        for symbol in top_movers:
            self.SetHoldings(symbol, -1.0 / len(top_movers))  # Short position
        
        # Reset previous close prices
        self.previous_close = {}
    
    def Rebalance(self):
        if self.IsWarmingUp:
            return
        
        # Ensure market is open before liquidating positions
        if not self.Securities[self.spy].Exchange.DateTimeIsOpen(self.Time):
            return
        
        # Liquidate all positions before market close
        for kvp in self.Portfolio:
            if kvp.Value.Invested:
                self.Liquidate(kvp.Key)
 
        # Log for debugging purposes
        self.Debug(f"Positions liquidated at {self.Time}")

# Ensure the code runs correctly
from AlgorithmImports import *