Created
March 13, 2020 20:15
-
-
Save ipl31/10a3fa9622fe1fbf3ea1d8e360130e1e to your computer and use it in GitHub Desktop.
Quant Connect Shareholder Yield Strategy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" Shareholder Yield Algo. | |
This is for learning the QuantConnect Algo Framework. I don't expect it to be useful outside of that. | |
The goal of this algo is: | |
- create a universe of liquid stocks with the best share holder yield (dividends + buybacks) relative to the market. | |
- buy an equal weighted portfolio of these stocks. | |
- rebalance monthly based on share holder yield. | |
Classes: | |
- ShareHolderYieldAlgo() is the main class that configures and instantiates the QuantConnect algo framework. | |
- LiquidTotalYieldUniverseSelectionModel() is a class which creates a liquid universe of stocks ranking by | |
dollar volume, then filters the list down for the top X yielders based on share holder yield. | |
- LongShareHolderYieldAlphaModel() is an alpha model class which emits buy and sell signals based on changes | |
to the universe selection. | |
""" | |
from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel | |
class ShareHolderYieldAlgo(QCAlgorithm): | |
def Initialize(self): | |
self.SetStartDate(2005, 1, 1) # Set Start Date | |
self.SetCash(100000) # Set Strategy Cash | |
self.UniverseSettings.Resolution = Resolution.Daily | |
self.AddUniverseSelection(LiquidTotalYieldUniverseSelectionModel()) | |
self.AddAlpha(LongShareHolderYieldAlphaModel()) | |
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) | |
self.SetExecution(ImmediateExecutionModel()) | |
class LiquidTotalYieldUniverseSelectionModel(FundamentalUniverseSelectionModel): | |
def __init__(self): | |
super().__init__(True, None, None) | |
self.lastMonth = -1 | |
# Use this to determined how many stocks we take off the dollar volume sorted list. | |
self.dollarVolumeListSize = 200 | |
# Number of securities to select and return from universe. | |
self.selectionSize = 10 | |
def SelectCoarse(self, algorithm, coarse): | |
if self.lastMonth == algorithm.Time.month: | |
return Universe.Unchanged | |
self.lastMonth = algorithm.Time.month | |
sortedByDollarVolume = sorted([x for x in coarse if x.HasFundamentalData], | |
key=lambda x: x.DollarVolume, reverse=True) | |
return [x.Symbol for x in sortedByDollarVolume[:self.dollarVolumeListSize]] | |
def SelectFine(self, algorithm, fine): | |
sortedByYields = sorted(fine, key=lambda f: f.ValuationRatios.TotalYield, reverse=True) | |
universe = sortedByYields[:self.selectionSize] | |
return [f.Symbol for f in universe] | |
class LongShareHolderYieldAlphaModel(AlphaModel): | |
def __init__(self): | |
self.insights = [] | |
self.lastMonth = -1 | |
def Update(self, algorithm, data): | |
if self.lastMonth == algorithm.Time.month: | |
return [] | |
self.lastMonth = algorithm.Time.month | |
# should I return an empty list if there have been no changes? | |
# Or is it ok to return the same insights again? | |
return self.insights | |
def OnSecuritiesChanged(self, algorithm, changes): | |
added = [x for x in changes.AddedSecurities] | |
removed = [x for x in changes.RemovedSecurities] | |
changedInsights = [] | |
for security in added: | |
changedInsights.append(Insight.Price(security.Symbol, timedelta(28), InsightDirection.Up)) | |
for security in removed: | |
changedInsights.append(Insight.Price(security.Symbol, timedelta(28), InsightDirection.Flat)) | |
self.insights = changedInsights |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment