class OptimizedHorizontalCircuit(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(2020,4,10) #Set Start Date
self.SetEndDate(2020,6,3) #Set End Date
self.SetCash(10000000)
# returns a equity object, which has .Symbol property
self.spy = self.AddEquity("SPY", Resolution.Minute)
# required for dealing with options
self.spy.SetDataNormalizationMode(DataNormalizationMode.Raw)
# this determines our fees
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
# creating a 30 min consolidator
spy_thirtyMinuteConsolidator = TradeBarConsolidator(timedelta(minutes=30))
# this is a event handler method, each time there is a new 30 minute bar, "self.ThirtyMinuteBarHandler" will be called
spy_thirtyMinuteConsolidator.DataConsolidated += self.ThirtyMinuteBarHandler
# our consolidator is registered to consolidate spy data
self.SubscriptionManager.AddConsolidator("SPY", spy_thirtyMinuteConsolidator)
self.spy_rsi = RelativeStrengthIndex(20, MovingAverageType.Simple)
self.RegisterIndicator("SPY", self.spy_rsi, spy_thirtyMinuteConsolidator)
#Aroon 13 Added - rolling window setup
self.spy_aroon13 = AroonOscillator(13,13)
self.RegisterIndicator("SPY", self.spy_aroon13, spy_thirtyMinuteConsolidator)
self.spy_aroon13.Updated += self.spy_Aroon13Updated
self.spy_aroon13win = RollingWindow[IndicatorDataPoint](10)
self.spy__aroon13_AroonUp = RollingWindow[float](10)
#Ema 13 Added rolling window setup
self.spy_ema13 = ExponentialMovingAverage(13)
self.RegisterIndicator("SPY", self.spy_ema13, timedelta(minutes=30))
self.spy_ema13.Updated += self.spy_Ema13Updated
self.spy_ema13Window = RollingWindow[IndicatorDataPoint](5)
# self.cci = CommodityChannelIndex(20,MovingAverageType.Simple)
# self.RegisterIndicator("SPY", self.cci, thirtyMinuteConsolidator)
self.underlying_rsi = {}
self.underlying_cci = {}
self.underlying_rsi[self.spy.Symbol] = self.spy_rsi
#self.underlying_cci[self.spy.Symbol] = self.spy_cci
#tickers = ["QQQ", "IWM", "GLD", "TLT", "VXX"]
tickers = ["QQQ"]
for ticker in tickers:
security = self.AddEquity(ticker, Resolution.Minute)
symbol = security.Symbol
security.SetDataNormalizationMode(DataNormalizationMode.Raw)
# creating a 30 min consolidator
thirtyMinuteConsolidator = TradeBarConsolidator(timedelta(minutes=30))
# our consolidator is registered to consolidate spy data
self.SubscriptionManager.AddConsolidator(symbol, thirtyMinuteConsolidator)
# create a RSI indicator for that ticker
rsi = RelativeStrengthIndex(20, MovingAverageType.Simple)
self.RegisterIndicator(symbol, rsi, thirtyMinuteConsolidator)
# save a reference to our RSI indicator for that underlying
self.underlying_rsi[symbol] = rsi
cci = CommodityChannelIndex(20, MovingAverageType.Simple)
self.RegisterIndicator(symbol, cci, thirtyMinuteConsolidator)
self.underlying_cci[symbol] = cci
#Aroon 13 Added - rolling window setup
aroon13 = AroonOscillator(13,13)
self.RegisterIndicator(symbol, aroon13, spy_thirtyMinuteConsolidator)
self.aroon13.Updated += self.Aroon13Updated
self.aroon13win = RollingWindow[IndicatorDataPoint](10)
self._aroon13_AroonUp = RollingWindow[float](10)
#Ema 13 Added rolling window setup
ema13 = ExponentialMovingAverage(13)
self.RegisterIndicator(symbol, ema13, timedelta(minutes=30))
self.ema13.Updated += self.Ema13Updated
self.ema13Window = RollingWindow[IndicatorDataPoint](5)
# self.AddUniverse(self.SelectCoarse)
self.outtime = 10080
self.symbolDataBySymbolRSI = {}
self.symbolDataBySymbolCCI = {}
self.SetSecurityInitializer(self.CustomSecurityInitializer)
######Rolling Windows for SPY
def spy_Ema13Updated(self, sender, updated):
self.spy_ema13Window.Add(updated)
def spy_Aroon13Updated(self, sender, updated):
'''Adds updated values to rolling window'''
self.spy_aroon13win.Add(updated)
self.spy__aroon13_AroonUp.Add(self.spy_aroon13.AroonUp.Current.Value)
######Rolling Windows for non-SPY symbols in list
def Ema13Updated(self, sender, updated):
self.ema13Window.Add(updated)
def Aroon13Updated(self, sender, updated):
'''Adds updated values to rolling window'''
self.aroon13win.Add(updated)
self._aroon13_AroonUp.Add(self.aroon13.AroonUp.Current.Value)
def CustomSecurityInitializer(self, security):
bar = self.GetLastKnownPrice(security)
security.SetMarketPrice(bar)
# Is called each time a new security is added to our universe
def OnSecuritiesChanged(self, changes):
# for each new underlyingsecurity, we can create an RSI, and save a refernce
# in the self.underlying_rsi dictionary
pass
def ThirtyMinuteBarHandler(self, sender, bar):
'''This is our event handler for our 30-minute trade bar defined above in Initialize(). So each time the consolidator produces a new 30-minute bar, this function will be called automatically. The sender parameter will be the instance of the IDataConsolidator that invoked the event '''
for underlying_symbol in self.underlying_rsi:
rsi = self.underlying_rsi[underlying_symbol]
if not rsi.IsReady:
continue
holdings = self.Portfolio[underlying_symbol].Quantity
if holdings > 10000:
# do nothing
return
#if rsi.Current.Value < 30 and self.spy_ema13Window[0].Value < self.spy_ema13Window[1].Value and self.spy__aroon13_AroonUp[0] < self.spy__aroon13_AroonUp[1]:
if rsi.Current.Value < 30 and self.ema13Window[0].Value < self.ema13Window[1].Value and self._aroon13_AroonUp[0] < self._aroon13_AroonUp[1]:
contract_call_symbol = self.OptionsFilter(underlying_symbol, 10, OptionRight.Call)
# if there are no contracts do nothing
if contract_call_symbol is None:
return
self.MarketOrder(contract_call_symbol, 1)
self.Log(f"RSI<30 >> {self.spy__aroon13_AroonUp[0]} , {self.spy__aroon13_AroonUp[1]}")
symbolDataObject = SymbolDataRSI(contract_call_symbol, self)
self.symbolDataBySymbolRSI[contract_call_symbol] = symbolDataObject
symbolDataObject.NewPosition(self.Time, 1)
# SELLING
################
# we have a reference to each option we've traded
# every thirty minutes loop over all open option contracts positions
for option_symbol in self.symbolDataBySymbolRSI:
symbolData = self.symbolDataBySymbolRSI[option_symbol]
if not self.Portfolio[option_symbol].Invested:
continue
# do we have an open position for contract? # if outtime time as passed
option_entry_time = symbolData.entry_time
current_time = self.Time
if (current_time - option_entry_time) >= timedelta(minutes=self.outtime):
self.Liquidate(option_symbol)
self.Log(f"Sell Call: {option_symbol}, entry_time:{option_entry_time}, exit_time:{current_time}")
underlying_symbol = self.Securities[option_symbol].Underlying.Symbol
if self.underlying_rsi[underlying_symbol].Current.Value > 70:
self.Log(f"Sell RSI>70 >> {option_symbol}")
# Given an underlying, Identify an slightly OTM call contract expiry closest to X days but greater than X days
def OptionsFilter(self, underlying_symbol, expiry_days, option_right):
# option_right = [OptionRight.Call, OptionRight.Put]
contracts = self.OptionChainProvider.GetOptionContractList(underlying_symbol, self.Time)
underlyingPrice = self.Securities[underlying_symbol].Price
contracts_filtered_for_right_and_expiry = [i for i in contracts if i.ID.OptionRight == option_right and \
i.ID.Date >= self.Time + timedelta(days = expiry_days)]
otm_contracts = None
if option_right == OptionRight.Call:
otm_contracts = [i for i in contracts_filtered_for_right_and_expiry if i.ID.StrikePrice > underlyingPrice]
else:
otm_contracts = [i for i in contracts_filtered_for_right_and_expiry if i.ID.StrikePrice < underlyingPrice]
# if there are no contrats
if otm_contracts is None or len(otm_contracts) == 0:
return None
sorted_by_strike = sorted(otm_contracts, key=lambda x: abs((underlyingPrice+1) - x.ID.StrikePrice))
closest_strike = sorted_by_strike[0].ID.StrikePrice
# all contracts slightly OTM
contracts_with_closest_strike = [i for i in sorted_by_strike if i.ID.StrikePrice == closest_strike]
# Sort slightly OTM contracts by Expiry and pick
sorted_by_expiry = sorted(contracts_with_closest_strike, key=lambda x: x.ID.Date)
contract = sorted_by_expiry[0]
self.AddOptionContract(contract, Resolution.Minute)
return contract
class SymbolDataRSI:
"""
Motivation: We want to keep track of properties for each specific security (options, equities) - some symbol
"""
def __init__(self, symbol, algorithm):
self.symbol = symbol
self.algorithm = algorithm
self.entry_time = algorithm.Time # self.Time
# idea is we want to add structure within SymbolData so we can keep track
# of individual lots of trades for a specific contract
# Call June 2020 400 Strike
# {"05/12/2020" : +3, 05/17/2020 : +5} # we want to treat them indivudally
# record of each individual position for RSI strategy
self.positions = {} # key = entrytime , values = size
# 1st trade happens when we initialize
def NewPosition(self, entry_time, size):
self.positions[entry_time] = size
class SymbolDataCCI:
"""
Motivation: We want to keep track of properties for each specific security (options, equities) - some symbol
"""
def __init__(self, symbol, algorithm):
self.symbol = symbol
self.algorithm = algorithm
self.entry_time = algorithm.Time # self.Time
option_contract_security = algorithm.Securities[symbol]
underlying_security = option_contract_security.Underlying
# creating a 30 min consolidator
thirtyMinuteConsolidator = TradeBarConsolidator(timedelta(minutes=30))
# our consolidator is registered to consolidate underlying data
self.SubscriptionManager.AddConsolidator(underlying_security.Symbol, thirtyMinuteConsolidator)
self.underlying_cci = CommodityChannelIndex(20, MovingAverageType.Simple)
self.RegisterIndicator(underlying_security.Symbol, self.underlying_cci, thirtyMinuteConsolidator)
# idea is we want to add structure within SymbolData so we can keep track
# of individual lots of trades for a specific contract
# Call June 2020 400 Strike
# {"05/12/2020" : +3, 05/17/2020 : +5} # we want to treat them indivudally
self.lot = {} # key = entrytime , values = size