| Overall Statistics |
|
Total Trades 776 Average Win 0.18% Average Loss -0.17% Compounding Annual Return 4.849% Drawdown 8.900% Expectancy 0.021 Net Profit 1.257% Sharpe Ratio 0.345 Loss Rate 50% Win Rate 50% Profit-Loss Ratio 1.05 Alpha 0.399 Beta -21.515 Annual Standard Deviation 0.139 Annual Variance 0.019 Information Ratio 0.228 Tracking Error 0.139 Treynor Ratio -0.002 Total Fees $8189.95 |
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Indicators")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm.Framework")
from System import *
from QuantConnect import *
from QuantConnect.Data import *
from QuantConnect.Algorithm import *
from QuantConnect.Indicators import *
from System.Collections.Generic import List
from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTarget
from QuantConnect.Algorithm.Framework.Risk import RiskManagementModel
class EmaCrossUniverseSelectionAlgorithm(QCAlgorithm):
def Initialize(self):
'''If the filter returns nothing, keep 60% invested in SPY. Maximium drawdown allowed is 2.5% per investment, check every minute. Liquidate end of day to avoid overnight risk.
Filter stocks such that all entries are liquid, breaking out, and of the selected breakouts the top 24 breaking out the hardest. '''
self.SetStartDate(2019,3,1) #Set Start Date
self.SetEndDate(datetime.now().date() - timedelta(1)) #Set End Date
#self.SetEndDate(2019,1,1) #Set End Date
self.SetCash(1000000) #Set Strategy Cash
self.UniverseSettings.Resolution = Resolution.Hour
self.marginRemaining = self.Portfolio.MarginRemaining
self.orderTiming = 5
self.averages = { };
# this add universe method accepts two parameters:
# - coarse selection function: accepts an IEnumerable<CoarseFundamental> and returns an IEnumerable<Symbol>
self.AddEquity("SPY", Resolution.Hour)
self.AddUniverse(self.CoarseSelectionFunction)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", self.orderTiming), self.BuyFunc)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SPY", self.orderTiming), self.SellFunc)
self.SetWarmUp(16)
#OnData
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'''
#Maintain weight proportion based on active securities in the universe
self.weighting = 1.0 / (self.ActiveSecurities.Count+1)
#Universe Filter
# sort the data by volume and price, apply the moving average crossver, and take the top 24 sorted results based on breakout magnitude
def CoarseSelectionFunction(self, coarse):
filtered = [ x for x in coarse if (x.Volume > 1000000 and x.Price > 15) ]
# We are going to use a dictionary to refer the object that will keep the moving averages
for cf in filtered:
if cf.Symbol not in self.averages:
self.averages[cf.Symbol] = SymbolData(cf.Symbol)
# Updates the SymbolData object with current EOD price
avg = self.averages[cf.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)
# we need to return only the symbol objects
return [ x.symbol for x in values[:10] ]
# this event fires whenever we have changes to our universe
def OnSecuritiesChanged(self, changes):
self.changes = changes
# liquidate removed securities
for security in changes.RemovedSecurities:
if security.Invested:
self.Liquidate(security.Symbol)
self.RemoveSecurity(security.Symbol)
#Buy
def BuyFunc(self):
#Used to control leverage
self.OpenOrders = self.Transactions.GetOpenOrders()
for security in self.Securities.Values:
if not self.Securities[security.Symbol].Invested and self.Securities[security.Symbol] not in self.OpenOrders:
self.SetHoldings(security.Symbol, self.weighting)
#Sell
def SellFunc(self):
self.Liquidate()
return
#EMA Crossover Class
class SymbolData(object):
def __init__(self, symbol):
self.symbol = symbol
self.fast = ExponentialMovingAverage(5)
self.slow = ExponentialMovingAverage(25)
self.is_uptrend = False
self.scale = 0
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)) > 1.00
if self.is_uptrend:
self.scale = (fast - slow) / ((fast + slow) / 2.0)