Overall Statistics
Total Trades
1634
Average Win
0.08%
Average Loss
-0.04%
Compounding Annual Return
42.377%
Drawdown
11.500%
Expectancy
1.028
Net Profit
42.377%
Sharpe Ratio
1.722
Probabilistic Sharpe Ratio
78.601%
Loss Rate
38%
Win Rate
62%
Profit-Loss Ratio
2.28
Alpha
0.357
Beta
-0.227
Annual Standard Deviation
0.177
Annual Variance
0.031
Information Ratio
0.344
Tracking Error
0.223
Treynor Ratio
-1.342
Total Fees
$1695.11
""" 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(2019, 1, 1)  # Set Start Date
        self.SetEndDate(2020, 1, 1)
        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)
        return [f.Symbol for f in sortedByYields][:self.selectionSize]
        
class LongShareHolderYieldAlphaModel(AlphaModel):

    def __init__(self):
        self.lastMonth = -1
        # Average number of trading days per month
        self.signalTimeDelta = timedelta(days = 21)
        self.symbols = []

    def Update(self, algorithm, data):
        
        insights = []
        
        # Skip if we have already run this month.
        if self.lastMonth == algorithm.Time.month:
            return insights
            
        self.lastMonth = algorithm.Time.month 
  
        for symbol in self.symbols:
            insights.append(Insight.Price(symbol, self.signalTimeDelta, InsightDirection.Up))
        
        return insights
        
    def OnSecuritiesChanged(self, algorithm, changes):
        
        for security in changes.AddedSecurities:
            symbol = security.Symbol
            if symbol not in self.symbols:
                self.symbols.append(security.Symbol)
                
        for security in changes.RemovedSecurities:
            self.symbols.remove(security.Symbol)