Overall Statistics
Total Trades
100
Average Win
4.23%
Average Loss
-2.56%
Compounding Annual Return
44.875%
Drawdown
44.000%
Expectancy
0.191
Net Profit
45.022%
Sharpe Ratio
0.954
Probabilistic Sharpe Ratio
40.172%
Loss Rate
55%
Win Rate
45%
Profit-Loss Ratio
1.65
Alpha
0.512
Beta
-0.503
Annual Standard Deviation
0.448
Annual Variance
0.201
Information Ratio
0.436
Tracking Error
0.597
Treynor Ratio
-0.85
Total Fees
$539.07
Estimated Strategy Capacity
$980000.00
Lowest Capacity Asset
OIH V2LT3QH97TYD
class SmoothBlueGuanaco(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2020, 1, 1)  # Set Start Date
        self.SetEndDate(2021,1,1)
        self.SetCash(100000)  # Set Strategy Cash
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
        self.data = {}
        self.closingPrices = {}
        self.symbols =  ["SPY", "IGV", "XLE", "VNQ", "XLF", "XME", "XHB", "TBT", "KWEB", "OIH","XBI"]
        for symbol in self.symbols:
            self.AddEquity(symbol, Resolution.Daily)
            self.data[symbol] = self.MOM(symbol, 30, Resolution.Daily)
            self.closingPrices[symbol] = self.Identity(symbol, Resolution.Daily, Field.Close)
            self.Log("{} symbol:{} closingPrice:{}".format(self.Time, symbol, self.closingPrices[symbol]))
      
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Friday), self.TimeRules.AfterMarketOpen("SPY",180), self.Rebalance) # Rebalances at 12:30 EST
        self.Schedule.On(self.DateRules.On(2022, 5, 31), self.TimeRules.AfterMarketOpen("SPY", 150), self.Rebalance)

        self.Settings.FreePortfolioValuePercentage=0
        self.Leverage=1.4
        self.assetsToHoldLong=2 # number of assets to hold long in portfolio 
        self.assetsToHoldShort=2 # number of assets to hold short in portfolio 
        

    def OnData(self, data: Slice):
        if self.IsWarmingUp: return
            
        #self.Debug("{} Entered OnData".format(self.Time))
        for symbol in self.symbols:
            if data.Bars.ContainsKey(symbol) and data.Bars[symbol] is not None:
                self.closingPrices[symbol] = data.Bars[symbol].Close
                
        for symbol in self.symbols:
            self.Plot('closingPrices',  symbol, self.closingPrices[symbol])

    def Rebalance(self):
        
        topLong = pd.Series(self.data).sort_values(ascending = False)[:self.assetsToHoldLong]
        topShort = pd.Series(self.data).sort_values(ascending = True)[:self.assetsToHoldShort]
        #self.Debug("{} Entering Rebalance with topLong:{}".format(self.Time, topLong ))
        #self.Debug("{} Entering Rebalance with topShort:{}".format(self.Time, topShort ))
        
        for kvp in self.Portfolio:
            security_key = kvp.Key
            security_hold = kvp.Value
            # liquidate the security which is no longer in the top3 momentum list
            if security_hold.Invested and self.Portfolio[security_key].Quantity > 1 :
                # For longs, liquidate position if momentum drops below 1 
                #self.Debug("Entered long")
                if (security_hold.Symbol.Value not in topLong.index or (security_hold.Invested and self.data[security_hold.Symbol.Value].Current.Value < 2)):
                    #self.Debug("{} Elim Long pos {} @{} Qty:{} MOM:{}".format(self.Time, security_hold, self.closingPrices.get(security_hold.Symbol, "None"),self.Portfolio[security_key].Quantity, self.data[security_hold.Symbol.Value].Current.Value))
                    self.Liquidate(security_hold.Symbol)
            elif security_hold.Invested and self.Portfolio[security_key].Quantity < 1 :
                #self.Debug("Entered Short")
                if security_hold.Symbol.Value not in topShort.index or (security_hold.Invested and (self.data[security_hold.Symbol.Value].Current.Value > -2)):
                # For longs, liquidate position if momentum drops below 1 
                    #self.Debug("{} Elim Short pos {} @{} Qty:{} MOM:{}".format(self.Time, security_hold, self.closingPrices.get(security_hold.Symbol, "None"), self.Portfolio[security_key].Quantity, self.data[security_hold.Symbol.Value].Current.Value))
                    self.Liquidate(security_hold.Symbol)
            
        # Buy top long stocks 
        added_symbolsLong = []        
        for symbol in topLong.index:
            self.Debug("{} TOP LONG Symbol:{} Mom:{} Close:{}".format(self.Time, symbol, self.data[symbol].Current.Value, self.closingPrices[symbol]))
            if not self.Portfolio[symbol].Invested and self.data[symbol].Current.Value > 1:
                added_symbolsLong.append(symbol)
                
         # Sell top short stocks 
        added_symbolsShort = []        
        for symbol in topShort.index:
            self.Debug("{} TOP SHORT Symbol:{} Mom:{} Close:{}".format(self.Time, symbol, self.data[symbol].Current.Value, self.closingPrices[symbol]))
            if not self.Portfolio[symbol].Invested and self.data[symbol].Current.Value < -1:
                added_symbolsShort.append(symbol)
                
        added_symbols = added_symbolsLong+added_symbolsShort
        count = 0
        for added in added_symbolsLong:

            lastPrice = self.closingPrices[added]
            
            if not self.closingPrices[added] ==0: 
                sharesToBuy= (self.Portfolio.MarginRemaining*self.Leverage) / (len(added_symbols)-count) // lastPrice
            self.Debug("{} Shares to buy Symbol:{} Close:{} RemMargin:{:.2f} shares:{}"
                    .format(self.Time, added, lastPrice , self.Portfolio.MarginRemaining, sharesToBuy))
            
            self.MarketOrder(added, sharesToBuy)
            count =+1
                
        for added in added_symbolsShort:
            lastPrice = self.closingPrices[added]
            
            sharesToBuy= - (self.Portfolio.MarginRemaining*self.Leverage) / (len(added_symbols)-count) // lastPrice
            self.Debug("{} Shares to sell Symbol:{} Close:{} RemMargin:{:.2f} shares:{}"
                    .format(self.Time, added, lastPrice , self.Portfolio.MarginRemaining, sharesToBuy))
            
            self.MarketOrder(added, sharesToBuy)