Overall Statistics |
Total Trades 666 Average Win 0.06% Average Loss -0.29% Compounding Annual Return -62.696% Drawdown 22.300% Expectancy -0.238 Net Profit -21.795% Sharpe Ratio -5.985 Probabilistic Sharpe Ratio 0% Loss Rate 36% Win Rate 64% Profit-Loss Ratio 0.19 Alpha -0.56 Beta 0.19 Annual Standard Deviation 0.093 Annual Variance 0.009 Information Ratio -4.914 Tracking Error 0.118 Treynor Ratio -2.933 Total Fees $666.00 |
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)