Overall Statistics
Total Trades
77
Average Win
0.26%
Average Loss
-0.21%
Compounding Annual Return
27.023%
Drawdown
1.700%
Expectancy
0.181
Net Profit
2.119%
Sharpe Ratio
2.68
Loss Rate
47%
Win Rate
53%
Profit-Loss Ratio
1.24
Alpha
0.141
Beta
0.288
Annual Standard Deviation
0.071
Annual Variance
0.005
Information Ratio
0.177
Tracking Error
0.094
Treynor Ratio
0.664
Total Fees
$90.89
from System.Collections.Generic import List
from QuantConnect.Data.UniverseSelection import *
import math

class BasicTemplateAlgorithm(QCAlgorithm):
	def __init__(self):
	# please adjust your parameters below:
		self.price_above = 10		# select only sotcks with price higher than this number
		self.top_volume = 0.15       # percentage of top volume stocks to choose
		self.market_open_minute = 15 # trade execution time.
		self.first_gap_minute = 15   # if this variable == self.market_open_minute, the initia price for calculating the first gap would be yesterday close price.
		self.second_gap_days = 5    # previous trading day to calculate the second gap
		self.rebalance_day = 2      # holding period for each stock
		self.first_selection_num = 3   # number of stocks to select according to the first gap
		self.second_selection_num = 2   # number of stocks to buy
		self.leverage = 0.2         # Leverage setting

		self.holdings = []

	def Initialize(self):
		self.SetCash(100000)      # initial cash
		self.SetStartDate(2016,5,1)
		self.SetEndDate(2016,6,1)

		# Add spy as the benchmark for market open
		self.AddUniverse(self.CoarseSelectionFunction)
		self.spy = self.AddEquity("SPY", Resolution.Minute).Symbol

	def CoarseSelectionFunction(self, coarse):
		# select stocks with prices higher than a certain level.
		selected = [x for x in coarse if float(x.Price) > self.price_above]
		sortedByVolume = sorted(selected, key=lambda x: x.Volume, reverse=True)
		top = sortedByVolume[:int(math.floor(self.top_volume*len(sortedByVolume)))]
		list = List[Symbol]()
		for x in top:
			list.Add(x.Symbol)

		return list

	def OnData(self, slice):
		self.data = slice

		if self.Time.hour != 9 or self.Time.minute != 30+self.market_open_minute:
			return

		self.symbols = [x.Symbol for x in self.Portfolio.Values]
		if len(self.symbols) == 1:
			return

	# select according to the first gap
		for i in self.symbols:
			if not self.data.ContainsKey(i):
				continue

			history = self.History(i, self.first_gap_minute+1, Resolution.Minute)
			if 'close' not in history.columns:
				continue

			_price = float(history.close[0])
			i.gap = float(self.data[i].Price)/_price - 1

		self.symbols = [x for x in self.symbols if hasattr(x,'gap')]

		self.symbols.sort(key = lambda x: x.gap, reverse = True)
		selected = self.symbols[:self.first_selection_num]

	# select according to the second gap
		for i in selected:
			long_history = self.History(i, self.second_gap_days, Resolution.Daily)
			if 'close' not in long_history.columns:
				continue

			long_his_price = long_history.close[0]
			i.long_gap = float(self.data[i].Price)/long_his_price - 1
			i.total_gap = i.gap + i.long_gap


	# sorted by total gap
		selected = [x for x in selected if hasattr(x,'total_gap')]
		selected.sort(key = lambda x: x.total_gap, reverse = True)


		final = selected[:self.second_selection_num]

	# log holdings information
		for i in self.holdings:
			i.holding_day +=1
			self.Log('Equity %s current %f, cost basis %f'%(str(i).split(' ')[0], float(self.data[i].Price), i.cost))
			if i.holding_day == self.rebalance_day:
				self.Log('sell %s at %f, cost basis %f'%(str(i).split(' ')[0], float(self.data[i].Price), i.cost))
				self.Liquidate(i)

		self.holdings = [x for x in self.holdings if self.Portfolio[x].Invested]


	# long stocks and log out long information.
		for i in final:
			i.cost = float(self.data[i].Price)
			self.Log('long %s, at price %f, target percentage %f'%(str(i).split(' ')[0], i.cost, self.leverage/(self.second_selection_num*self.rebalance_day)))
			self.SetHoldings(i, self.leverage/(self.second_selection_num*self.rebalance_day))
			i.holding_day = 0
			self.holdings.append(i)