Overall Statistics
Total Trades
3385
Average Win
0.38%
Average Loss
-0.27%
Compounding Annual Return
22.178%
Drawdown
35.100%
Expectancy
0.167
Net Profit
118.953%
Sharpe Ratio
1.188
Probabilistic Sharpe Ratio
57.216%
Loss Rate
52%
Win Rate
48%
Profit-Loss Ratio
1.41
Alpha
0.209
Beta
-0.1
Annual Standard Deviation
0.164
Annual Variance
0.027
Information Ratio
0.221
Tracking Error
0.257
Treynor Ratio
-1.948
Total Fees
$0.00
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
class VentralModulatedCompensator(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2016, 11, 2)  # Set Start Date
        self.SetCash(3500)  # Set Strategy Cash
        # self.AddEquity("SPY", Resolution.Minute)
        self.SetExecution(ImmediateExecutionModel())
        self.UniverseSettings.Leverage = 1
        self.UniverseSettings.Resolution = Resolution.Daily
        self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction, None, None))
        self.SetSecurityInitializer(self.CustomSecurityInitializer)
        #self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
        #self.Securities[AddedSecurities].FeeModel = ConstantFeeModel(0)
        #self.ActiveSecurities.FeeModel = ConstantFeeModel(0)
        self.UniverseSettings.MinimumTimeInUniverse = 10
        self.averages = { }
        
    def CustomSecurityInitializer(self, security):
        security.SetFeeModel(CustomFeeModel())    
    
    def CoarseSelectionFunction(self, coarse):
        selected = []
        universe = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True)  
        universe = [c for c in universe if c.Price > 10]
        
        for coarse in universe:  
            symbol = coarse.Symbol
            
            if symbol not in self.averages:
                # 1. Call history to get an array of 200 days of history data
                history = self.History(symbol, 31, Resolution.Daily)
                
                #2. Adjust SelectionData to pass in the history result
                self.averages[symbol] = SelectionData(history) 

            self.averages[symbol].update(self.Time, coarse.AdjustedPrice)
            
            if  self.averages[symbol].is_ready() and self.averages[symbol].fast > self.averages[symbol].slow:
                selected.append(symbol)
        
        return selected
        
    def FineSelectionFunction(self, fine):
    
        #sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.NormalizedPEGatio, reverse=False)
        #sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.EVToEBITDA.latest > 0, reverse=False)
        #sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.PricetoEBITDA > 0, reverse=False)
        #sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.PCFRatio,reverse=False)
        #sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.CashReturn, reverse=True)
        #sorted_by_roic = sorted(fine, key=lambda x: x.OperationRatios.ROIC.ThreeMonths, reverse=True)[:150]
        
        sorted_by_ev = sorted(fine, key=lambda x: x.ValuationRatios.PricetoEBITDA, reverse=True)[:100]
        sorted_by_roic = sorted(sorted_by_ev, key=lambda x: x.OperationRatios.ROIC.ThreeMonths, reverse=True)[:50]
        sorted_by_bookvalue = sorted(sorted_by_roic, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse = False)[:15]
        
        #CashReturn = 8.98%, 4.2 alpha
        #EVToRevenue3YrAvg = Not worth pursuing?
        #EVToFCF3YrAvg
        #PBRatio3YrAvg = 9.15%, 4.8 alpha
        #EarningYield
        #NormalizedPEGatio
        #PB/Cash Return = 1.235, alpha = .177
        #ROIC/PBRATIO3YRAVG = 88.78%, alpha = .194
  
        return [x.Symbol for x in sorted_by_bookvalue]
        
        #sorted_by_cash = sorted(sorted_by_roic, key=lambda x: x.ValuationRatios.CashReturn, reverse=True)
        
        #return [x.Symbol for x in sorted_by_cash[:60]]
        
        #sorted_by_pb = sorted(sorted_by_roic, key=lambda x: x.ValuationRatios.PBRatio3YrAvg, reverse=False)
        
        #return [x.Symbol for x in sorted_by_pb[:15]]
        
    def OnSecuritiesChanged(self, changes):
        self.changes = changes
        self.Log(f"OnSecuritiesChanged({self.Time}):: {changes}")
        
        for security in self.changes.RemovedSecurities:
            if security.Invested:
                self.Liquidate(security.Symbol)
        
        for security in self.changes.AddedSecurities:
            self.SetHoldings(security.Symbol, 0.04)
            
class CustomFeeModel:
    def GetOrderFee(self, parameters):
        return OrderFee(CashAmount(0, 'USD'))
        
class SelectionData():
    #3. Update the constructor to accept a history array
    def __init__(self, history):
        self.slow = SimpleMovingAverage(30)
        self.fast = SimpleMovingAverage(1)
        #4. Loop over the history data and update the indicators
        for bar in history.itertuples():
            self.fast.Update(bar.Index[1], bar.close)
            self.slow.Update(bar.Index[1], bar.close)
    
    def is_ready(self):
        return self.slow.IsReady and self.fast.IsReady
    
    def update(self, time, price):
        self.fast.Update(time, price)
        self.slow.Update(time, price)