Overall Statistics
Total Trades
5120
Average Win
0.16%
Average Loss
-0.16%
Compounding Annual Return
36.267%
Drawdown
20.000%
Expectancy
0.021
Net Profit
8.204%
Sharpe Ratio
0.813
Loss Rate
49%
Win Rate
51%
Profit-Loss Ratio
1.02
Alpha
2.883
Beta
-159.228
Annual Standard Deviation
0.404
Annual Variance
0.164
Information Ratio
0.773
Tracking Error
0.404
Treynor Ratio
-0.002
Total Fees
$8165.16
#from System.Collections.Generic import List
import decimal as d
import numpy as np
import pandas as pd
import time

class BatchProblem(QCAlgorithm):

    def Initialize(self):

        self.SetStartDate(2017, 5, 24)  #Set Start Date
        self.SetEndDate(  2017, 8, 24)    #Set End Date
        self.SetCash(100000)           #Set Strategy Cash

        self.res = Resolution.Minute
        self.UniverseSettings.Resolution = self.res

        self.max_per_side_assets     = 20 # so 2*N is total assets at L=2
        self.coarse_count            = 300 #3000

        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        self.AddUniverse(self.CoarseSelectionFunction)
        
        self.AddEquity("SPY", self.res)
        
        # schedule problem live vs backtest
        if self.LiveMode:
            
            self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.At(8,45),
                Action(self.Downloader))
            
            self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.At(8,55),
                Action(self.Downloader))
            
            self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.At(9,5),
                Action(self.Downloader))
            
            self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.At(9,15),
                Action(self.Downloader))
            
            self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.At(9,25),
                Action(self.Strategy))
                
            self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.AfterMarketOpen("SPY", 0),
                Action(self.Rebalance))
        else:
            # Note we force shift into market hours, otherwise these run out of order if set before 9:31
            
            for i in range(4):
                self.Schedule.On(self.DateRules.EveryDay("SPY"),
                    self.TimeRules.At(9,25+i),
                    Action(self.Downloader))
            
            self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.At(9,32),
                Action(self.Strategy))
                
            self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.At(9,33),
                Action(self.Rebalance))

        self.Schedule.On(self.DateRules.EveryDay("SPY"),
            self.TimeRules.BeforeMarketClose("SPY", 3),
            Action(self.FireSale))
            
        self.Schedule.On(self.DateRules.EveryDay("SPY"),
            self.TimeRules.BeforeMarketClose("SPY", 0),
            Action(self.Reset_Baskets))
            
        self.universe              = []
        self.Reset_Baskets()

        self.n_hist_items = 5
        
        self.splotName = 'Strategy Info'
        sPlot = Chart(self.splotName)
        sPlot.AddSeries(Series('N_Universe', SeriesType.Line, 0)) # Label only
        self.AddChart(sPlot)


    def Reset_Baskets(self):
        
        self.current_symbols_long  = []
        self.current_symbols_short = []

        self.split_universe        = []
        self.current_subset        = 0
        self.asset_hist            = {}


    def Downloader(self):
        
        self.Log("Downloader %d "%self.current_subset + str(self.Time))
        
        if len(self.split_universe) == 0:
            return
        
        # Download subset of universe to prevent timeout via History server
        for symbol in self.split_universe[self.current_subset]:
            #self.AddEquity(str(symbol), Resolution.Minute)
            if str(symbol) != "SPY":
                self.AddEquity(str(symbol), self.res)
                self.asset_hist[symbol] = self.History([symbol,], self.n_hist_items, Resolution.Daily).astype(np.float32)
                self.RemoveSecurity(symbol)
        self.current_subset += 1
        
        
    def ProcessAsset(self, symbol):
        
        asset_hist   = self.asset_hist[symbol]

        #compute something with history to show difference
        dp = asset_hist.open.pct_change().values[-1]
        
        return symbol, dp
        
    def Strategy(self):
        
        self.Log("Strategy " + str(self.Time))
        if len(self.split_universe) == 0:
            return
        
        # Collect the info we need per assets to filter/mask
        res = [self.ProcessAsset(symbol) for symbol in self.asset_hist]#self.universe if symbol in self.asset_hist]

        symbols, std = zip(*res)
        symbols      = np.array(symbols)      
        std          = np.array(std)
        
        self.current_symbols_long  = symbols[np.argsort(std)][-self.max_per_side_assets:]
        self.current_symbols_short = symbols[np.argsort(std)][:self.max_per_side_assets]
        
        # manually manage
        for symbol in self.asset_hist:
            if (symbol in self.current_symbols_long) or (symbol in self.current_symbols_short):
                self.AddEquity(str(symbol), self.res)
                

    
    def Rebalance(self):
        
        self.Log("Rebalance " + str(self.Time))
        
        # Enter short positions
        if len(self.current_symbols_short) > 0:
            for sym in self.current_symbols_short:
                self.SetHoldings(sym, -0.1)
        
        # Enter long positions
        if len(self.current_symbols_long) > 0:
            for sym in self.current_symbols_long:
                self.SetHoldings(sym, 0.1)
                
        self.Plot(self.splotName,'N_Universe', len(self.universe))
    
        
    def FireSale(self):
        self.Liquidate()

        map(self.RemoveSecurity, self.current_symbols_short)
        map(self.RemoveSecurity, self.current_symbols_long)

    def CoarseSelectionFunction(self, coarse):
        
        
        self.Log("CoarseSelectionFunction " + str(self.Time))
        
        # Filter the values of the dict: by price
        values = filter(lambda x: (x.Price >= 5.0) and (x.Price <= 250.0), coarse)

        # sort descending by daily dollar volume
        sortedByDollarVolume = sorted(values, key=lambda x: x.DollarVolume, reverse=True) 

        # Assets selected
        # we need to return only the symbol objects
        result = [ x.Symbol for x in sortedByDollarVolume[:self.coarse_count] ]
        
        # we need to call Strategy n times to hit our uni target
        self.split_universe = np.array_split(result, 4) # universe is ~2k so use 4 updated taking ~5-8 min each?
        
        return []

    # this event fires whenever we have changes to our universe
    
    # this event fires whenever we have changes to our universe
    def OnSecuritiesChanged(self, changes):
        
        # liquidate removed securities
        for security in changes.RemovedSecurities:
            if security.Symbol in self.universe:
                self.universe.remove(security.Symbol)
            if security.Invested:
                self.Log(str("OnSecuritiesChanged Liquidate of %s"%str(security.Symbol)))
                self.Liquidate(security.Symbol)

        # we want equal allocation in each security in our universe
        for security in changes.AddedSecurities:
            security.MarginModel   =  PatternDayTradingMarginModel()
            if security.Symbol not in self.universe:
                if str(security.Symbol) != "SPY":
                    self.universe.append(security.Symbol)