Overall Statistics
Total Trades
141
Average Win
0.03%
Average Loss
0.00%
Compounding Annual Return
8.109%
Drawdown
11.600%
Expectancy
7.947
Net Profit
14.960%
Sharpe Ratio
0.636
Loss Rate
16%
Win Rate
84%
Profit-Loss Ratio
9.67
Alpha
0.024
Beta
2.899
Annual Standard Deviation
0.117
Annual Variance
0.014
Information Ratio
0.488
Tracking Error
0.117
Treynor Ratio
0.026
Total Fees
$141.31
import numpy as np
from datetime import timedelta

### <summary>
### Basic template algorithm simply initializes the date range and cash. This is a skeleton
### framework you can use for designing an algorithm.
### </summary>
class BasicTemplateAlgorithm(QCAlgorithm):
    '''Basic template algorithm simply initializes the date range and cash'''

    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(2017,1, 1)  #Set Start Date
        self.SetEndDate(2018,4,1)    #Set End Date

        self.SetCash(100000)           #Set Strategy Cash
        # Find more symbols here: http://quantconnect.com/data
        
        self.UniverseSettings.Resolution = Resolution.Minute
        self.AddUniverse(self.CoarseSelectionFunction)
        self.spy = self.AddEquity("SPY", Resolution.Minute).Symbol
        
        self.num_stocks = 5
        self.big_number_of_stocks = 200
        
        #rolling window size for dollar volume calculation
        self.history_window = 200
        self.SetWarmUp( timedelta( self.history_window ) )
        
        self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 10), Action(self.rebalance))

        self.symbols = []
        self.volumes = {}
        self.prices = {}

    def CoarseSelectionFunction(self, coarse):
        '''reset symbol universe for price sorted by dollar volume desc'''
        selected = [x for x in coarse if (x.HasFundamentalData)]
        filtered = sorted(selected, key=lambda x: x.DollarVolume, reverse=True)[:self.big_number_of_stocks]
        syms = list( self.volumes.keys() )
        new_syms = []
        
        #add dollar volume and price info to our dict object
        for x in filtered:
            new_syms.append( x.Symbol )
            if x.Symbol not in syms:
                self.volumes[x.Symbol] = [x.DollarVolume]
                self.prices[x.Symbol] = [x.Price]
            else:
                self.volumes[x.Symbol].append( x.DollarVolume )
                self.prices[x.Symbol].append( x.Price )

            #keep the data the right length
            if len( self.volumes[ x.Symbol ] ) > self.history_window:
                self.volumes[ x.Symbol ] = self.volumes[ x.Symbol ][ :-1 ]
                self.prices[ x.Symbol ] = self.prices[ x.Symbol ][ :-1 ]
        
        #remove any not in current set
        syms = list( self.volumes.keys() )
        for sym in syms:
            if sym not in new_syms:
                del self.volumes[ sym ]
                del self.prices[ sym ]
        
        
        #make total dollar volume stat
        total_volumes = []
        syms_filtered = []
        for sym in new_syms:
            if len( self.volumes[ sym ] ) >= self.history_window: 
                total_volumes.append( np.sum( self.volumes[ sym ] ) )
                syms_filtered.append(sym)
         
        top_five_idx = np.argsort( total_volumes )[-self.num_stocks:]     
        
        self.symbols = [ syms_filtered[i] for i in top_five_idx]
        return self.symbols

    def OnData(self, data):
        pass
    
    def make_trades(self, w):
        for symbol in self.Portfolio.Keys:
            if ( symbol not in self.symbols ):
                self.SetHoldings(symbol, 0)
                
        self.Log(str(self.symbols))
        for i in range(0, self.num_stocks):
            self.AddEquity(self.symbols[i].Value)
            self.SetHoldings(self.symbols[i], w[i])
    
    def rebalance(self):
        if len(self.symbols) == self.num_stocks:
            if self.no_bear():
                return
            w = self.get_weights()
            self.make_trades(w)
    
    def no_bear( self ):
        spy_hist = self.History([self.spy], 120, Resolution.Daily).loc[str(self.spy)]['close']
        if self.Securities[self.spy].Price < spy_hist.mean():
            for symbol in self.Portfolio.Keys:
                if symbol.Value != "TLT":
                    self.Liquidate(symbol)
            self.AddEquity("TLT")
            self.SetHoldings("TLT", 1)
            return True
        else:
            return False
        
    def get_weights(self):
        return [1/self.num_stocks] * self.num_stocks
def take_top_stocks(self):
        hist = self.History(self.symbols, self.history_window, Resolution.Daily)
        
        self.price = {}
        for symbol in self.symbols:
            if str(symbol) in hist.index:
                this_vector = np.array( hist.loc[str(symbol)]['close'] )
                if len( this_vector ) == self.history_window:
                    self.price[symbol.Value] = np.array( this_vector )
            
        price_matrix = np.array([ self.price[symbol] for symbol in self.price.keys() ])
        
        lr = np.diff( np.log( price_matrix ) )
        # self.Log(str(lr))
        mu = np.mean( lr, axis=1)
        std = np.std( lr, axis=1)
        sharpe = mu / std
        # self.Log(str(sharpe))
        filt = list( np.nonzero( sharpe >= np.sort( sharpe )[ -self.tiny_num_stocks ] )[0] )
        syms = list( self.price.keys() )
        
        # return [ lr, syms ]
        return [ np.array( [ lr[i,:] for i in filt] ), [syms[i] for i in filt] ]