Overall Statistics |
Total Trades 5174 Average Win 0.22% Average Loss -0.19% Compounding Annual Return 3.611% Drawdown 17.600% Expectancy 0.061 Net Profit 32.838% Sharpe Ratio 0.437 Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.17 Alpha 0.038 Beta -0.046 Annual Standard Deviation 0.077 Annual Variance 0.006 Information Ratio -0.412 Tracking Error 0.16 Treynor Ratio -0.728 Total Fees $31578.95 |
from Execution.ImmediateExecutionModel import ImmediateExecutionModel from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel class VerticalTachyonRegulators(QCAlgorithm): def Initialize(self): self.SetStartDate(2011, 1, 1) # Set Start Date self.SetEndDate(2019, 1, 1) #self.SetStartDate(2019, 1, 1) #self.SetEndDate(2019, 8, 16) self.SetCash(1000000) # Set Strategy Cash ## Set execution model to mimic market orders self.SetExecution(ImmediateExecutionModel()) ## set equal weighting protfolio construction to mimic intital algorithm weighting scheme self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) ## Helper variables for universe selection and checking for conditions to update only at the ## beginning of each month self.num_coarse = 250 self.num_fine = 10 self.lastMonth = -1 ## Coarse/Fine universe selection model self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) ## Custom Alpha model where we can perform the trading logic and the filtering done based on Fundamental Data self.AddAlpha(FundamentalFactorAlphaModel(self.num_fine)) def CoarseSelectionFunction(self, coarse): ## If not time to rebalance, keep the same universe if self.Time.month == self.lastMonth: return Universe.Unchanged ## Else reassign the month variable and filter self.lastMonth = self.Time.month ## 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 [i.Symbol for i in selected[:self.num_coarse]] def FineSelectionFunction(self, fine): ## Filter the fine data for equities with non-zero/non-null Value, ## 1-month Price Change, and Book Value per Share filtered_fine = [x.Symbol for x in fine if x.OperationRatios.OperationMargin.Value > 0 and x.ValuationRatios.PriceChange1M > 0 and x.ValuationRatios.BookValuePerShare > 0] return filtered_fine class FundamentalFactorAlphaModel(AlphaModel): def __init__(self, num_fine): ## Initialize the various variables/helpers we'll need self.lastMonth = -1 self.longs = [] self.shorts = [] self.num_fine = num_fine def Update(self, algorithm, data): ## Return no insights if it's not the month to rebalance if algorithm.Time.month == self.lastMonth: return [] self.lastMonth = algorithm.Time.month ## Create empty list of insights insights = [] ## We will liquidate any securities we're still invested in that we don't want to hold a position ## for the next month for kvp in algorithm.Portfolio: holding = kvp.Value symbol = holding.Symbol if holding.Invested and symbol not in self.longs and symbol not in self.shorts: insights.append(Insight(symbol, timedelta(30), InsightType.Price, InsightDirection.Flat, None, None)) ## Emit Up (buy) Insights for our desired long positions for symbol in self.longs: insights.append(Insight(symbol, timedelta(30), InsightType.Price, InsightDirection.Up, 0.01, None)) ## Emit Down (sell) Insights for our desired short positions for symbol in self.shorts: insights.append(Insight(symbol, timedelta(30), InsightType.Price, InsightDirection.Down, 0.01, None)) return insights def OnSecuritiesChanged(self, algorithm, changes): ## Get the securities added = [x for x in changes.AddedSecurities] ## Perform filtering/sorting based on Value, Quality, and Momentum sortedByfactor1 = sorted(added, key=lambda x: x.Fundamentals.OperationRatios.OperationMargin.Value, reverse=True) sortedByfactor2 = sorted(added, key=lambda x: x.Fundamentals.ValuationRatios.PriceChange1M, reverse=True) sortedByfactor3 = sorted(added, key=lambda x: x.Fundamentals.ValuationRatios.BookValuePerShare, reverse=True) ## Create dictionary to store scores scores = {} ## Assign a score to each stock. for i,ele in enumerate(sortedByfactor1): rank1 = i rank2 = sortedByfactor2.index(ele) rank3 = sortedByfactor3.index(ele) scores[ele] = rank1*0.2 + rank2*0.4 + rank3*0.4 ## Sort the stocks by their scores sorted_stock = sorted(scores.items(), key=lambda d:d[1],reverse=False) sorted_symbol = [x[0] for x in sorted_stock] ## Sort the top stocks into the long_list and the bottom ones into the short_list self.longs = [x.Symbol for x in sorted_symbol[:self.num_fine]] self.shorts = [x.Symbol for x in sorted_symbol[-self.num_fine:]] algorithm.Log('Long: ' + ', '.join(sorted([x.Value for x in self.longs]))) algorithm.Log('Short: ' + ', '.join(sorted([x.Value for x in self.shorts]))) from pytz import utc from itertools import groupby from datetime import datetime, timedelta UTCMIN = datetime.min.replace(tzinfo=utc) class EqualWeightingPortfolioConstructionModel(PortfolioConstructionModel): '''Provides an implementation of IPortfolioConstructionModel that gives equal weighting to all securities. The target percent holdings of each security is 1/N where N is the number of securities. For insights of direction InsightDirection.Up, long targets are returned and for insights of direction InsightDirection.Down, short targets are returned.''' def __init__(self, resolution = Resolution.Daily): '''Initialize a new instance of EqualWeightingPortfolioConstructionModel Args: resolution: Rebalancing frequency''' self.insightCollection = InsightCollection() self.removedSymbols = [] self.nextExpiryTime = UTCMIN self.rebalancingTime = UTCMIN self.rebalancingPeriod = TimeSpan.FromDays(30) def ShouldCreateTargetForInsight(self, insight): '''Method that will determine if the portfolio construction model should create a target for this insight Args: insight: The insight to create a target for''' return True def DetermineTargetPercent(self, activeInsights): '''Will determine the target percent for each insight Args: activeInsights: The active insights to generate a target for''' result = {} # give equal weighting to each security count = sum(x.Direction != InsightDirection.Flat for x in activeInsights) percent = 0 if count == 0 else 1.0 / count for insight in activeInsights: result[insight] = insight.Direction * percent return result def CreateTargets(self, algorithm, insights): '''Create portfolio targets from the specified insights Args: algorithm: The algorithm instance insights: The insights to create portfolio targets from Returns: An enumerable of portfolio targets to be sent to the execution model''' targets = [] if (algorithm.UtcTime <= self.nextExpiryTime and algorithm.UtcTime <= self.rebalancingTime and len(insights) == 0 and self.removedSymbols is None): return targets for insight in insights: if self.ShouldCreateTargetForInsight(insight): self.insightCollection.Add(insight) # Create flatten target for each security that was removed from the universe if self.removedSymbols is not None: universeDeselectionTargets = [ PortfolioTarget(symbol, 0) for symbol in self.removedSymbols ] targets.extend(universeDeselectionTargets) self.removedSymbols = None # Get insight that haven't expired of each symbol that is still in the universe activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime) # Get the last generated active insight for each symbol lastActiveInsights = [] for symbol, g in groupby(activeInsights, lambda x: x.Symbol): lastActiveInsights.append(sorted(g, key = lambda x: x.GeneratedTimeUtc)[-1]) # Determine target percent for the given insights percents = self.DetermineTargetPercent(lastActiveInsights) errorSymbols = {} for insight in lastActiveInsights: target = PortfolioTarget.Percent(algorithm, insight.Symbol, percents[insight]) if not target is None: targets.append(target) else: errorSymbols[insight.Symbol] = insight.Symbol # Get expired insights and create flatten targets for each symbol expiredInsights = self.insightCollection.RemoveExpiredInsights(algorithm.UtcTime) expiredTargets = [] for symbol, f in groupby(expiredInsights, lambda x: x.Symbol): if not self.insightCollection.HasActiveInsights(symbol, algorithm.UtcTime) and not symbol in errorSymbols: expiredTargets.append(PortfolioTarget(symbol, 0)) continue targets.extend(expiredTargets) self.nextExpiryTime = self.insightCollection.GetNextExpiryTime() if self.nextExpiryTime is None: self.nextExpiryTime = UTCMIN self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod return targets 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''' # Get removed symbol and invalidate them in the insight collection self.removedSymbols = [x.Symbol for x in changes.RemovedSecurities] self.insightCollection.Clear(self.removedSymbols)