Overall Statistics
Total Trades
1197
Average Win
0.11%
Average Loss
-0.04%
Compounding Annual Return
58.957%
Drawdown
10.400%
Expectancy
0.669
Net Profit
24.086%
Sharpe Ratio
2.188
Probabilistic Sharpe Ratio
71.688%
Loss Rate
51%
Win Rate
49%
Profit-Loss Ratio
2.44
Alpha
0.478
Beta
0.072
Annual Standard Deviation
0.229
Annual Variance
0.053
Information Ratio
0.697
Tracking Error
0.25
Treynor Ratio
6.943
Total Fees
$1433.33
Estimated Strategy Capacity
$360000.00
Lowest Capacity Asset
HWKN R735QTJ8XC9X
class WellDressedSkyBlueSardine(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2021, 2, 25)
     #   self.SetEndDate(2020, 3, 1)
        self.SetCash(100000)
        self.SetBenchmark("SPY")
        self.rebalanceTime = datetime.min
        self.activeStocks = set()
        
        
        self.AddUniverse(self.CoarseFilter, self.FineFilter)
        self.UniverseSettings.Resolution = Resolution.Daily
  
        #self.brokerage = BrokerageName.InteractiveBrokersBrokerage
        self.SetBrokerageModel(BrokerageName.AlphaStreams)
        #self.SetBrokerageModel(self.brokerage, AccountType.Margin)
     
        self.portfolioTargets = []
        self.UniverseSettings.DataNormalizationMode=DataNormalizationMode.SplitAdjusted 
        #self.UniverseSettings.ExtendedMarketHours = False 
        
       
        self.AddRiskManagement(MyRiskModel(.21))
        self.SetExecution(ImmediateExecutionModel())
        self.SetAlpha(LongOnlyConstantAlphaCreationModel())
        

        
    def CoarseFilter(self, coarse):
        # Rebalancing monthly
        if self.Time <= self.rebalanceTime:
            return self.Universe.Unchanged
        self.rebalanceTime = self.Time + timedelta(2)
        
        myuniverse = [x for x in coarse if x.Price < 50 and x.DollarVolume > 1000000]
    
        return [x.Symbol for x in myuniverse if x.Price < 50
                                                and x.HasFundamentalData][:2000]   
        
    def FineFilter(self, fine):
        security_filter = [x for x in fine if x.EarningReports.DilutedEPS.Value > .1 
            and x.ValuationRatios.PERatio < 25
            and x.OperationRatios.RevenueGrowth.ThreeMonths > .05
            and x.MarketCap > 100000000
            and x.OperationRatios.ROA.ThreeMonths > .02
            and x.OperationRatios.ROE.ThreeMonths > .03
            and x.EarningRatios.DilutedEPSGrowth.ThreeMonths > .03
            and x.OperationRatios.NetMargin.ThreeMonths > .08
            and x.ValuationRatios.PSRatio < 1.5]


        sorting = sorted(security_filter, key = lambda x: x.ValuationRatios.PSRatio, reverse=False)
        self.Log(str([x.OperationRatios.RevenueGrowth.ThreeMonths for x in sorting[:50]]))
        self.Log(str([x.MarketCap for x in sorting[:50]]))
        self.Log(str([x.OperationRatios.ROE.ThreeMonths for x in sorting[:50]]))
        self.Log(str([x.OperationRatios.ROA.ThreeMonths for x in sorting[:50]]))
        self.Log(str([x.EarningRatios.DilutedEPSGrowth.ThreeMonths for x in sorting[:50]]))
        self.Log(str([x.OperationRatios.NetMargin.ThreeMonths for x in sorting[:50]]))
        self.Log(str([x.ValuationRatios.PSRatio for x in sorting[:50]]))

        return [x.Symbol for x in sorting[:50]]
    
      
    def OnSecuritiesChanged(self, changes):
    
            
       # close positions in removed securities
        for x in changes.RemovedSecurities:
            self.Liquidate(x.Symbol)
            if x.Symbol in self.activeStocks:
                self.activeStocks.remove(x.Symbol)
            
            
        # can't open positions here since data might not be added correctly yet
        for x in changes.AddedSecurities:
            self.activeStocks.add(x.Symbol)

        # adjust targets if universe has changed
        self.portfolioTargets = [PortfolioTarget(symbol, 1/len(self.activeStocks))
                        for symbol in self.activeStocks]
                            
                            
        
    def OnData(self, data):

            
        if self.portfolioTargets == []:
            return
        
        for symbol in self.activeStocks:
            if symbol not in data:
                return
        
        self.SetHoldings(self.portfolioTargets)
        
      #  self.portfolioTargets = []
    
        

        
        
 #Riskmodel not working properly    
class MyPortfolioModel(EqualWeightingPortfolioConstructionModel):
    def __init__(self):
        pass
        
    def CreateTargets(self, algorithm, insights):
        
        # Simple insight weighting PCM
        targets = []
        for insight in insights:
            targ = PortfolioTarget(insight.Symbol, insight.Direction*insight.Weight)
            targets.append(targ)
        return targets

class MyRiskModel(RiskManagementModel):

    def __init__(self, maxDrawdown=.21):

        self.maxDrawdown = maxDrawdown
        self.liquidatedSymbols = set()                                  # Tracks symbols that have been liquidated
        self.currentTargets = []                                        # Tracks state of current targets

    def ManageRisk(self, algorithm, targets):
        
        # Reset trackers on new targets
        if (set(targets) != self.currentTargets) and len(targets)>0:
            algorithm.Log(f'New Targets. Quantity: {targets[0].Quantity}')
            self.liquidatedSymbols = set()
            self.currentTargets = set(targets)

        riskAdjustedTargets = []
        for _ in algorithm.Securities:
            symbol = _.Key                                              # Symbol object
            security = _.Value                                          # Security object
            ticker = symbol.Value                                       # String ticker

            symbolPnL = security.Holdings.UnrealizedProfitPercent       # Current PnL

            #Liquidate if exceed  drawdown
            
            if (symbolPnL < -self.maxDrawdown) or (ticker in self.liquidatedSymbols):
                riskAdjustedTargets.append(PortfolioTarget(symbol, 0))
                
                if algorithm.Securities[symbol].Invested:
                    self.liquidatedSymbols.add(ticker)
                    algorithm.Log(f'Trailing stop loss triggered for {ticker}.')

        return riskAdjustedTargets
        
class LongOnlyConstantAlphaCreationModel(AlphaModel):
    
    '''
    Description:
        This Alpha model creates InsightDirection.Up (to go Long) for a duration of 1 day, every day for all active securities in our Universe
    Details:
        The important thing to understand here is the concept of Insight:
            - A prediction about the future of the security, indicating an expected Up, Down or Flat move
            - This prediction has an expiration time/date, meaning we think the insight holds for some amount of time
            - In the case of a constant long-only strategy, we are just updating every day the Up prediction for another extra day
            - In other words, every day we are making the conscious decision of staying invested in the security one more day
    '''

    def __init__(self, resolution = Resolution.Daily):
        
        self.insightExpiry = Time.Multiply(Extensions.ToTimeSpan(resolution), 0.25) # insight duration
        self.insightDirection = InsightDirection.Up # insight direction
        self.securities = [] # list to store securities to consider
        
    def Update(self, algorithm, data):
        
        insights = [] # list to store the new insights to be created
        
        # loop through securities and generate insights
        for security in self.securities:
            # check if there's new data for the security or we're already invested
            # if there's no new data but we're invested, we keep updating the insight since we don't really need to place orders
            if data.ContainsKey(security.Symbol) or algorithm.Portfolio[security.Symbol].Invested:
                # append the insights list with the prediction for each symbol
                insights.append(Insight.Price(security.Symbol, self.insightExpiry, self.insightDirection))
            else:
                algorithm.Log('excluding this security due to missing data: ' + str(security.Symbol.Value))
            
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        
        '''
        Description:
            Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm
        '''
        
        # add new securities
        for added in changes.AddedSecurities:
            self.securities.append(added)

        # remove securities
        for removed in changes.RemovedSecurities:
            if removed in self.securities:
                self.securities.remove(removed)