Overall Statistics
Total Trades
1733
Average Win
1.08%
Average Loss
-0.29%
Compounding Annual Return
19.882%
Drawdown
17.400%
Expectancy
0.862
Net Profit
773.309%
Sharpe Ratio
1.172
Probabilistic Sharpe Ratio
65.958%
Loss Rate
61%
Win Rate
39%
Profit-Loss Ratio
3.72
Alpha
0.096
Beta
0.408
Annual Standard Deviation
0.121
Annual Variance
0.015
Information Ratio
0.217
Tracking Error
0.135
Treynor Ratio
0.347
Total Fees
$14159.42
Estimated Strategy Capacity
$36000.00
Lowest Capacity Asset
FOR TYX3G2PC742T
class WellDressedSkyBlueSardine(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2010, 1, 1)
     #   self.SetEndDate(2010, 6, 1)
        self.cap = 100000
        self.SetCash(self.cap) 
        self.SetBenchmark("SPY")
        self.rebalanceTime = datetime.min
        self.activeStocks = set()
        
        self.AddUniverse(self.CoarseFilter, self.FineFilter)
        self.UniverseSettings.Resolution = Resolution.Hour
  
        self.brokerage = BrokerageName.InteractiveBrokersBrokerage
        self.SetBrokerageModel(self.brokerage, AccountType.Margin)
        
        self.portfolioTargets = []
        self.UniverseSettings.DataNormalizationMode=DataNormalizationMode.SplitAdjusted #how data goes into alg
        self.UniverseSettings.ExtendedMarketHours = False #Does not takes in account after hours data
        
        self.AddRiskManagement(MyRiskModel(.12))
        self.SetAlpha(LongOnlyConstantAlphaCreationModel())
        self.Settings.RebalancePortfolioOnInsightChanges = False;
        self.Settings.RebalancePortfolioOnSecurityChanges = False;
        self.MKT = self.AddEquity('SPY', Resolution.Daily).Symbol 
        self.mkt = []
        
    def CoarseFilter(self, coarse):
    
        if self.Time <= self.rebalanceTime:
            return self.Universe.Unchanged
        self.rebalanceTime = self.Time + timedelta(4)
        
        myuniverse = [x for x in coarse if x.Price > 5 and x.DollarVolume > 1000000]
    
        return [x.Symbol for x in myuniverse if x.Price > 5
                                                and x.HasFundamentalData][:2000]   
        
    def FineFilter(self, fine):
        security_filter = [x for x in fine if x.EarningReports.DilutedEPS.Value > .0 
            and x.ValuationRatios.PERatio < 15
            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 > .1
            and x.ValuationRatios.PSRatio < 1
            and x.ValuationRatios.EVToEBITDA < 10
            and x.ValuationRatios.EVtoRevenue < 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[:100]]))
        self.Log(str([x.MarketCap for x in sorting[:100]]))
        self.Log(str([x.OperationRatios.ROE.ThreeMonths for x in sorting[:100]]))
        self.Log(str([x.OperationRatios.ROA.ThreeMonths for x in sorting[:100]]))
        self.Log(str([x.EarningRatios.DilutedEPSGrowth.ThreeMonths for x in sorting[:100]]))
        self.Log(str([x.OperationRatios.NetMargin.ThreeMonths for x in sorting[:100]]))
        self.Log(str([x.ValuationRatios.PSRatio for x in sorting[:100]]))
        self.Log(str([x.ValuationRatios.EVToEBITDA for x in sorting[:100]]))
        return [x.Symbol for x in sorting[:100]]
    
      
      
    def OnSecuritiesChanged(self, changes):
        for x in changes.RemovedSecurities:
            self.Liquidate(x.Symbol)
            if x.Symbol in self.activeStocks:
                self.activeStocks.remove(x.Symbol)
                
        for x in changes.AddedSecurities:
            self.activeStocks.add(x.Symbol)
      
        self.portfolioTargets = [PortfolioTarget(symbol, .8/(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 = []
    
    def OnEndOfDay(self): 
        mkt_price = self.Securities[self.MKT].Close
        self.mkt.append(mkt_price)
        mkt_perf = self.mkt[-1] / self.mkt[0] * self.cap
        self.Plot('Strategy Equity', 'SPY', mkt_perf)
        
            

   
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=.12):

        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)