Overall Statistics
Total Trades
89
Average Win
0.64%
Average Loss
-0.46%
Compounding Annual Return
21.792%
Drawdown
9.700%
Expectancy
0.415
Net Profit
23.983%
Sharpe Ratio
1.581
Loss Rate
41%
Win Rate
59%
Profit-Loss Ratio
1.40
Alpha
0.089
Beta
5.874
Annual Standard Deviation
0.13
Annual Variance
0.017
Information Ratio
1.428
Tracking Error
0.13
Treynor Ratio
0.035
Total Fees
$127.16
# Transported to QuantConnect Lean Framework by Serge d'Adesky


from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from System.Collections.Generic import List
import decimal as d
import sys

VERBOSEMODE= True # set this to True to rough detailed error logging
VERBOSEMODE2=False # set this to True to see finer detailed error logging

class TripleGapDaytradeAlgorithm(QCAlgorithm):

    def Initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''

        self.SetStartDate(2016,1,1)  #Set Start Date
        self.SetEndDate(2017,2,1)    #Set End Date
        self.SetCash(100000)           #Set Strategy Cash

        self.UniverseSettings.Resolution = Resolution.Daily
        self.UniverseSettings.Leverage = 2

        self.symbols = [] # this will be our symbols to populate
        self.coarse_count = 10
        self.indicators = { }
        self.rebalance = True # whether or not to recalculate allocation. Default is True
        # this add symbols method accepts two parameters:
        # - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol>
        self.symbols = self.AddUniverse(self.CoarseSelectionFunction)
        self.securitiesRegistered = False # rist time we want to reregister all equities 
        self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
            
    def registerAll(self):
        if len(self.symbols) == 0: 
            self.securitiesRegistered = False
        
        # for some reason our symbols does not correctly register all symbols so test for that
        for symbol in self.symbols:
            if symbol is None:
                self.Log('initialize() unable to find symbol {} . Haling process'.format(symbol)  ) 
                return
            try:
               self.AddEquity(symbol,Resolution.Daily).Symbol
               if VERBOSEMODE2: self.Log('initialize() added equity in case not defined'.format(symbol)  ) 
               self.securitiesRegistered = True
            except:
               if VERBOSEMODE2: self.Log('unable to add equity in initialize() '.format(symbol)  ) 
            
        
    def OnData(self, data):
        try:
        # first have to make sure our symbols have populate once in CoarseSelectionFuncion
            if not self.securitiesRegistered:
                  self.registerAll()
                 
            # This updates the indicators at each data step(based on resolution)
            for symbol in self.symbols:
                    
                # is symbol iin Slice object? (do we even have data on this step for this asset)
                if not data.ContainsKey(symbol):
                    continue
                
                
                # 686 | 13:35:43: Runtime Error: Python.Runtime.PythonException: AttributeError : 'NoneType' object has no attribute 'Price'
                if data[symbol] is None:
                    continue
                # Does this slice have the price data we need at this moment?
                if data[symbol].Price is None:
                    continue
    
    
                # Either create a new indicator, or update one we already have
                if symbol not in self.indicators:
                    self.indicators[symbol] = SymbolData(symbol)
                               
                self.indicators[symbol].update(data[symbol])
                
                # We are warming up the indicators, cannot trade or other stuff
                if self.IsWarmingUp: continue
                
                
                # now you can use logic to trade, random example:
                #lowerband = self.indicators[symbol].bb10.LowerBand.Current.Value
                #upperband = self.indicators[symbol].bb10.UpperBand.Current.Value
    
                # Log the symbol, price & indicators. 
                self.Log("{0}\tPrice : {1:0.2f}\tUPPERBAND : {2:0.2f}\tLOWERBAND : {3:0.2f}".format(symbol, data[symbol].Price,upperband, lowerband))
        except:       
            self.Log('Last good line was {} . An unknown error to print indicators of bollinger upper and lower. {} '  .format(str() ,  str(sys.exc_info()[0] ))  )                                                                                          
        

    # sort the data by daily dollar volume and take the top 'NumberOfSymbols'
    def CoarseSelectionFunction(self, coarse):
        try:    
            # if the rebalance flag is not true, return null list to save time.
            if self.rebalance == False:
                return self.symbols
                
            # make symbols selection once a month
            # drop stocks which have no fundamental data or have too low prices
            selected = [x for x in coarse if x.HasFundamentalData and float(x.Price) > 5]
                           
                
            sortedByDollarVolume = sorted(selected, key=lambda x: x.DollarVolume, reverse=True) 
            #self.Log('in CoarseSelectionFunction values at after is_sortedByDollarVolume  of coarse have count of  {}'.format(len(sortedByDollarVolume)))
            
                        

            # Save the top X (self.coarse_count)  symbols for rebalance flag not 1
            self.symbols = [ x.Symbol for x in sortedByDollarVolume[:self.coarse_count] ]
            self.Log('in CoarseSelectionFunction selected count is {} _sortedByDollarVolume count is {} '.format(len(selected), len(self.symbols)))
                        
    
            # We are going to use a dictionary to refer to the object that will keep the moving averages 
            # so we can load them once now but access them during the day
            
            
            for cf in selected:
    
                if cf.Symbol not in self.indicators:
                    self.indicators[cf.Symbol] = SymbolData(cf.Symbol)
    
                # Updates the SymbolData object with current EOD price
                self.indicators[cf.Symbol].update(cf)
                
            # Filter the values of the dict: wait for indicator to be ready
            values = list(filter(lambda x: x.is_ready, self.indicators.values()))
            self.Log('values at after is_ready filter of coarse have count of  {}'.format(len(values)))
            
            # Sorts the values of the dict: we want those with greater difference between the moving averages
            values.sort(key=lambda x: x.vol30.Current.Value, reverse=True)
            self.Log('values at after vol30  filter of coarse have count of  {}'.format(len(values)))
            for x in values[:self.coarse_count]:
                self.Log('symbol: ' + str(x.symbol.Value) + '  mean vol: ' + str(x.vol30.Current.Value) + '  mean price: ' + str(x.sma_short.Current.Value))
                
            
            # we need to return only the symbol objects
            return [ x.symbol for x in values[:self.coarse_count] ]
        except:       
            self.Log('in CoarseSelectionFunction   An unknown error {} '  .format(  str(sys.exc_info()[0] ))  )                                                                                          

    # this event fires whenever we have changes to our symbols
    def OnSecuritiesChanged(self, changes):
        # liquidate removed securities
        for security in changes.RemovedSecurities:
            if security.Invested:
                self.Liquidate(security.Symbol)

        # we want 20% allocation in each security in our symbols
        for security in changes.AddedSecurities:
            self.SetHoldings(security.Symbol, 0.1)



       
class SymbolData(object):
    def __init__(self, symbol):
        try:

            self.qc = QCAlgorithm()
    
            self.symbol = symbol
            self.sma_short = SimpleMovingAverage(30)
            self.vol30 = SimpleMovingAverage(30)
            self.sma_medium = SimpleMovingAverage(55)
            self.sma_long = SimpleMovingAverage(100)

            self.is_ready = False

        except:
            self.qc.Log('In SymbolData.__init__ An unknown error occurred in SymbolData loading Algorithm library'), sys.exc_info()[0]    
            
    def update(self, value):
        self.is_ready = self.sma_short.Update(value.EndTime, value.Price) and self.vol30.Update(value.EndTime, value.DollarVolume)     
 
 
    def get_history_by_symbol(self,symbol,period,resolution=None,attr=None):
        newDF = pd.DataFrame() #creates a new dataframe that's empty
        try:
            #quantopian s history method allows symbol as object or string but not QC so we need to always 
            # convert to symbol before calling history method to streamline compatibility
            sym = self.symbolAsString(symbol)            
            if attr is None:
                attr = "close"
            if resolution is None:
                resolution = "Resolution.Daily"            
            try:
                #make sure it exists in our securities list first
                price = Securities[sym].Close
            except:
                equity=self.AddEquity(sym,resolution)
            history = self.History(equity.Symbol,period,resolution)
            df_history = history[attr].unstack(level=0).dropna()
            return df_history
        except:       
            self.Log('An unknown error occurred trying get history by_symbol.' +  str(sys.exc_info()[0]) )
            return newDF        
          
    def symbolAsString(self,symbol):
        if isinstance(symbol,Securities.Equity.Equity):
            return str(symbol.Symbol)
        else:
            return str(symbol)