Overall Statistics
Total Trades
32
Average Win
0.52%
Average Loss
-0.90%
Compounding Annual Return
64.658%
Drawdown
7.700%
Expectancy
0.022
Net Profit
7.070%
Sharpe Ratio
2.082
Probabilistic Sharpe Ratio
61.464%
Loss Rate
35%
Win Rate
65%
Profit-Loss Ratio
0.58
Alpha
0.649
Beta
-0.148
Annual Standard Deviation
0.288
Annual Variance
0.083
Information Ratio
0.845
Tracking Error
0.308
Treynor Ratio
-4.041
Total Fees
$32.00
import pandas as pd

class ParticleNadirsonInterceptor(QCAlgorithm):
  
    
    def Initialize(self): 
        
        self.SetStartDate(2020, 12, 2)  # Set Start Date
        self.SetEndDate(2021, 1, 20)    # Set End Date
        self.SetCash(10000)          # Set Strategy Cash
        
        self.current_month  =   -1
        self.coarse_count   =   10
        self.fine_count     =   5
        self.benchmark      =   "SPY"
        self.resolution     =   Resolution.Daily  
        
        self.SetWarmUp(timedelta(minutes = 10)) 
        self.UniverseSettings.Resolution = self.resolution
        self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionRsBased)
        
        self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, timedelta(30)))

        myPCM = EqualWeightingPortfolioConstructionModel()
        myPCM.RebalanceOnInsightChanges = False
        myPCM.RebalanceOnSecurityChanges = True
        
        self.SetPortfolioConstruction(myPCM)
        self.SetExecution(ImmediateExecutionModel())

        
    def CoarseSelectionFunction(self, coarse):
    
            if self.current_month == self.Time.month:
                return Universe.Unchanged
    
            self.current_month = self.Time.month
    
            sortedByDollarVolume = sorted([x for x in coarse if x.HasFundamentalData],
                key=lambda x: x.DollarVolume, reverse=True)[:self.coarse_count] 
                
            self.Debug(f"No of Securities in Coarse Selection: {len([i.Symbol for i in sortedByDollarVolume])}")

            return [i.Symbol for i in sortedByDollarVolume]
    
    def FineSelectionRsBased(self, fine):
        
        resultSet={}    
        
        rsBenchmark=self.getRslFactor(self.benchmark)

        for x in fine:
            resultSet[x.Symbol]=self.getRslFactor(str(x.Symbol.Value))

        resultSet=sorted([x for x in resultSet.items() if x[1]>rsBenchmark], key=lambda x: x[1], reverse=True)[:self.fine_count] 
       
        self.Debug(f"No of Securities in Fine Selection: {len([x[0] for x in  resultSet])}")
        
        for security in self.ActiveSecurities.Keys:
            self.Log("Security: " + str(security.Value))
            self.RemoveSecurity(security.Value)

        return [x[0] for x in resultSet]
        
    def getRslFactor(self,symbol):
        
        self.AddEquity(symbol, Resolution.Daily)

        # lookback days : weight 
        days = {40:0.6,80:0.25,160:0.15}
        result=[]

        df=pd.DataFrame(self.History(self.Symbol(symbol), 300, Resolution.Daily)) 
        df=df.iloc[::-1]
        df=df.reset_index(level=0, drop=True)
        for x in days:
    
            if len(df)>int(x):
                result.append([symbol, x, df.iloc[0]['close'], df.iloc[x-1]['close'],days[x]])
            else:
                return -1000 # missing data workaround
        
        df = pd.DataFrame(result,columns=['Symbol','Days','Ref_Price','Close_Price','Weight'],dtype=float)
        df = df.assign(Rsl=(df['Ref_Price'])/df['Close_Price']*df['Weight'])
        rsl= round(float((abs(df['Rsl']).sum()*1000)-1000),5)
        
        
        return rsl
# selection - RS based / Z-score?
    # risk - manual (hourly) or mean reverse
    # rebalance - weekly based on growth
    # cash - increase size monthly
    
         # Returns True if TradeBar data is present else False
        #if not data.Bars.ContainKey(symbol):
        #return

    #self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFundamental)
    #    def FineSelectionFundamental(self, fine):
    
     #       fine = [x for x in fine if x.EarningReports.TotalDividendPerShare.ThreeMonths
     #                              and x.ValuationRatios.PriceChange1M 
     #                              and x.ValuationRatios.BookValuePerShare
     #                              and x.ValuationRatios.FCFYield]
    
    #        sortedByfactor1 = sorted(fine, key=lambda x: x.EarningReports.TotalDividendPerShare.ThreeMonths, reverse=True)
    #        sortedByfactor2 = sorted(fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=False)
    #        sortedByfactor3 = sorted(fine, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse=True)
    #        sortedByfactor4 = sorted(fine, key=lambda x: x.ValuationRatios.FCFYield, reverse=True)
    
    #        stock_dict = {}
    
    #        for rank1, ele in enumerate(sortedByfactor1):
    #            rank2 = sortedByfactor2.index(ele)
    #            rank3 = sortedByfactor3.index(ele)
    #            rank4 = sortedByfactor4.index(ele)
    #            stock_dict[ele] = rank1 + rank2 + rank3 + rank4
    
    #        sorted_stock = sorted(stock_dict.items(),
    #            key=lambda d:d[1], reverse=True)[:self.fine_count]
    
    #        return [x[0].Symbol for x in sorted_stock]