| Overall Statistics |
|
Total Trades 506 Average Win 0.18% Average Loss -0.06% Compounding Annual Return 8.756% Drawdown 5.300% Expectancy 0.085 Net Profit 1.344% Sharpe Ratio 0.623 Probabilistic Sharpe Ratio 43.018% Loss Rate 74% Win Rate 26% Profit-Loss Ratio 3.13 Alpha -0.056 Beta 0.239 Annual Standard Deviation 0.097 Annual Variance 0.009 Information Ratio -2.737 Tracking Error 0.156 Treynor Ratio 0.253 Total Fees $1144.38 |
class Gap_Up(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 10, 1) # Set Start Date
self.SetCash(100000) # Set Strategy Cash
# Creates "SPY" Equity
self.symbol = self.AddEquity('SPY', Resolution.Minute).Symbol
# Initiates our Universe on the Daily Resolution
self.UniverseSettings.Resolution = Resolution.Daily
self.AddUniverse(self.CoarseSelectionFilter)
# Schedule for 10 minutes before market close
self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.BeforeMarketClose(self.symbol, 10), self.BeforeMarketClose)
# Whether or not to trade a given stock
self.stop_trading = False
# Whether a stock has already been traded
self.made_trade = False
# Dictionary of data for the universe selection
self.stateData = { }
# Dictionary of data for the stocks that satisfy the gap conditions
self.Gaps = {}
# Value for determining when the day starts
self.curr_day = -1
def OnData(self, data):
# If the market is not open, return
if not self.IsMarketOpen(self.symbol):
return
# Get current day open
if self.curr_day != self.Time.day:
for symbol in self.Gaps:
# Resets StopTrading and MadeTrade for each stock in Gaps
self.Gaps[symbol].StopTrading = False
self.Gaps[symbol].MadeTrade = False
# If the symbol exists, set the open to the days open price
if data.ContainsKey(symbol) and data[symbol] is not None:
self.Gaps[symbol].Open = data[symbol].Open
# Else, set the "Open" to -1
else:
self.Gaps[symbol].Open = -1
self.curr_day = self.Time.day
# Checks to make sure all symbols in Gaps contain the data we need
# If they don't sets StopTrading to True
for symbol, gap in self.Gaps.items():
# If the stock should not be traded, continue and StopTrading is True
if gap.StopTrading:
continue
# If the the symbol does not have an open or close price, continue and StopTrading is True
elif gap.Open < 0 or gap.Close < 0:
gap.StopTrading = True
continue
# if gap up doesn't meet our threshold, continue and StopTrading is True
elif gap.Open - gap.Close <= 0:
gap.StopTrading = True
continue
# If symbol contains no data, continue
elif not data.ContainsKey(symbol) or data[symbol] is None:
continue
curr_price = data[symbol].Close
# Gives a decimal value so that SetHoldings always invests an equal amount per stock
order_perc = 1 / len(self.Gaps)
# Executes a trade for the given symbol
if not gap.MadeTrade:
self.SetHoldings(symbol, order_perc)
gap.MadeTrade = True
def BeforeMarketClose(self):
# Liquidate all assets
self.Liquidate()
for symbol in self.Gaps:
# If the symbol exists, sets the close for the day to Gaps.Close
if self.CurrentSlice.ContainsKey(symbol) and self.CurrentSlice[symbol] is not None:
self.Gaps[symbol].Close = self.Securities[symbol].Price
# Otherwise, the close equals -1, so it will be flitered out
else:
self.Gaps[symbol].Close = -1
# Sets the StopTrading back to False
self.Gaps[symbol].StopTrading = False
def OnSecuritiesChanged(self, changed):
# Removes removed securites from Gaps
for security in changed.RemovedSecurities:
self.Gaps.pop(security.Symbol)
# Adds Added Securites to Gaps
for security in changed.AddedSecurities:
self.Gaps[security.Symbol] = Gap()
def CoarseSelectionFilter(self, coarse):
self.coarse = coarse
# We are going to use a dictionary to refer the object that will keep the moving averages
# Adds the stocks that fit the volume criteria to the dictionary
for c in coarse:
if c.Symbol not in self.stateData:
self.stateData[c.Symbol] = SelectionData(c.Symbol, 10)
# Update the stateData to make sure it has the most recent stock data
avg = self.stateData[c.Symbol]
avg.update(c.EndTime, c.AdjustedPrice, c.DollarVolume)
values = [x for x in self.stateData.values() if x.is_above_vol and x.price >= 1 and x.price <= 100]
#values.sort(key=lambda x: x.volume, reverse=True)
# Makes sure price is between 1 and 100
#filtered = [symbol for symbol, sd in self.stateData.items() if sd.price >= 1 and sd.price <= 100]
return [ x.symbol for x in values[:10] ]
# return filtered[:50]
class Gap:
def __init__(self):
self.Close = -1
self.Open = -1
self.MadeTrade = False
self.StopTrading = False
class SelectionData(object):
def __init__(self, symbol, period):
self.symbol = symbol
self.volume = 0
self.price = 0
self.sma = SimpleMovingAverage(period)
self.is_above_vol = False
def update(self, time, price, volume):
self.volume = volume
self.price = price
if self.sma.Update(time, volume):
self.is_above_vol = self.sma.Current.Value > 3500000