import numpy as np

from datetime import datetime, timedelta

from System.Collections.Generic import List

from QuantConnect.Data.UniverseSelection import *

class BasicTemplateAlgorithm(QCAlgorithm):

'''Basic template algorithm simply initializes the date range and cash'''

def __init__(self):

# set the flag for rebalance

self.reb = 1

# Number of stocks to pass CoarseSelection process

self.num_coarse = 250

# Number of stocks to long/short

self.num_fine = 20

self.symbols = [] #None

self.first_month = 0

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(2015,10,07) #Set Start Date

self.SetEndDate(2017,12,12) #Set End Date

self.SetCash(1000) #Set Strategy Cash

self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol

self.UniverseSettings.Resolution = Resolution.Daily

self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)

# Schedule the rebalance function to execute at the begining of each month

self.Schedule.On(self.DateRules.MonthStart(self.spy),

self.TimeRules.AfterMarketOpen(self.spy, 5), Action(self.rebalance))

#self.UniverseSettings.Resolution = Resolution.Daily

#self.UniverseSettings.MinimumTimeInUniverse = 0

# Add universes in which to find investments

#Warm up 200 bars for all subscribed data.

#self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash)

#self.SetBenchmark('SPY')

#self.UniverseSettings.Resolution = Resolution.Daily

#self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)

# Schedule the rebalance function to execute at the begining of each month

#self.spy = self.AddEquity('SPY', Resolution.Daily).Symbol

#self.Schedule.On(self.DateRules.MonthStart(self.spy),

#self.TimeRules.AfterMarketOpen(self.spy,5), Action(self.rebalance)) #

def CoarseSelectionFunction(self, coarse):

# if the rebalance flag is not 1, return null list to save time.

if self.reb != 1:

return []

# make universe 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)

top = sortedByDollarVolume[:self.num_coarse]

return [i.Symbol for i in top]

def FineSelectionFunction(self, fine):

# return null list if it's not time to rebalance

if self.reb != 1:

return []

self.reb = 0

# drop stocks which don't have the information we need.

# you can try replacing those factor with your own factors here

filtered_fine = [x for x in fine if x.OperationRatios.OperationMargin.Value

and x.ValuationRatios.PriceChange1M

and x.ValuationRatios.BookValuePerShare]

self.Log('remained to select %d'%(len(filtered_fine)))

# rank stocks by three factor.

sortedByfactor1 = sorted(filtered_fine, key=lambda x: x.OperationRatios.OperationMargin.Value, reverse=True)

sortedByfactor2 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.PriceChange1M, reverse=True)

sortedByfactor3 = sorted(filtered_fine, key=lambda x: x.ValuationRatios.BookValuePerShare, reverse=True)

stock_dict = {}

# assign a score to each stock, you can also change the rule of scoring here.

for i,ele in enumerate(sortedByfactor1):

rank1 = i

rank2 = sortedByfactor2.index(ele)

rank3 = sortedByfactor3.index(ele)

score = sum([rank1*0.2,rank2*0.4,rank3*0.4])

stock_dict[ele] = score

# sort the stocks by their scores

self.sorted_stock = sorted(stock_dict.items(), key=lambda d:d[1],reverse=False)

sorted_symbol = [x[0] for x in self.sorted_stock]

# sotre the top stocks into the long_list and the bottom ones into the short_list

self.buy = [x for x in sorted_symbol[:self.num_fine]]

self.short = [x for x in sorted_symbol[-self.num_fine:]]

topFine = self.buy + self.short

return [i.Symbol for i in topFine]

#Our rebalanced method is straightforward: We first liquidate the stocks that are no longer in the long/short list, and then assign equal weight to the stocks we are going to long or short.

def rebalance(self):

# if this month the stock are not going to be long/short, liquidate it.

long_short_list = self.buy + self.short

for i in self.Portfolio.Values:

if (i.Invested) and (i.Symbol not in long_short_list):

self.Liquidate(i.Symbol)

# Assign each stock equally. Always hold 10% cash to avoid margin call

for i in self.buy:

self.SetHoldings(i,0.9/self.num_fine)

for i in self.short:

self.SetHoldings(i,-0.9/self.num_fine)

self.reb = 1

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

'''

if not self.Portfolio.Invested:

self.SetHoldings("SPY", 1)