Overall Statistics
Total Trades
776
Average Win
0.18%
Average Loss
-0.17%
Compounding Annual Return
4.849%
Drawdown
8.900%
Expectancy
0.021
Net Profit
1.257%
Sharpe Ratio
0.345
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
1.05
Alpha
0.399
Beta
-21.515
Annual Standard Deviation
0.139
Annual Variance
0.019
Information Ratio
0.228
Tracking Error
0.139
Treynor Ratio
-0.002
Total Fees
$8189.95
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm.Framework")

from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from System.Collections.Generic import List
from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTarget
from QuantConnect.Algorithm.Framework.Risk import RiskManagementModel


class EmaCrossUniverseSelectionAlgorithm(QCAlgorithm):

    def Initialize(self):
        '''If the filter returns nothing, keep 60% invested in SPY.  Maximium drawdown allowed is 2.5% per investment, check every minute.  Liquidate end of day to avoid overnight risk.
            Filter stocks such that all entries are liquid, breaking out, and of the selected breakouts the top 24 breaking out the hardest. '''
        
        self.SetStartDate(2019,3,1)  #Set Start Date
        self.SetEndDate(datetime.now().date() - timedelta(1)) #Set End Date
        #self.SetEndDate(2019,1,1)    #Set End Date
        self.SetCash(1000000)      #Set Strategy Cash

        self.UniverseSettings.Resolution = Resolution.Hour

        self.marginRemaining = self.Portfolio.MarginRemaining
        self.orderTiming = 5
        self.averages = { };

        # this add universe method accepts two parameters:
        # - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol>
        self.AddEquity("SPY", Resolution.Hour)   
        self.AddUniverse(self.CoarseSelectionFunction)
            
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", self.orderTiming), self.BuyFunc)
        self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SPY", self.orderTiming), self.SellFunc) 
        
        self.SetWarmUp(16)
        
#OnData 
    def OnData(self, data):
        '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.'''
        '''Arguments:
            data: Slice object keyed by symbol containing the stock data'''
        
        #Maintain weight proportion based on active securities in the universe    
        self.weighting = 1.0 / (self.ActiveSecurities.Count+1)
        

#Universe Filter            
    # sort the data by volume and price, apply the moving average crossver, and take the top 24 sorted results based on breakout magnitude
    def CoarseSelectionFunction(self, coarse):
        
        filtered = [ x for x in coarse if (x.Volume > 1000000 and x.Price > 15) ]  
        
        # We are going to use a dictionary to refer the object that will keep the moving averages
        for cf in filtered:
            if cf.Symbol not in self.averages:
                self.averages[cf.Symbol] = SymbolData(cf.Symbol)

            # Updates the SymbolData object with current EOD price
            avg = self.averages[cf.Symbol]
            avg.update(cf.EndTime, cf.AdjustedPrice)

        # Filter the values of the dict: we only want up-trending securities
        values = list(filter(lambda x: x.is_uptrend, self.averages.values()))

        # Sorts the values of the dict: we want those with greater difference between the moving averages
        values.sort(key=lambda x: x.scale, reverse=True)
            
        # we need to return only the symbol objects
        return [ x.symbol for x in values[:10] ]
        

    # this event fires whenever we have changes to our universe
    def OnSecuritiesChanged(self, changes):
        self.changes = changes
        
        # liquidate removed securities
        for security in changes.RemovedSecurities:
            if security.Invested:
                self.Liquidate(security.Symbol)
                
            self.RemoveSecurity(security.Symbol)
            
            
#Buy       
    def BuyFunc(self):
        
        #Used to control leverage
        self.OpenOrders = self.Transactions.GetOpenOrders()
        
        for security in self.Securities.Values:
            if not self.Securities[security.Symbol].Invested and self.Securities[security.Symbol] not in self.OpenOrders:
                self.SetHoldings(security.Symbol, self.weighting) 
                
                
#Sell
    def SellFunc(self):
        
        self.Liquidate()
            
        return    
    

#EMA Crossover Class
class SymbolData(object):
    def __init__(self, symbol):
        self.symbol = symbol
        self.fast = ExponentialMovingAverage(5)
        self.slow = ExponentialMovingAverage(25)
        self.is_uptrend = False
        self.scale = 0

    def update(self, time, value):
        if self.fast.Update(time, value) and self.slow.Update(time, value):
            fast = self.fast.Current.Value
            slow = self.slow.Current.Value
            self.is_uptrend = ((fast / slow)) > 1.00

        if self.is_uptrend:
            self.scale = (fast - slow) / ((fast + slow) / 2.0)