class EmaCrossUniverseSelectionAlgorithm(QCAlgorithm):
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,4,1) #Set Start Date
self.SetEndDate(2015,6,30) #Set End Date
self.SetCash(25000) #Set Strategy Cash
self.spy = self.AddEquity("SPY", Resolution.Minute).Symbol
self.UniverseSettings.Resolution = Resolution.Minute
self.UniverseSettings.Leverage = 2
self.coarse_count = 10
self.averages = { }
self.hold_day={ } # Make an empty dictionary to store holding days
self.long = [ ]
self.short = [ ]
# this add universe method accepts two parameters:
# - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol>
self.AddUniverse(self.CoarseSelectionFunction)
# Schedule the rebalance function to execute at user defined period
self.Schedule.On(self.DateRules.EveryDay(self.spy),
self.TimeRules.Every(TimeSpan.FromMinutes(60)),
Action(self.rebalance))
# Schedule the holding period function to execute every day
self.Schedule.On(self.DateRules.EveryDay(self.spy),
self.TimeRules.BeforeMarketClose(self.spy, 15),
Action(self.check_days))
# sort the data by daily dollar volume and take the top 'NumberOfSymbols'
def CoarseSelectionFunction(self, coarse):
filtered = [x for x in coarse if
5 < x.Price < 500
and x.DollarVolume > 1000000
and x.HasFundamentalData
]
# We are going to use a dictionary to refer the object that will keep the moving averages
for cf in filtered:
symbol = cf.Symbol
if symbol not in self.averages:
# Call history to get an array of 50 days of history data
history = self.History(symbol, 50, Resolution.Daily)
self.averages[symbol] = SymbolData(symbol, history)
# Updates the SymbolData object with current EOD price
avg = self.averages[symbol]
avg.update(cf.EndTime, cf.AdjustedPrice)
# Filter the values of the dict: we only want up-trending securities
values = list(filter(lambda x: x.is_uptrend, self.averages.values()))
# Sorts the values of the dict: we want those with greater difference between the moving averages
values.sort(key=lambda x: x.scale, reverse=True)
# for x in values[:self.coarse_count]:
# self.Debug('symbol: ' + str(x.symbol.Value) + ' scale: ' + str(x.scale))
self.long = [x.symbol for x in values[:self.coarse_count] if x.is_ready()]
self.short = [x.symbol for x in values[-self.coarse_count:] if x.is_ready()]
self.tradingList = self.long
# we need to return only the symbol objects
return self.tradingList
def OnData(self, data):
pass
def rebalance(self):
# self.Debug("Rebalance Event started running at: " + str(self.Time))
# 1. Cancel entryLong and entryShort tagged limit orders
# 2a. Liquidate Positions that reached Max TakeProfit or Holding Time; set hold_day to -1
for i in self.Portfolio.Values:
if i.Invested:
if self.Portfolio[i.Symbol].UnrealizedProfitPercent > 0.0101 or \
self.hold_day[i.Symbol.Value] >= 5:
self.Debug(
str(i.Symbol.Value) +
"Profit :" + str(self.Portfolio[i.Symbol].UnrealizedProfitPercent))
self.Liquidate(i.Symbol)
try:
self.hold_day[i.Symbol.Value] = -1
except:
self.Debug("RemovedfromDict: " + str(i.Symbol.Value))
# 3. Reset holding percentage for positive profit positions only
for i in self.Portfolio.Values:
if i.Invested and (self.hold_day[i.Symbol.Value] >= 5 or self.Portfolio[i.Symbol].UnrealizedProfitPercent) > 0.005:
self.SetHoldings(i.Symbol, 0.02)
self.Debug("Reseting:" + str(i.Symbol.Value))
# 4. Enter new position if it meets long/short entry criteria and not currently
# invested in that asset
tradingCandidates = self.tradingList
for symbol in tradingCandidates:
if not self.Portfolio[symbol].Invested:
self.SetHoldings(symbol, 0.02, False, "entryLong")
self.Debug("entryLong: " + str(symbol))
self.hold_day[symbol.Value] = 0 # Add stock and 0 days to the dictionary
# this event fires whenever we have changes to our universe
#def OnSecuritiesChanged(self, changes):
# liquidate removed securities
#for security in changes.RemovedSecurities:
#if security.Invested:
#self.Liquidate(security.Symbol)
# we want 20% allocation in each security in our universe
#for security in changes.AddedSecurities:
#self.SetHoldings(security.Symbol, 0.1)
# Helper function to count holding days for each holding stock
def check_days(self):
for i in self.Portfolio.Values:
if i.Invested:
self.hold_day[i.Symbol.Value] += 1 # Increment on each holding stock by 1 day
class SymbolData():
"""
Class to update Universe technical indicator data
"""
def __init__(self, symbol, history):
self.tolerance = 1.01
self. symbol = symbol
self.fast = ExponentialMovingAverage(3)
self.slow = ExponentialMovingAverage(45)
self.is_uptrend = False
self.scale = 0
for bar in history.itertuples():
self.fast.Update(bar.Index[1], bar.close)
self.slow.Update(bar.Index[1], bar.close)
def is_ready(self):
return self.slow.IsReady and self.fast.IsReady
def update(self, time, value):
if self.fast.Update(time, value) and self.slow.Update(time, value):
fast = self.fast.Current.Value
slow = self.slow.Current.Value
self.is_uptrend = fast > slow * self.tolerance
if self.is_uptrend:
self.scale = (fast - slow) / ((fast + slow) / 2.0)