Overall Statistics
Total Trades
1458
Average Win
0.25%
Average Loss
-0.23%
Compounding Annual Return
1.547%
Drawdown
5.900%
Expectancy
0.077
Net Profit
13.078%
Sharpe Ratio
0.439
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.12
Alpha
0.011
Beta
0.03
Annual Standard Deviation
0.03
Annual Variance
0.001
Information Ratio
-0.306
Tracking Error
0.193
Treynor Ratio
0.435
Total Fees
$2697.30
class SeasonalityMarketSessionTrading(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2008, 1, 1)  # Set Start Date
        self.SetEndDate(2015, 12, 31) # Set End Date
        self.SetCash(100000)  # Set Strategy Cash
        
        future = self.AddFuture(Futures.Indices.SP500EMini) # S&P E-mini future contracts
        future.SetFilter(timedelta(0), timedelta(180)) # Filter according to Expiry date
        
        # Set benchmark
        benchmark = self.AddEquity("SPY")
        self.SetBenchmark(benchmark.Symbol)
        
        # Set season
        # Bullish Season: Nov - May; 
        # Bearish Season: Jun - Oct
        
        # self.season = {11, 12, 1, 2, 3, 4, 5} # Bullish Season
        
        self.season = {6, 7, 8, 9, 10} # Bearish Season
        
        # Overnight trading strategy
        
        # Night: 1800 - 530
        # Afternoon: 1315 - 1400
        # Close: 1400 - 1515
        
        # Night session
        self.Schedule.On(self.DateRules.EveryDay('SPY'), self.TimeRules.At(18, 0), Action(self.Enter))
        self.Schedule.On(self.DateRules.EveryDay('SPY'), self.TimeRules.At(5, 30), Action(self.Exit))
        
        # # Afternoon session
        # self.Schedule.On(self.DateRules.EveryDay('SPY'), self.TimeRules.At(13, 15), Action(self.Enter))
        # self.Schedule.On(self.DateRules.EveryDay('SPY'), self.TimeRules.At(14, 0), Action(self.Exit))
        
        # # Close session
        # self.Schedule.On(self.DateRules.EveryDay('SPY'), self.TimeRules.At(14, 0), Action(self.Enter))
        # self.Schedule.On(self.DateRules.EveryDay('SPY'), self.TimeRules.At(15, 15), Action(self.Exit))


    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
            Arguments:
                data: Slice object keyed by symbol containing the stock data
        '''
        # self.Debug(f'Month: {self.Time.date().month}')
        self.data = data
        pass
    
    def Enter(self):
        if (not self.Portfolio.Invested) and (self.Time.date().month in self.season):
            
            for chain in self.data.FutureChains:
                contracts = list(filter(lambda x: x.Expiry < self.Time + timedelta(90), chain.Value))
                
                # self.Debug(f'Num of Contracts {self.Time}: {len(contracts)}')
                
                if len(contracts) == 0:
                    continue
                
                front = sorted(contracts, key = lambda x: x.Expiry, reverse = False)[0]

                # long
                self.MarketOrder(front.Symbol, 1)
    
    def Exit(self):
        if self.Portfolio.Invested:
            
            # short
            self.Liquidate()