Overall Statistics
Total Trades
64
Average Win
1.00%
Average Loss
-1.28%
Compounding Annual Return
-10.300%
Drawdown
12.200%
Expectancy
-0.166
Net Profit
-6.926%
Sharpe Ratio
-0.859
Probabilistic Sharpe Ratio
3.897%
Loss Rate
53%
Win Rate
47%
Profit-Loss Ratio
0.78
Alpha
-0.081
Beta
-0.041
Annual Standard Deviation
0.099
Annual Variance
0.01
Information Ratio
-1.036
Tracking Error
0.17
Treynor Ratio
2.06
Total Fees
$355.08
import operator
class FadingTheGap(QCAlgorithm):

    def Initialize(self):
        #Backtesting parameters
        self.SetStartDate(2017, 11, 1)
        self.SetEndDate(2018, 7, 1)
        self.SetCash(100000)
        self.minimumDollar = 20
        self.maximumDollar = 5000
        self.topVolume     = 100
        
        # Securities list that is dynamically changes by the universe coarse filter
        self.SecuritiesList = []
        
        # Schedules
        self.AddEquity("SPY", Resolution.Daily)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 1), self.OpeningBar)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 2), self.Buy)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 45), self.ClosePositions) 
        
        # Universe Settings
        self.UniverseSettings.Resolution = Resolution.Minute
        self.AddUniverse(self.CoarseSelectionFilter)
        
        # Dictionaries
        self.securityDelta      = {}
        self.data               = {}
        self.securityDeviations = {}
                
    def CoarseSelectionFilter(self, coarse):
        # Sort equities by volume - highest volume first
        sortedByDollarVolume = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True)
        
        # Filter equities by price 
        filteredByPrice = [c.Symbol for c in sortedByDollarVolume if c.Price > self.minimumDollar and c.Price < self.maximumDollar]
        self.SecuritiesList = filteredByPrice[:self.topVolume]
        
        return self.SecuritiesList 
     
    def OnSecuritiesChanged(self, changes):
        # When securities are added we create a SymbolData object for them
        for security in changes.AddedSecurities:
            symbol = security.Symbol
            self.data[symbol] = SymbolData(self, symbol)
        
        
    def OpeningBar(self):
        # Go through all the top traded securities of the day
        for symbol in self.SecuritiesList:
        
            # Retrieve the current day open of the same securities
            if not self.CurrentSlice.ContainsKey(symbol): continue
            if self.CurrentSlice[symbol] is None: continue
            if self.CurrentSlice[symbol].Open <= 0: continue
            if self.CurrentSlice[symbol].IsFillForward: continue
            currentOpen  = self.CurrentSlice[symbol].Open
            
            self.securityDelta[symbol] = currentOpen - self.data[symbol].previousClose
        # reset our securityDeviations every morning before deviation calculations    
        self.securityDeviations = {}
    
    def Buy(self):
        for symbol, value in self.data.items():
            if value.std.IsReady and symbol in self.securityDelta:
                deviation = self.securityDelta[symbol] / value.std.Current.Value
                if deviation < -2:
                    self.securityDeviations[symbol] = deviation
        # if there are symbols with more than 2 deviations today
        if len(self.securityDeviations) > 0:
            # retrives the symbol with the largest negative deviation
            buySymbol = min(self.securityDeviations.items(), key=operator.itemgetter(1))[0]
            self.SetHoldings(buySymbol, 1)
            self.Debug("BUY " + buySymbol.Value  + " deviations: " + str(self.securityDeviations[buySymbol]) )
    
    def ClosePositions(self):
        for kvp in self.Portfolio:
            if kvp.Value.Invested:
                self.Debug("SELL " + kvp.Key.Value)
        # Liquidate Portfolio
        self.Liquidate()
        
        
class SymbolData:
    
    def __init__(self, algorithm, symbol):
        self.algorithm = algorithm
        self.symbol = symbol
        
        self.std = StandardDeviation(symbol, 60)
        self.previousClose = -1
        # Initalize indicator with historical daily data
        history = algorithm.History(symbol, 100, Resolution.Daily)
        
        for bar in history.itertuples():
            self.Update(bar.Index[1], bar.close)
            
        dailyConsolidator = TradeBarConsolidator(timedelta(days = 1))
        algorithm.SubscriptionManager.AddConsolidator(symbol, dailyConsolidator)
        dailyConsolidator.DataConsolidated += self.OnConsolidated
    # Update previous day close and daily STD indicator            
    def Update(self, time, close):
        self.std.Update(time, close)
        self.previousClose = close
    # daily consolidator updates our indicator and previous daily close    
    def OnConsolidated(self, sender, bar):
        self.Update(bar.Time, bar.Close)