Overall Statistics |
Total Trades 183 Average Win 11.81% Average Loss -6.02% Compounding Annual Return 81.891% Drawdown 40.600% Expectancy 0.879 Net Profit 5391.519% Sharpe Ratio 1.447 Probabilistic Sharpe Ratio 68.033% Loss Rate 37% Win Rate 63% Profit-Loss Ratio 1.96 Alpha 0.651 Beta 0.029 Annual Standard Deviation 0.452 Annual Variance 0.204 Information Ratio 1.144 Tracking Error 0.474 Treynor Ratio 22.291 Total Fees $366607.31 Estimated Strategy Capacity $6500000.00 Lowest Capacity Asset RIVN XTDCDCDRI6HX |
class MyHistoricalReturnsAlphaModel(AlphaModel): '''Uses Historical returns to create insights.''' def __init__(self, *args, **kwargs): '''Initializes a new default instance of the HistoricalReturnsAlphaModel class. Args: lookback(int): Historical return lookback period resolution: The resolution of historical data''' self.lookback = kwargs['lookback'] if 'lookback' in kwargs else 1 self.resolution = kwargs['resolution'] if 'resolution' in kwargs else Resolution.Daily self.predictionInterval = Time.Multiply(Extensions.ToTimeSpan(self.resolution), self.lookback) self.symbolDataBySymbol = {} 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: The new insights generated''' insights = [] for symbol, symbolData in self.symbolDataBySymbol.items(): if symbolData.CanEmit: direction = InsightDirection.Flat magnitude = symbolData.Return if magnitude > .10: direction = InsightDirection.Down if magnitude < -.10: direction = InsightDirection.Up insights.append(Insight.Price(symbol, self.predictionInterval, direction, magnitude, None)) 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''' # clean up data for removed securities for removed in changes.RemovedSecurities: symbolData = self.symbolDataBySymbol.pop(removed.Symbol, None) if symbolData is not None: symbolData.RemoveConsolidators(algorithm) # initialize data for added securities symbols = [ x.Symbol for x in changes.AddedSecurities ] history = algorithm.History(symbols, self.lookback, self.resolution) if history.empty: return tickers = history.index.levels[0] for ticker in tickers: symbol = SymbolCache.GetSymbol(ticker) if symbol not in self.symbolDataBySymbol: symbolData = SymbolData(symbol, self.lookback) self.symbolDataBySymbol[symbol] = symbolData symbolData.RegisterIndicators(algorithm, self.resolution) symbolData.WarmUpIndicators(history.loc[ticker]) class SymbolData: '''Contains data specific to a symbol required by this model''' def __init__(self, symbol, lookback): self.Symbol = symbol self.ROCP = RateOfChange('{}.ROCP({})'.format(symbol, lookback), lookback) self.Consolidator = None self.previous = 0 def RegisterIndicators(self, algorithm, resolution): self.Consolidator = algorithm.ResolveConsolidator(self.Symbol, resolution) algorithm.RegisterIndicator(self.Symbol, self.ROCP, self.Consolidator) def RemoveConsolidators(self, algorithm): if self.Consolidator is not None: algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.Consolidator) def WarmUpIndicators(self, history): for tuple in history.itertuples(): self.ROCP.Update(tuple.Index, tuple.close) @property def Return(self): return float(self.ROCP.Current.Value) @property def CanEmit(self): if self.previous == self.ROCP.Samples: return False self.previous = self.ROCP.Samples return self.ROCP.IsReady def __str__(self, **kwargs): return '{}: {:.2%}'.format(self.ROC.Name, (1 + self.Return)**252 - 1)
class BracketRiskModel(RiskManagementModel): '''Provides an implementation of IRiskManagementModel that limits the maximum possible loss measured from the highest unrealized profit''' def __init__(self, maximumDrawdownPercent = 0.05, maximumUnrealizedProfitPercent = 0.05): '''Initializes a new instance of the TrailingStopRiskManagementModel class Args: maximumDrawdownPercent: The maximum percentage drawdown allowed for algorithm portfolio compared with the highest unrealized profit, defaults to 5% drawdown''' self.maximumDrawdownPercent = -abs(maximumDrawdownPercent) self.trailingHighs = dict() self.maximumUnrealizedProfitPercent = abs(maximumUnrealizedProfitPercent) def ManageRisk(self, algorithm, targets): '''Manages the algorithm's risk at each time step Args: algorithm: The algorithm instance targets: The current portfolio targets to be assessed for risk''' riskAdjustedTargets = list() for kvp in algorithm.Securities: symbol = kvp.Key security = kvp.Value # Remove if not invested if not security.Invested: self.trailingHighs.pop(symbol, None) continue pnl = security.Holdings.UnrealizedProfitPercent if pnl > self.maximumUnrealizedProfitPercent: # liquidate algorithm.Debug("Profit Taken") algorithm.Log("Profit Taken") riskAdjustedTargets.append(PortfolioTarget(security.Symbol, 0)) return riskAdjustedTargets # Add newly invested securities if symbol not in self.trailingHighs: self.trailingHighs[symbol] = security.Holdings.AveragePrice # Set to average holding cost continue # Check for new highs and update - set to tradebar high if self.trailingHighs[symbol] < security.High: self.trailingHighs[symbol] = security.High continue # Check for securities past the drawdown limit securityHigh = self.trailingHighs[symbol] drawdown = (security.Low / securityHigh) - 1 if drawdown < self.maximumDrawdownPercent: # liquidate algorithm.Debug("Losses Taken") algorithm.Log("Losses Taken") riskAdjustedTargets.append(PortfolioTarget(symbol, 0)) return riskAdjustedTargets
from Execution.ImmediateExecutionModel import ImmediateExecutionModel from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel from AlgorithmImports import * from risk import BracketRiskModel from alpha import MyHistoricalReturnsAlphaModel class TwoWeekMagnitudeEffectOnCurrentReturns(QCAlgorithm): def Initialize(self): self.SetStartDate(2015, 5, 8) # Set Start Date self.SetCash(1000000) # Set Strategy Cash self.AddAlpha(MyHistoricalReturnsAlphaModel(14, Resolution.Daily)) self.SetExecution(ImmediateExecutionModel()) self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) # uses universe selection to build a universe with 150 high volume stocks that have earnings within 3 months and low ATR(Average True Range) which signals low volatility self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction)) self.UniverseSettings.Resolution = Resolution.Daily # makes sure the universe is on a daily rebalance period self.period = 14 self.up_value = 0.10 self.down_value = 0.05 self.SetRiskManagement(BracketRiskModel(self.down_value, self.up_value)) self.SetBrokerageModel(AlphaStreamsBrokerageModel()) def CoarseSelectionFunction(self, coarse): # sort the universe by high dollar volume sortedByDollarVolume = sorted(coarse, key=lambda x: x.DollarVolume, reverse=True) # filter any assets that are above $5.00 and have a trading volume of over 10,000,000. then return the top 275 filtered = [ x.Symbol for x in sortedByDollarVolume if x.HasFundamentalData and x.Price >= 5 and x.DollarVolume > 10000000 ][:275] return [ x for x in filtered ] # passes to FineSelectionFunction() def FineSelectionFunction(self, fine): # filters assets to return the top 150 that have earnings dates within 3 months filteredByDates = [ x for x in fine if x.EarningReports.FileDate.year == self.Time.year and x.EarningReports.FileDate.month - self.Time.month <= 3 ][:150] # gives us our atr indicator values for symbol in filteredByDates: self.AddSecurity(symbol.Symbol, Resolution.Daily) history = self.History([symbol.Symbol], self.period, Resolution.Daily) atr = self.ATR(symbol.Symbol, self.period, Resolution.Daily) for bar in history.itertuples(): tradebar = TradeBar(bar.Index[1], symbol, bar.open, bar.high, bar.low, bar.close, bar.volume) atr.Update(tradebar) symbol.atr = atr.Current.Value # use this to sort by low to high ATR values sortedByVolatility = sorted(filteredByDates, key=lambda x: abs(x.atr), reverse=False) return [ x.Symbol for x in sortedByVolatility ] # gives us our universe