| Overall Statistics |
|
Total Trades 302 Average Win 0.29% Average Loss -0.25% Compounding Annual Return 28.248% Drawdown 4.600% Expectancy 0.225 Net Profit 8.746% Sharpe Ratio 2.239 Loss Rate 43% Win Rate 57% Profit-Loss Ratio 1.14 Alpha 0.461 Beta -10.865 Annual Standard Deviation 0.111 Annual Variance 0.012 Information Ratio 2.062 Tracking Error 0.111 Treynor Ratio -0.023 Total Fees $667.58 |
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Algorithm.Framework")
from QuantConnect.Data.UniverseSelection import *
from QuantConnect.Indicators import ExponentialMovingAverage
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel
import decimal as d
class UniverseSelectionModel(FundamentalUniverseSelectionModel):
def __init__(self,
universeCount = 20,
universeSettings = None,
securityInitializer = None):
super().__init__(False, universeSettings, securityInitializer)
self.universeCount = universeCount
self.averages = {}
def SelectCoarse(self, algorithm, coarse):
# We are going to use a dictionary to refer the object that will keep the moving averages
for cf in coarse:
if cf.Symbol not in self.averages:
self.averages[cf.Symbol] = SymbolData(cf.Symbol)
# Updates the SymbolData object with price and volume
avg = self.averages[cf.Symbol]
avg.update(cf.EndTime, cf.Price, cf.Volume)
# Filter the values of the dict based on criteria
# In this case, we only want up-trending securities
filtered_values = list(filter(lambda x: x.is_uptrend, self.averages.values()))
# Sort the values of the dict: we want those with greater DollarVolume
sorted_values = sorted(filtered_values, key = lambda x: x.volume, reverse = True)
return [ x.symbol for x in sorted_values[:self.universeCount] ]
# class used to improve readability of the coarse selection function
class SymbolData:
def __init__(self, symbol):
self.symbol = symbol
self.priceSMA = SimpleMovingAverage(10)
self.volSMA = SimpleMovingAverage(50)
self.priceWin = RollingWindow[float](5)
self.is_uptrend = False
def update(self, time, price, volume):
self.price = price
self.volume = volume
self.priceWin.Add(price)
self.priceSMA.Update(time, price)
self.volSMA.Update(time, volume)
if self.priceSMA.IsReady and self.volSMA.IsReady and self.priceWin.IsReady:
MAprice = self.priceSMA.Current.Value
MAvol = self.volSMA.Current.Value
# Here we can add the criteria for our universe
# current price > 10-days moving average price
# current volume > 10-days moving average volume
# current price > yesterday's close
# current price > 10-days maximum price
self.is_uptrend = price > 20 and price > MAprice and volume > MAvol and price > self.priceWin[1] and price > max(self.priceWin)from UniverseSelection import UniverseSelectionModel
from Alphas.ConstantAlphaModel import ConstantAlphaModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
from Risk.NullRiskManagementModel import NullRiskManagementModel
class BasicTemplateFrameworkAlgorithm(QCAlgorithmFramework):
def Initialize(self):
# Set requested data resolution
self.UniverseSettings.Resolution = Resolution.Daily
self.SetStartDate(2018, 6, 1) # Set Start Date
self.SetEndDate(2018, 10, 1) # Set End Date
self.SetCash(100000) # Set Strategy Cash
count = 5
self.SetUniverseSelection(UniverseSelectionModel(universeCount = count))
self.SetAlpha(ConstantAlphaModel(InsightType.Price, InsightDirection.Up, TimeSpan.FromMinutes(20), 0.025, None))
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.SetExecution(ImmediateExecutionModel())
self.SetRiskManagement(NullRiskManagementModel())
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
self.Debug("Purchased Stock: {0}".format(orderEvent.Symbol))
#for x in sorted_values[:self.universeCount]:
#self.Log('symbol: ' + str(x.symbol.Value)
#+ ' close price: ' + str(x.price)
#+ ' volume: ' + str(x.volume)
#+ ' mean price: ' + str(x.priceSMA.Current.Value)
#+ ' mean vol: ' + str(x.volSMA.Current.Value))
def OnEndOfDay(self):
self.Liquidate()from clr import AddReference
AddReference("QuantConnect.Algorithm.Framework")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")
from QuantConnect import *
from QuantConnect.Indicators import *
from QuantConnect.Algorithm.Framework.Alphas import *
from datetime import timedelta
class HistoricalReturnsAlphaModel(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 > 0: direction = InsightDirection.Up
if magnitude < 0: direction = InsightDirection.Down
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.ROC = RateOfChange('{}.ROC({})'.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.ROC, 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.ROC.Update(tuple.Index, tuple.close)
@property
def Return(self):
return float(self.ROC.Current.Value)
@property
def CanEmit(self):
if self.previous == self.ROC.Samples:
return False
self.previous = self.ROC.Samples
return self.ROC.IsReady
def __str__(self, **kwargs):
return '{}: {:.2%}'.format(self.ROC.Name, (1 + self.Return)**252 - 1)