Overall Statistics |
Total Trades 280 Average Win 0.22% Average Loss -0.22% Compounding Annual Return 14.188% Drawdown 34.200% Expectancy -0.171 Net Profit 14.230% Sharpe Ratio 0.492 Probabilistic Sharpe Ratio 25.982% Loss Rate 59% Win Rate 41% Profit-Loss Ratio 1.01 Alpha -0.029 Beta 1.038 Annual Standard Deviation 0.296 Annual Variance 0.087 Information Ratio -0.346 Tracking Error 0.064 Treynor Ratio 0.14 Total Fees $280.36 Estimated Strategy Capacity $45000000.00 Lowest Capacity Asset NTAP R735QTJ8XC9X |
#region imports from AlgorithmImports import * #endregion class FundamentalFactorAlphaModel(AlphaModel): def __init__(self): self.rebalanceTime = datetime.min # Dictionary containing set of securities in each sector # e.g. {technology: set(AAPL, TSLA, ...), healthcare: set(XYZ, ABC, ...), ... } self.sectors = {} def Update(self, algorithm, data): '''Updates this alpha model with the latest data from the algorithm. This is called each time the algorithm receives data for subscribed securities Args: algorithm: The algorithm instance data: The new data available Returns: New insights''' if algorithm.Time <= self.rebalanceTime: return [] # Set the rebalance time to match the insight expiry self.rebalanceTime = Expiry.EndOfQuarter(algorithm.Time) insights = [] for sector in self.sectors: securities = self.sectors[sector] sortedByROE = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.ROE.Value, reverse=True) sortedByPM = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.NetMargin.Value, reverse=True) sortedByPE = sorted(securities, key=lambda x: x.Fundamentals.ValuationRatios.PERatio, reverse=False) # Dictionary holding a dictionary of scores for each security in the sector scores = {} for security in securities: score = sum([sortedByROE.index(security), sortedByPM.index(security), sortedByPE.index(security)]) scores[security] = score # Add best 20% of each sector to longs set (minimum 1) length = max(int(len(scores)/5), 1) for security in sorted(scores.items(), key=lambda x: x[1], reverse=False)[:length]: symbol = security[0].Symbol # Use Expiry.EndOfQuarter in this case to match Universe, Alpha and PCM insights.append(Insight.Price(symbol, Expiry.EndOfQuarter, InsightDirection.Up)) return insights def OnSecuritiesChanged(self, algorithm, changes): '''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''' # Remove security from sector set for security in changes.RemovedSecurities: for sector in self.sectors: if security in self.sectors[sector]: self.sectors[sector].remove(security) # Add security to corresponding sector set for security in changes.AddedSecurities: sector = security.Fundamentals.AssetClassification.MorningstarSectorCode if sector not in self.sectors: self.sectors[sector] = set() self.sectors[sector].add(security)
#region imports from AlgorithmImports import * #endregion from AlphaModel import * class VerticalTachyonRegulators(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 1) self.SetEndDate(2021, 1, 1) self.SetCash(100000) # Universe selection self.month = 0 self.num_coarse = 500 self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) # Alpha Model self.AddAlpha(FundamentalFactorAlphaModel()) # Portfolio construction model self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(self.IsRebalanceDue)) # Risk model self.SetRiskManagement(NullRiskManagementModel()) # Execution model self.SetExecution(ImmediateExecutionModel()) # Share the same rebalance function for Universe and PCM for clarity def IsRebalanceDue(self, time): # Rebalance on the first day of the Quarter if time.month == self.month or time.month not in [1, 4, 7, 10]: return None self.month = time.month return time def CoarseSelectionFunction(self, coarse): # If not time to rebalance, keep the same universe if not self.IsRebalanceDue(self.Time): return Universe.Unchanged # Select only those with fundamental data and a sufficiently large price # Sort by top dollar volume: most liquid to least liquid selected = sorted([x for x in coarse if x.HasFundamentalData and x.Price > 5], key = lambda x: x.DollarVolume, reverse=True) return [x.Symbol for x in selected[:self.num_coarse]] def FineSelectionFunction(self, fine): # Filter the fine data for equities that IPO'd more than 5 years ago in selected sectors sectors = [ MorningstarSectorCode.FinancialServices, MorningstarSectorCode.RealEstate, MorningstarSectorCode.Healthcare, MorningstarSectorCode.Utilities, MorningstarSectorCode.Technology] filtered_fine = [x.Symbol for x in fine if x.SecurityReference.IPODate + timedelta(365*5) < self.Time and x.AssetClassification.MorningstarSectorCode in sectors and x.OperationRatios.ROE.Value > 0 and x.OperationRatios.NetMargin.Value > 0 and x.ValuationRatios.PERatio > 0] return filtered_fine