Overall Statistics Total Trades101Average Win3.94%Average Loss-1.87%Compounding Annual Return4729.819%Drawdown21.100%Expectancy0.520Net Profit45.035%Sharpe Ratio3.096Probabilistic Sharpe Ratio80.916%Loss Rate51%Win Rate49%Profit-Loss Ratio2.10Alpha3.381Beta-2.698Annual Standard Deviation1.61Annual Variance2.592Information Ratio3.068Tracking Error1.819Treynor Ratio-1.848Total Fees$0.00 import math class SupportResistance: # Get the S&R's for the last 2 weeks. def __init__(self, algorithm, ticker): self.Ticker = ticker self.Algorithm = algorithm self.Daily = RollingWindow[TradeBar](7); self.Min = algorithm.MIN(self.Ticker, 390, Resolution.Minute) # Range of today; breaking out of new highs/lows self.Max = algorithm.MAX(self.Ticker, 390, Resolution.Minute) algorithm.Consolidate(self.Ticker, Resolution.Daily, self.SaveDailyBars) def NextSupport(self): support = [] price = self.Algorithm.Securities[self.Ticker].Price # Rounded Price to$1
support.append( round(price) )

# Rounded Price to $10 support.append( int(math.floor(price / 10.0)) * 10 ) # Yesterday's OHLC Price support.append( self.Daily[0].Close ) support.append( self.Daily[0].Low ) # Append 7 day Low: support.append( min([bar.Low for bar in self.Daily]) ) support = sorted(support, reverse=True) return support[0] def NextResistance(self): resistance = [] price = self.Algorithm.Securities[self.Ticker].Price # Round Price Up to$1
resistance.append( math.ceil(price) )

# Rounded Price Up to $10 resistance.append( int(math.ceil(price / 10.0)) * 10 ) # Yesterday's Close, High resistance.append( self.Daily[0].Close ) resistance.append( self.Daily[0].High ) # Append 7 Day High resistance.append( max([bar.High for bar in self.Daily]) ) # Take the lowest value on the list resistance = sorted(resistance) return resistance[0] # Build a 14 day historical rolling window of underlying prices. def SaveDailyBars(self, bar): self.Daily.Add(bar) # Reset any "daily" indicators def Reset(self): self.Min.Reset() self.Max.Reset() from trade import * from levels import * class OptionsOvernightContrarian(QCAlgorithm): def Initialize(self): # Settings self.SetStartDate(2020, 1, 31) self.SetEndDate(2020, 3, 5) self.SetCash(10000) self.SetWarmup(timedelta(7)) self.EnableAutomaticIndicatorWarmUp = True self.orderDate = None # Apply Robinhood Fees self.SetSecurityInitializer(lambda security: security.SetFeeModel(ConstantFeeModel(0))) # Select Underlying and Configure self.ticker = "SPY" self.equity = self.AddEquity(self.ticker) self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw) # Select Target Asset to Trade self.option = self.AddOption(self.ticker) self.option.SetFilter(self.OptionFilterUniverse) # Exit before any assignments self.Schedule.On(self.DateRules.EveryDay(self.ticker), self.TimeRules.AfterMarketOpen(self.ticker, 1), self.MarketOpen) self.Schedule.On(self.DateRules.EveryDay(self.ticker), self.TimeRules.BeforeMarketClose(self.ticker, 5), self.BeforeMarketClose) self.Schedule.On(self.DateRules.EveryDay(self.ticker), self.TimeRules.BeforeMarketClose(self.ticker, -1), self.MarketClose) # Trade Tracker self.Trade = TradeManagement(self, self.ticker, self.option.Symbol) # Support Resistance Detection: self.SupportResistance = SupportResistance(self, self.ticker) self.mean = self.EMA(self.ticker, 10, Resolution.Minute) def MarketClose(self): self.SupportResistance.Reset() def CloseTo(self, x, y, delta): return abs(x-y) < delta def OnData(self, data): # Ignore dividends, splits etc if not self.equity.Exchange.ExchangeOpen or self.IsWarmingUp: return # Manage existing positions: self.Trade.ManageOpenPositions() price = self.equity.Price support = self.SupportResistance.NextSupport() resistance = self.SupportResistance.NextResistance() mean = self.mean.Current.Value if self.CloseTo(price, support, 0.15) and mean > support: t = self.Trade.Create(OrderDirection.Buy) self.Log(f"{self.Time} LONG: Price {price} Support {support}") if self.CloseTo(price, resistance, 0.15) and mean < resistance: t = self.Trade.Create(OrderDirection.Sell) self.Log(f"{self.Time} SHORT: Price {price} Resistance {resistance}") ################################################################################################# # SCRATCH ####################################################################################### ################################################################################################# def MarketOpen(self): pass def BeforeMarketClose(self): pass def OptionFilterUniverse(self, universe): # Select puts 2-3 strikes OOM, expiring at least 2 days out; but no more than 7 return universe.IncludeWeeklys().Strikes(-3, 3).Expiration(2, 7) def StrikeVisualization(self, contracts): strikes = f"{self.Time} - {self.equity.Price} :: [[" for x in self.contracts: strikes = strikes + f"{x.Strike}-{x.Expiry} ]] [[" self.Log(strikes) self.Log(f"{self.Time} - Contract: {self.contracts[0]} | Strike: {self.contracts[0].Strike} | Underlying: {self.equity.Price} | Delta: {self.equity.Price - self.contracts[0].Strike}") import random import string import math class TradeManagement: def __init__(self, algorithm, ticker, optionSymbol): self.Trades = {} self.Algorithm = algorithm self.Ticker = ticker self.OptionSymbol = optionSymbol # Volatility indicators for position sizing self.atr = self.Algorithm.ATR(self.Ticker, 3, MovingAverageType.Simple, Resolution.Hour) self.stddev = self.Algorithm.STD(self.Ticker, 6, Resolution.Hour) # Manage Open Positions def ManageOpenPositions(self): for t in self.OpenTrades(): # Scan for Profit-Loss if t.UnrealizedProfit() > t.Target: self.Close(t, "Profit") elif t.UnrealizedProfit() < -t.Target: self.Close(t, "Loss") # Stop Assignment if t.ExpiringSoon(): self.Close(t, "Expiring") # Base target contract count on the number of contracts to hit the profit assuming 2SD move. def ContractSizing(self, targetProfit): expectedDollarMovement = 2 * self.stddev.Current.Value * 100 # At least 1 contract contracts = min(5, math.ceil(targetProfit / expectedDollarMovement)) return int(contracts) # Base target profit per position on the volatility of the last few days. def TargetProfitEstimate(self): return round(self.atr.Current.Value * 100) def OpenTrades(self): return [t for t in self.Trades.values() if t.IsOpen()] # Place a trade in the direction signalled def Create(self, direction): symbol = self.SelectOptionContract(direction) if symbol is None: return # If we already hold; skip alreadyOpen = [c for c in self.OpenTrades() if c.Symbol == symbol] if len(alreadyOpen) > 0: return # If today's the expiry don't trade # if (symbol.ID.Date < self.Algorithm.Time): # return targetProfit = self.TargetProfitEstimate() size = self.ContractSizing(targetProfit) price = self.Algorithm.Securities[symbol].Price asset = self.Algorithm.Securities[self.Ticker].Price tag = f"Asset: {asset} | Opened | Target of${targetProfit} at \${price}"
ticket  = self.Algorithm.MarketOrder(symbol, size, False, tag)

# Select OOM Option Contract with Given Bias
def SelectOptionContract(self, direction):

right = OptionRight.Call
else:
right = OptionRight.Put

chain = self.Algorithm.CurrentSlice.OptionChains.GetValue(self.OptionSymbol)
if chain is None:
self.Algorithm.Log(f"{self.Algorithm.Time} - No option chains with {self.OptionSymbol}. Missing data for Mar-2nd.")
return None

# Select contracts expirying tomorrow at least.
chain = [x for x in chain if (x.Right == right and x.Expiry > self.Algorithm.Time)]
reverseSort = right == OptionRight.Call         # Reverse sort of a call
contracts = sorted(sorted(chain, key = lambda x: abs(x.Strike), reverse=reverseSort), key = lambda x: x.Expiry)

# No contracts found
if len(contracts) == 0:
return None;

return contracts[0].Symbol

def __init__(self, algorithm, contract, ticket, target):
self.Id         = ''.join(random.choice(string.ascii_lowercase) for i in range(10))
self.Ticket     = ticket
self.Contract   = contract
self.Symbol     = contract.Symbol
self.Algorithm  = algorithm
self.Open       = True
self.Target     = target

def ExpiringSoon(self):
expiry = self.Symbol.ID.Date + timedelta(hours=16)
if ( (expiry - self.Algorithm.Time) < timedelta(minutes=10)):
self.Algorithm.Log(f"{self.Symbol} Close to Expiry: {expiry} - {self.Algorithm.Time} < 10min" )
return True
else:
return False

def UnrealizedProfitPercent(self):
return (self.Contract.Holdings.Price - self.Ticket.AverageFillPrice) / self.Contract.Holdings.Price

def UnrealizedProfit(self):
return 100*(self.Contract.Holdings.Price - self.Ticket.AverageFillPrice) * self.Ticket.Quantity

def IsOpen(self):
return self.Open

def Close(self, reason):
self.Open = False
tag = f"Close | {reason} | {self.UnrealizedProfit()} Net"
self.Algorithm.Liquidate(self.Symbol, tag)