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)