| 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 riskAdjustedTargetsfrom 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