Overall Statistics |
Total Trades 644 Average Win 0.10% Average Loss -0.12% Compounding Annual Return 5.278% Drawdown 7.500% Expectancy 0.055 Net Profit 5.293% Sharpe Ratio 0.564 Probabilistic Sharpe Ratio 29.639% Loss Rate 41% Win Rate 59% Profit-Loss Ratio 0.80 Alpha 0.041 Beta -0.011 Annual Standard Deviation 0.069 Annual Variance 0.005 Information Ratio -0.446 Tracking Error 0.289 Treynor Ratio -3.491 Total Fees $644.00 Estimated Strategy Capacity $73000000.00 Lowest Capacity Asset CSIQ TNII135YAI5H |
#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_pos = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.ROE.Value, reverse=True) sortedByPM_pos = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.NetMargin.Value, reverse=True) sortedByPE_pos = sorted(securities, key=lambda x: x.Fundamentals.ValuationRatios.PERatio, reverse=True) sortedByROE_neg = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.ROE.Value, reverse=False) sortedByPM_neg = sorted(securities, key=lambda x: x.Fundamentals.OperationRatios.NetMargin.Value, reverse=False) sortedByPE_neg = sorted(securities, key=lambda x: x.Fundamentals.ValuationRatios.PERatio, reverse=False) # Dictionary holding a dictionary of scores for each security in the sector scores_pos = {} for security in securities: score = sum([sortedByROE_pos.index(security), sortedByPM_pos.index(security), sortedByPE_pos.index(security)]) scores_pos[security] = score scores_neg = {} for security in securities: score = sum([sortedByROE_neg.index(security), sortedByPM_neg.index(security), sortedByPE_neg.index(security)]) scores_neg[security] = score # Add best 20% of each sector to longs set (minimum 1) length = max(int(len(scores_pos)/5), 1) for security in sorted(scores_pos.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)) length = max(int(len(scores_neg)/5), 1) for security in sorted(scores_neg.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.Down)) 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] return filtered_fine