Overall Statistics
Total Trades
2594
Average Win
2.78%
Average Loss
-2.09%
Compounding Annual Return
314.028%
Drawdown
35.200%
Expectancy
0.304
Net Profit
140772.120%
Sharpe Ratio
2.341
Loss Rate
44%
Win Rate
56%
Profit-Loss Ratio
1.33
Alpha
0.351
Beta
59.578
Annual Standard Deviation
0.569
Annual Variance
0.323
Information Ratio
2.312
Tracking Error
0.569
Treynor Ratio
0.022
Total Fees
$4074229.55
from QuantConnect.Data.UniverseSelection import *
import pandas as pd
import numpy as np
from scipy import stats

import decimal as d
import random

class BasicTemplateAlgorithm(QCAlgorithm):
    
    def Initialize(self):
        self.Debug("%%%%%%%%%%%%% Initialize")
        self.my_universe = []
        
        # Set the initial cash, start, and end dates for backtesting
        self.mycash = 30000
        self.mycash = 5000
        self.cash_per_trade = 5000
        self.SetCash(self.mycash)
        self.SetStartDate(2013,1,1)
        #self.SetEndDate(2018,11,11)
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin) # Or override account type
        
        # Subscribe to data for securities returned in my universe 
        self.UniverseSettings.Resolution = Resolution.Minute
        self.UniverseSettings.MinimumTimeInUniverse = 0 
        self.AddUniverse(self.universe_filter_course, self.universe_filter_fine)
        
        # Schedule the 'trade' method to run
        # Add SPY to our data subscription so we can schedule from it (use defaults)
        
        self.Schedule.On(self.DateRules.EveryDay(),
                         self.TimeRules.At(9, 31),
                         Action(self.my_trade))
                         
        self.Schedule.On(self.DateRules.EveryDay(),
                         self.TimeRules.At(15, 50),
                         Action(self.liquidate_portfolio))
        
        self.Schedule.On(self.DateRules.EveryDay(),
                         self.TimeRules.At(11, 00),
                         Action(self.cancel_orders))
                         
        overlayPlot = Chart("Overlay Plot")
        self.AddChart(overlayPlot)
        
        #self.Schedule.On(
        #    self.DateRules.EveryDay('SPY'), 
        #    self.TimeRules.AfterMarketOpen('SPY', 1),       
        #    Action(self.my_trade))
    
    def universe_filter_fine(self, fine_data):
        columns = ['SecurityType', 'ShareClassStatus', 'ExchangeId', 'IsDepositaryReceipt', 'IsPrimaryShare']
        
        data_df = pd.DataFrame.from_records(
                [[getattr(s.SecurityReference, name) for name in columns] for s in fine_data],
                #[[s.ValuationRatios.PERatio, s.ValuationRatios.SalesYield] for s in fine_data],
                index=[s.Symbol for s in fine_data],
                columns=columns,)
        data_df = data_df.query("(SecurityType == 'ST00000001') and (IsDepositaryReceipt == False) and (IsPrimaryShare)")
        
        #data_df = data_df.sort_values(data_df.columns[0], ascending = True)
        data_df.sort_index(inplace=True)
        #data_df = data_df[:10]
        #self.Debug(data_df)
        
        #self.Debug("data is: {}".format(data_df.EVToEBITDA.values))
        self.universe = data_df.index.tolist()
        self.my_universe = data_df.index.tolist()
        
        #self.Debug("fine size is: {}".format(len(self.universe)))
        return self.universe

    def universe_filter_course(self, coarse_data):
        # With data_df already initialized globally, this should now just
        #   populate securities for values matching query instead.
        columns = ['Price', 'DollarVolume', 'HasFundamentalData', 'Volume']
        
        data_df = pd.DataFrame.from_records(
                [[getattr(s, name) for name in columns] for s in coarse_data],
                index=[s.Symbol for s in coarse_data],
                columns=columns,
                coerce_float=True)
                
        data_df = data_df.sort_values(by='Volume', ascending=False)

        data_df = data_df.query("HasFundamentalData " \
            "and (DollarVolume > @data_df.DollarVolume.quantile(.20))" \
            "and (DollarVolume < @data_df.DollarVolume.quantile(.21))")
        '''
        data_df = data_df.query("HasFundamentalData ")
        data_df = data_df.sort_values('DollarVolume')
        data_df = data_df[data_df.DollarVolume > 1000000]
        data_df = data_df[50:100]
        '''
        self.StocksDailyData =data_df
        if len(data_df) == 0:
            return
        self.my_universe = data_df.index.tolist()
        
        return data_df.index.tolist()

    def my_trade(self):
        # See how many securities we have in our Securities list
        '''
        for universe in self.UniverseManager.Keys:
            self.Debug("universe name: {}".
                format(universe))
        '''    
        
        self.Debug("%%%%%%%%%%%%% my_trade")
        for universe in self.UniverseManager.Values:
            self.Debug("universe count: {}".
                format(universe.Members.Count))
        
        if len(self.my_universe) == 0:
            self.Debug('Empty universe')
            return
        
        stock_list = []

        for stock in self.my_universe:
            stock_list.append(stock)
            stockID = self.AddEquity(stock.Value)
            
        h1 = self.History(stock_list, 31, Resolution.Daily)
        h2 = self.History(stock_list, 1, Resolution.Minute)
        #self.Debug("\nstock_list - 1: %s "%([s.Value for s in stock_list]))
        #self.Debug("\nstock_list - 2: %s "%([s.Value for s in self.my_universe]))
        #self.Debug("\nstock_list - 3: %s "%([s[0] for s in h2.index.tolist()]))
        
        if not 'close' in h1.keys() or \
            not 'open' in h2.keys():
            self.Debug('Missing Data')
            return
        
        noMatch = returnNotMatches(h1.index.levels[0],h2.index.levels[0])
        
        h1.drop(noMatch[0], level='symbol', inplace=True)
        h2.drop(noMatch[1], level='symbol', inplace=True)
        
        #h1.index.get_level_values(0).unique()
        
        closes = h1['close'].unstack(level=0).reset_index(drop=True).values
        opens = h1['open'].unstack(level=0).append(
                h2['open'].unstack(level=0), 
                ignore_index=True)[1:].reset_index(drop=True).values

        gap = opens - closes

        gap_z = stats.zscore(gap, axis=0, ddof=1)

        min_gap_z = np.nanmin(gap_z[-1,:])
        max_gap_z = np.nanmax(gap_z[-1,:])
        
        k_min = np.nanargmin(gap_z[-1,:])
        k_max = np.nanargmax(gap_z[-1,:])

        #self.Debug("Min (%s) %f Max (%s) %f"%(self.my_universe[k_min], min_gap_z, self.my_universe[k_max], max_gap_z))
            
        i=0
        buyList = []
        sellList = []
        self.Debug("NOMBER IN UNIVERSe {}".format(len(self.my_universe)))

        #if len(stock_list) != len(h1.index.levels[0]) or \
        #    len(stock_list) != len(h2.index.levels[0]):
        #if len(stock_list) != len(h1.index.get_level_values(0).unique()) or \
        #    len(stock_list) != len(h2.index.get_level_values(0).unique()):
        #        
        #    self.Debug('===================')
        #    self.Debug(len(stock_list))
        #    self.Debug("\nstock_list: %s "%([s.Value for s in stock_list]))
        #    self.Debug(len(h1.index.get_level_values(0).unique()))
        #    self.Debug("\nh1.index.levels[0]: %s "%([s for s in h1.index.get_level_values(0).unique()]))
        #    self.Debug(len(h2.index.get_level_values(0).unique()))
        #    self.Debug("\nh2.index.levels[0]: %s "%([s for s in h2.index.get_level_values(0).unique()]))
            
            
        #for sid in h1.index.levels[0]:
        '''
        allStock = h1.index.get_level_values(0).unique()
        maxZ = gap_z[-1,]/min_gap_z
        maxZval = np.nanmax(maxZ)
        if maxZval >= 0.75:
            maxZpos = np.nanargmax(maxZ)
            buyList.append(allStock[maxZpos])
        '''
        self.Debug(gap_z[-1])
        for sid in h1.index.get_level_values(0).unique():
            if gap_z[-1,i] / min_gap_z >= 0.999:
            #if gap_z[-1,i] / min_gap_z >= 0.75:    
                self.Debug("->buy: %s  :: %f"%(sid,gap_z[-1,i]))
                buyList.append(sid)
            elif gap_z[-1,i] / max_gap_z >= 0.95:
                self.Debug("->sell: %s  :: %f"%(sid,gap_z[-1,i]))
                sellList.append(sid)
            i += 1
        #buyPower = (self.Portfolio.Cash * leverage) / (len(buyList) + len(sellList))
        #self.Debug('VALUES !!!!!!!!!!!!!!!!!!!!!!!!!!')
        #self.Debug(self.Portfolio.Cash)
        #self.Debug(self.Portfolio.TotalPortfolioValue)
        #self.Debug(self.mycash)
        #buyPower = ((self.mycash * 1) / (len(buyList)))-100
        
        percent = 1. / (len(buyList))
        
        self.Plot("Overlay Plot", "Num Stocks Buy", len(buyList))
        
        #buyPower = float(max(self.cash_per_trade,buyPower/5))
        #buyPower = float(min(self.cash_per_trade,self.portfolio.cash/5))
        
        self.Debug("\nBUYS: %s \nSELLS: %s"%([s for s in buyList],[s for s in sellList]))
        
        self.Log('Cash: ' + str(self.Portfolio.Cash))
        self.Log('TotalFees: ' + str(self.Portfolio.TotalFees))

        self.Debug('Checking Holdings ...')
        for kvp in self.Portfolio:
            holdings = kvp.Value
            symbol = holdings.Symbol
            
            if (holdings.Invested):
                self.Debug("Symbol: {} -> Price: {}".format(symbol, holdings.AveragePrice))
                
        for sid in buyList:
            #stock = self.AddEquity(sid.Value).Symbol
            price = float(self.Securities[sid].Price)
            if price == 0: continue
        
            #self.Debug(self.StocksDailyData.head(50))
            
            self.Debug(h1.loc[sid]['close'][0])
            self.Debug(h1.loc[sid]['volume'][0])
            self.Debug(h1.loc[sid]['close'][0]*h1.loc[sid]['volume'][0])
            prevDayDV = h1.loc[sid]['close'][0]*h1.loc[sid]['volume'][0]
            self.Debug('//////////////////////////////////////////////////////////')
            
            self.Debug(prevDayDV)
            
            #allocatedValue = float(min(prevDayDV/10, self.Portfolio.TotalPortfolioValue/(len(buyList))))
            allocatedValue = float(self.Portfolio.TotalPortfolioValue/(len(buyList)))
            shares = allocatedValue / price
            
            #if allocatedValue < self.cash_per_trade:
            #    continue
            
            
            #shares = (prevDayDV/10) / price
            
            #shares = self.cash_per_trade/price
            
            #self.Debug(self.Portfolio.GetBuyingPower(sid, OrderDirection.Buy))
            #shares = self.CalculateOrderQuantity(sid, percent)
            if shares > 0:
                self.Buy(sid, shares)
                #self.MarketOrder(sid, shares)
                #self.LimitOrder(sid, shares, price)
                #self.SetHoldings(sid.Value, shares)
                self.Debug("Buy Stock : {} , # Shares {} , price {}".format(sid, shares, price))
                #self.MarketOrder(sid, shares)
                #self.SetHoldings(sid, percent)
                #self.StopLimitOrder(sid, shares, decimal.Decimal(price  * 0.90), decimal.Decimal(price  * 0.90))
                #self.StopMarketOrder(sid, shares, decimal.Decimal(price  * 0.90));
                #self.LimitOrder(sid.Value, shares)
                #order(sid,shares,limit_price=price,stop_price=price)

        '''
        for sid in sellList:
            #stock = self.AddEquity(sid.Value).Symbol
            price = float(self.Securities[sid].Price)
            if price == 0: continue
            shares = buyPower/price
            if shares > 0:
                self.Debug("Stock : {} , # Shares {} , price {}".format(sid, shares, price))
               #self.MarketOrder(sid, -shares)
                #self.StopLimitOrder(sid, shares, decimal.Decimal(price  * 1.10), decimal.Decimal(price  * 1.10))
                #self.StopMarketOrder(sid, shares, decimal.Decimal(price  * 1.10));
                
                
                #self.Debug("Stock : {} , # Shares {} , price {}".format(sid, shares, price))
                #self.LimitOrder(sid.Value, shares)
                #order(sid,-shares,limit_price=price,stop_price=price)
            
        '''
        '''
        #for security in self.my_universe:
        #    self.Debug('#####################')
        #    self.SetHoldings(security, weight)
        '''
        
    def liquidate_portfolio(self):
        
        self.Debug('Checking Holdings 2222222222222...')
        for kvp in self.Portfolio:
            holdings = kvp.Value
            symbol = holdings.Symbol
            
            if (holdings.Invested):
                self.Debug("Symbol: {} -> Price: {}".format(symbol, holdings.AveragePrice))
                
                
                
        for symbol in self.Portfolio.Keys:
            self.SetHoldings(symbol, 0)

      
    def cancel_orders(self):
        self.Debug('=====================================================================')
        openOrders = self.Transactions.GetOpenOrders()
        if len(openOrders)> 0:
            for x in openOrders:
                 self.Transactions.CancelOrder(x.Id)
################https://www.quantconnect.com/docs/algorithm-reference/overview

def returnNotMatches(a, b):
    return [[x for x in a if x not in b], [x for x in b if x not in a]]
    
def to_dataframe(data_c, properties, labels):
    # Helper function to make a dataframe from the coarse object. 
    # Then we can use all the handy dataframe methods.
    # Set the 'coerce_float' parameter to get meaningful datatypes
    # otherwise all the fields will be un-useful generic objects.

    data = [[getattr(stock, name) for name in properties] for stock in data_c]
    symbols = [stock.Symbol for stock in data_c ]
    
    data_df = pd.DataFrame.from_records(
            data, 
            index=symbols, 
            columns=labels, 
            coerce_float=True)
            
    return data_df