Hey Guys,
I have just started delving into the futures side of building algos and boy are they more complicated than equities. With equities I've managed to have a different indicator resoltuion to the data feed resolution i.e. self.AddEquity(self.ticker, Resolution.Minute) with a Resolution.Daily indicator. But, when it comes to futures I have hit a brick wall. I have tried consolidators but my knowledge is lacking even after watching the consolidator help videos.
I have created an algo which rolls the front month ES contract when it has less than 10 days and it updates the SMA accordingly but now I want to be able to have a Resolution.Tick for the ES data and a Resolution.Minute for the SMA, but when I change the ES Resoltuion to Tick. I get this error message:
"Runtime Error: AttributeError : 'NoneType' object has no attribute 'Symbol'
at OnData in main.py:line 29
:: if not self.UpdateSMA(data): return
at UpdateSMA in main.py:line 77
:: if self.currentSMAsymbol != self.frontContract.Symbol:
AttributeError : 'NoneType' object has no attribute 'Symbol' (Open Stacktrace)"
Any ideas on how I could go about do running Resolution.Tick for the ES and Resolution.Minute for the indicators?
Here is my code which working using the self.AddFuture("ES", Resolution.Minute):
class DynamicCalibratedContainmentField(QCAlgorithm):
frontContract = None
currentSMAsymbol = None
futureSma = None
newDay = True
def Initialize(self):
self.SetStartDate(2019, 3, 3) # Set Start Date
self.SetEndDate(2019, 12, 4) # Set End Date
self.SetCash(50000)
futureES = self.AddFuture("ES", Resolution.Minute)
futureES.SetFilter(timedelta(0), timedelta(182))
def OnData(self, data):
# Check to see if I have rolled to a new contract
if not self.UpdateContract(data):
# If I have rolled to a new contract and I am currently invested then liquidate any positions
if self.Portfolio.Invested:
self.Liquidate()
self.Debug("Liquidated old contract positions")
# Update SMA and if it isn't ready then return
if not self.UpdateSMA(data): return
# If I am not currently invested then go long
if not self.Portfolio.Invested:
self.MarketOrder(self.frontContract.Symbol, 1)
def UpdateContract(self, slice):
if not self.newDay: return True
# If we haven't already stored the front contract, we need to find it
if self.frontContract is not None and self.frontContract.Expiry > self.Time + timedelta(10): return True
else:
# Get front month symbol
for chain in slice.FutureChains:
# Get contracts expiring no earlier than in 90 days
contracts = list(filter(lambda x: x.Expiry > self.Time + timedelta(90), chain.Value))
# If there is any contract, store the front month contract
if len(contracts) == 0: continue
# If there are no contracts in the list above then just sort the contracts by closest expiry
self.frontContract = sorted(contracts, key = lambda x: x.Expiry, reverse=True)[0]
self.newDay = False
self.Debug(f"Front month contract found: {self.frontContract.Symbol}")
return False
# Create and update SMA
def UpdateSMA(self, slice):
# If we haven't already created the SMA, we need to create it
if self.futureSma is None and self.frontContract is not None:
self.futureSma = self.SMA(self.frontContract.Symbol, 2, Resolution.Minute)
self.currentSMAsymbol = self.frontContract.Symbol
self.Debug("SMA created")
# If the frontContract changes then we need to update the SMA to the new symbol
if self.currentSMAsymbol != self.frontContract.Symbol:
self.futureSma = self.SMA(self.frontContract.Symbol, 2, Resolution.Minute)
self.currentSMAsymbol = self.frontContract.Symbol
self.Debug("SMA Updated")
# If our SMA isn't ready then we don't want to enter the market
if self.futureSma is not None and self.futureSma.IsReady:
self.Debug("SMA is ready")
return True
else:
self.Debug("SMA is not ready yet")
return False
def OnEndOfDay(self):
self.newDay = True
def OnEndOfAlgorithm(self):
self.Debug("The SMA ending value was %f" % (self.futureSma.Current.Value))
self.Debug("The SMA was following the %s" % (self.currentSMAsymbol))
self.Debug("The SMA should be following %s" % (self.frontContract.Symbol))
If anyone has the answer to this it would mean the world as I have been stuck on this for a while!
Thanks again,
Seb
Rahul Chowdhury
Hey Sebastian,
In order to create a futures data SMA for the front month contract, we can first create a 1 minute TickQuoteBarConsolidator and manually update it with tick data from our contract. Then we can manually update our SimpleMovingAverage indicator with the consolidated minute bar data. The DataConsolidated event handler for a consolidator is automatically called each time a new bar is consolidated. We can use that event handler to automatically update our SMA as new bars are available.
# In Initialize self.sma = SimpleMovingAverage(2) self.consolidator = TickQuoteBarConsolidator(timedelta(minutes=1)) self.consolidator.DataConsolidated += self.OnMinuteData # In main class def OnMinuteData(self, sender, bar): self.sma.Update(self.Time, bar.Close)
Now we have to retrieve the tick data from our front month contract and feed it into the consolidator. In this example, I used the built-in front month contract filter to access the front month contract.
futureES = self.AddFuture("ES", Resolution.Tick) futureES.SetFilter(lambda x : x.FrontMonth())
This ensures the only contract in our chain is the front month contract .Now we'll need to access that contract and its tick data.
for chain in data.FutureChains: contracts = [contract for contract in chain.Value] self.frontContract = contracts[0] ticks = chain.Value.Ticks
Then we can update our consolidator with the tick data from our front month contract.
if symbol in ticks.Keys: for tick in ticks[symbol]: self.consolidator.Update(tick)
Keep in mind that consolidators are only valid for a single symbol. This means we will need to reset our consolidator each time there is a new front month contract. This can be accomplished by redefining self.consolidator
if current_contract.Symbol != previous_contract.Symbol: self.consolidated -= self.OnMinuteData self.consolidator = TickQuoteBarConsolidator(timedelta(minutes=1)) self.consolidator += self.OnMinuteData
Best
Rahul
Sebastian Bunney
Thanks a ton Rahul! Thats an amazing explanation and has given me a much better understanding of how everything pieces together. Thanks again
Sebastian Bunney
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!