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)