| Overall Statistics |
|
Total Trades 64 Average Win 1.00% Average Loss -1.28% Compounding Annual Return -10.300% Drawdown 12.200% Expectancy -0.166 Net Profit -6.926% Sharpe Ratio -0.859 Probabilistic Sharpe Ratio 3.897% Loss Rate 53% Win Rate 47% Profit-Loss Ratio 0.78 Alpha -0.081 Beta -0.041 Annual Standard Deviation 0.099 Annual Variance 0.01 Information Ratio -1.036 Tracking Error 0.17 Treynor Ratio 2.06 Total Fees $355.08 |
import operator
class FadingTheGap(QCAlgorithm):
def Initialize(self):
#Backtesting parameters
self.SetStartDate(2017, 11, 1)
self.SetEndDate(2018, 7, 1)
self.SetCash(100000)
self.minimumDollar = 20
self.maximumDollar = 5000
self.topVolume = 100
# Securities list that is dynamically changes by the universe coarse filter
self.SecuritiesList = []
# Schedules
self.AddEquity("SPY", Resolution.Daily)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 1), self.OpeningBar)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 2), self.Buy)
self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen("SPY", 45), self.ClosePositions)
# Universe Settings
self.UniverseSettings.Resolution = Resolution.Minute
self.AddUniverse(self.CoarseSelectionFilter)
# Dictionaries
self.securityDelta = {}
self.data = {}
self.securityDeviations = {}
def CoarseSelectionFilter(self, coarse):
# Sort equities by volume - highest volume first
sortedByDollarVolume = sorted(coarse, key=lambda c: c.DollarVolume, reverse=True)
# Filter equities by price
filteredByPrice = [c.Symbol for c in sortedByDollarVolume if c.Price > self.minimumDollar and c.Price < self.maximumDollar]
self.SecuritiesList = filteredByPrice[:self.topVolume]
return self.SecuritiesList
def OnSecuritiesChanged(self, changes):
# When securities are added we create a SymbolData object for them
for security in changes.AddedSecurities:
symbol = security.Symbol
self.data[symbol] = SymbolData(self, symbol)
def OpeningBar(self):
# Go through all the top traded securities of the day
for symbol in self.SecuritiesList:
# Retrieve the current day open of the same securities
if not self.CurrentSlice.ContainsKey(symbol): continue
if self.CurrentSlice[symbol] is None: continue
if self.CurrentSlice[symbol].Open <= 0: continue
if self.CurrentSlice[symbol].IsFillForward: continue
currentOpen = self.CurrentSlice[symbol].Open
self.securityDelta[symbol] = currentOpen - self.data[symbol].previousClose
# reset our securityDeviations every morning before deviation calculations
self.securityDeviations = {}
def Buy(self):
for symbol, value in self.data.items():
if value.std.IsReady and symbol in self.securityDelta:
deviation = self.securityDelta[symbol] / value.std.Current.Value
if deviation < -2:
self.securityDeviations[symbol] = deviation
# if there are symbols with more than 2 deviations today
if len(self.securityDeviations) > 0:
# retrives the symbol with the largest negative deviation
buySymbol = min(self.securityDeviations.items(), key=operator.itemgetter(1))[0]
self.SetHoldings(buySymbol, 1)
self.Debug("BUY " + buySymbol.Value + " deviations: " + str(self.securityDeviations[buySymbol]) )
def ClosePositions(self):
for kvp in self.Portfolio:
if kvp.Value.Invested:
self.Debug("SELL " + kvp.Key.Value)
# Liquidate Portfolio
self.Liquidate()
class SymbolData:
def __init__(self, algorithm, symbol):
self.algorithm = algorithm
self.symbol = symbol
self.std = StandardDeviation(symbol, 60)
self.previousClose = -1
# Initalize indicator with historical daily data
history = algorithm.History(symbol, 100, Resolution.Daily)
for bar in history.itertuples():
self.Update(bar.Index[1], bar.close)
dailyConsolidator = TradeBarConsolidator(timedelta(days = 1))
algorithm.SubscriptionManager.AddConsolidator(symbol, dailyConsolidator)
dailyConsolidator.DataConsolidated += self.OnConsolidated
# Update previous day close and daily STD indicator
def Update(self, time, close):
self.std.Update(time, close)
self.previousClose = close
# daily consolidator updates our indicator and previous daily close
def OnConsolidated(self, sender, bar):
self.Update(bar.Time, bar.Close)