Hey guys!
I have a simple equity strategy that looks a lot like this:
# region imports
from AlgorithmImports import *
import pandas as pd
import numpy as np
from datetime import time, datetime, timedelta
# endregion
class CombinedAlgorithm(QCAlgorithm):
def Initialize(self):
# INITIALIZE
self.SetStartDate(2022, 1, 1) # Set Start Date
self.SetEndDate(2022, 5, 5)
self.SetCash(10000) # Set Strategy Cash
self.symbol = self.AddEquity('AAPL', Resolution.Minute)
self.symbol.SetDataNormalizationMode(DataNormalizationMode.SplitAdjusted)
# SCHEDULED EVENTS
self.Schedule.On(self.DateRules.EveryDay(self.symbol.Symbol), self.TimeRules.AfterMarketOpen(self.symbol.Symbol, 120), self.TradingOn)
self.Schedule.On(self.DateRules.EveryDay(self.symbol.Symbol), self.TimeRules.BeforeMarketClose(self.symbol.Symbol, 10), self.TradingOff)
self.Schedule.On(self.DateRules.EveryDay(self.symbol.Symbol), self.TimeRules.BeforeMarketClose(self.symbol.Symbol, 2), self.LiquidateCheck)
self.Schedule.On(self.DateRules.EveryDay(self.symbol.Symbol), self.TimeRules.BeforeMarketClose(self.symbol.Symbol), self.DayEnd)
# TOGGLES
self.thirty_min_reversal_sell = False
self.initial_thirty_min_stop_sell = False
self.initial_stop_sell = False
self.target_sell = False
self.target_hit = False
self.short = False
self.trading = False
self.break_even = False
self.liquidate_toggle = False
self.invest_thirty_min = False
self.thirty_min_trigger = False
# VARIABLES
self.symbol_price = 0
self.initial_stop_price = 0
self.initial_target_price = 0
self.entry_price = 0
self.trigger_thirty_min_open = 0
self.trigger_thirty_min_close = 0
self.trigger_thirty_min_high = 0
self.trigger_thirty_min_low = 0
self.SetWarmUp(timedelta(days = 25))
self.ema = self.EMA(self.symbol.Symbol, 15, Resolution.Daily)
# CONSOLIDATED DATA
# 30-Minute Bars
thirty_min_consolidator = TradeBarConsolidator(timedelta(minutes=30))
self.SubscriptionManager.AddConsolidator(self.symbol.Symbol, thirty_min_consolidator)
thirty_min_consolidator.DataConsolidated += self.OnThirtyMinBar
self.thirtyMinBarWindow = RollingWindow[TradeBar](2)
def OnData(self, data):
if self.IsWarmingUp:
return
# VARIABLES
self.symbol_price = self.Securities[self.symbol.Symbol].Price
held_stocks = self.Portfolio[self.symbol.Symbol].Quantity
shares_to_buy = int(self.Portfolio.Cash / self.Securities[self.symbol.Symbol].Price)
# ENTRY (SHORT) (HOUR TRIGGERING HOUR - FIRST TRADE OTD)
if self.trading and self.thirty_min_trigger and not self.short:
if (self.symbol_price < self.trigger_thirty_min_low) and self.invest_thirty_min and not self.Portfolio.Invested:
self.ticket = self.MarketOrder(self.symbol.Symbol, -(shares_to_buy))
self.invest_thirty_min = False
self.thirty_min_trigger = False
self.initial_thirty_min_stop_sell = True
self.initial_stop_sell = True
self.initial_thirty_min_stop_price = self.trigger_thirty_min_high
self.thirty_min_reversal_sell = True
self.short = True
self.target_sell = True
self.entry_price = self.symbol_price
self.initial_target_price = self.entry_price * 0.99
self.Log(f'SHORT ENTRY (HOURLY TRIGGERING HOURLY) {self.symbol_price}')
# EXIT (INITIAL TARGET)
if self.Portfolio.Invested and self.short and self.target_sell and (self.symbol_price < self.initial_target_price):
self.MarketOrder(self.ticket.Symbol, (self.Securities[self.ticket.Symbol].Holdings.AbsoluteQuantity))
self.target_sell = False
self.target_hit = True
self.Log(f'EXIT (INITIAL TARGET) {self.symbol_price}')
# EXIT (INITIAL STOP)
if not self.target_hit and self.Portfolio.Invested and self.short and self.initial_thirty_min_stop_sell and (self.symbol_price > self.initial_thirty_min_stop_price):
self.MarketOrder(self.ticket.Symbol, (self.Securities[self.ticket.Symbol].Holdings.AbsoluteQuantity))
self.initial_thirty_min_stop_sell = False
self.thirty_min_reversal_sell = False
self.short = False
self.Log(f'EXIT (INITIAL STOP before target hit) {self.symbol_price}')
if not self.target_hit and self.Portfolio.Invested and self.short and self.initial_stop_sell and (self.symbol_price > self.initial_stop_price):
self.MarketOrder(self.ticket.Symbol, (self.Securities[self.ticket.Symbol].Holdings.AbsoluteQuantity))
self.initial_stop_sell = False
self.thirty_min_reversal_sell = False
self.short = False
self.Log(f'EXIT (INITIAL STOP before target hit) {self.symbol_price}')
if (self.target_hit or self.break_even) and self.Portfolio.Invested and self.short and self.initial_thirty_min_stop_sell and (self.symbol_price > self.entry_price):
self.MarketOrder(self.ticket.Symbol, (self.Securities[self.ticket.Symbol].Holdings.AbsoluteQuantity))
self.initial_thirty_min_stop_sell = False
self.thirty_min_reversal_sell = False
self.short = False
self.Log(f'EXIT (INITIAL STOP after target hit) {self.symbol_price}')
# EXIT (60-MIN TRAILING STOP)
if self.short and self.Portfolio.Invested and self.thirty_min_reversal_sell and (self.symbol_price > (self.trigger_hour_high)):
self.MarketOrder(self.ticket.Symbol, (self.Securities[self.ticket.Symbol].Holdings.AbsoluteQuantity))
self.thirty_min_reversal_sell = False
self.initial_stop_sell = False
self.short = False
self.Log(f'EXIT (30-MIN REVERSAL) {self.symbol_price} and {self.trigger_thirty_min_high}')
# EXIT (LIQUIDATE AT THE END OF THE DAY)
if self.liquidate_toggle:
self.MarketOrder(self.ticket.Symbol, (self.Securities[self.ticket.Symbol].Holdings.AbsoluteQuantity))
self.liquidate_toggle = False
self.short = False
self.Log(f'EXIT (LIQUIDATE AT THE END OF THE DAY) {self.symbol_price}')
# CONSOLILDATED FUNCTIONS
def OnThirtyMinBar(self, sender, bar):
self.thirtyMinBarWindow.Add(bar)
if not self.thirtyMinBarWindow.IsReady:
return
if self.IsWarmingUp:
return
# VARIABLES
self.thirtyMinBarWindow.Add(bar)
if not self.thirtyMinBarWindow.IsReady:
return
trigger_thirty_min = self.thirtyMinBarWindow[0]
previous_thirty_min = self.thirtyMinBarWindow[1]
self.trigger_thirty_min_open = trigger_thirty_min.Open
self.trigger_thirty_min_close = trigger_thirty_min.Close
self.trigger_thirty_min_high = trigger_thirty_min.High
self.trigger_thirty_min_low = trigger_thirty_min.Low
previous_thirty_min_high = previous_thirty_min.High
previous_thirty_min_low = previous_thirty_min.Low
if self.Time.hour == 10 and self.Time.minute == 0:
self.open_thirty_low = self.trigger_thirty_min_low
if (self.trigger_thirty_min_high < previous_thirty_min_high) and (self.trigger_thirty_min_low > previous_thirty_min_low):
self.trigger_thirty_min_timer = self.Time.minute
self.invest_thirty_min = True
self.thirty_min_trigger = True
# SCHEDULED EVENTS
def TradingOn(self): # 2 hours after market open
self.trading = True
def TradingOff(self): # 1 hour before market close
self.trading = False
def LiquidateCheck(self): # 2 mins before market close
if self.Portfolio.Invested:
self.liquidate_toggle = True
if not self.Portfolio.Invested:
return
def DayEnd(self):
self.trading = False
self.short = False
I am trying to leave all the entry and exit logic the same, but trade Equity Options instead.
I have changed two things.
contract_symbols = self.OptionChainProvider.GetOptionContractList(self.symbol.Symbol, self.Time)
expiry = min([symbol.ID.Date for symbol in contract_symbols])
filtered_symbols = [symbol for symbol in contract_symbols if symbol.ID.Date == expiry and symbol.ID.OptionRight == OptionRight.Call]
self.contract_symbol = sorted(filtered_symbols, key=lambda symbol: symbol.ID.StrikePrice)[0]
And then I changed the Market Orders to order the ‘contract_symbol’ instead of symbol.Symbol:
# self.ticket = self.MarketOrder(self.symbol.Symbol, -(shares_to_buy))
self.ticket = self.MarketOrder(self.contract_symbol, -(shares_to_buy))
Now here is the problem:
I mess around with the code and it will either produce one of two errors.
Runtime Error: This asset symbol (AAPL XUSJM7EPSLUU|AAPL R735QTJ8XC9X) was not found in your security list. Please add this security or check it exists before using it with 'Securities.ContainsKey("AAPL XUSJM7EPSLUU|AAPL R735QTJ8XC9X")' in SecurityManager
############ OR ############
OptionRight is only defined for SecurityType.Option, SecurityType.FutureOption, and SecurityType.IndexOption in SecurityIdentifier
Does anyone have any idea what is happening! Thank you to all those who took the time and effort to read this. I am looking forward to hear from my fellow Quants.
Best,
Jesse
Derek Melchin
Hi Jesse,
We need to subscribe to the Option contract before we can trade it.
Best,
Derek Melchin
Want to invest in QuantConnect as we build the Linux of quant finance? Checkout our Wefunder campaign to join the revolution.
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.
Jesse Fleming
Hey Derek Melchin,
Such a simple fix, thank you for pointing this out for me.
I now must bring these two errors to your attention:
Here is what the Option code looks like now:
Let me know what you think, sir! Thank you for taking the time to help me out. This community is remarkable.
Best,
Jesse
Derek Melchin
Hi Jesse,
We were unable to reproduce this error with just the code snippets above. For the best assistance, please attach a full algorithm so we can reproduce the error. We can only provide limited guidance at this point.
The error message is likely because the contract expired after a while and the algorithm is still trying to trade it. Instead of adding the contract selection logic to Initialize, add it to OnData before you place a buy order so that the algorithm only buys contracts that are currently trading. Alternatively, instead of subscribing to a single contract, we could add an Option universe.
Best,
Derek Melchin
Want to invest in QuantConnect as we build the Linux of quant finance? Checkout our Wefunder campaign to join the revolution.
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.
Jesse Fleming
This was phenomenal advice! Allow me to give you all the code I am working with so that you can reproduce the same error!
Here is the error:
Let me know what you think! Thank you for your efforts, much appreciated friend
Best,
Jesse
Nico Xenox
Hey Jesse Fleming,
if you're requesting/accessing and buying the contract at the same time, it has no time to get the actual value.
This means you will have to add this to your code:
Also because we are buying Options and not Stock I calculated the amount from the askPrice. TO access this I did the following:
Jesse Fleming
Nico Xenox This is phenomenal work! Thank you so much for putting in that effort to fix that problem!
As I begin to implement the actual strategy code, I have come across something so weird. Every time I have the EMA logic commented out, it will trade properly and buy as it should, every time I have the EMA logic in, it will not. What is happening here? The only difference is that I am commenting out the EMA or not.
For the sake of being proactive, I have one more question. I will be adding in parameters for entry, and that is all good as you have solved the mystery,
but how does one sell half or even liquidate options? And how does someone short? Is it as simple as replacing Call with Put in the entry logic?
In short, check out this backtest and notice that it buys. Then comment in:
and notice how it will not buy anymore.
Then, how do you liquidate / sell half when dealing with options.
Finally, is shorting as easy as replacing Call with Put?
Thank you team for your combined effort!
Best,
Can't wait to hear back,
Jesse
Yuri Lopukhov
Hi, Nico Xenox this is a very common mistake, it appears here pretty regularly, perhaps QC team would consider making this a default behaviour?
Nico Xenox
Hey Yuri Lopukhov,
are you talking about the Initializer? If so, yes, there are multiple posts asking the same question. Tbh I dont really know why it isnt a default behaviour or what the consequences might be if it would be one. Maybe different problems would come up, etc. but it would be interesting to here from the QC Team about this.
Nico Xenox
Hey Jesse Fleming,
so it seems that it buys more options that it should. The reason for that is that the AskPrice is 0.1. Im not sure if this is because the option doesnt have data or something else but you could add an if statement to check if the AskPrice or Volume is above a certain number (example: 1).
Jesse Fleming
Nico Xenox Wow. This is something I would not have caught in a million years. Thank you for that line. If you do not mind me diverting our attention to this EMA behaviour.
Does this in essence mean I cannot have entries dependant on EMA's? I am confused about that.
As well, how does one go about shorting options, is it as simple as ordering Puts?
How does one go about selling half / liquidating positions while invested in options? Is it the same as you would if you were invested in Equities?
Thank you for your support! Yuri Lopukhov I hope the team gets back to us too!
Best,
Jesse
Nico Xenox
Hey Jesse Fleming,
You can use EMA if you have the underlying, the stock, added to your universe. If the EMA emits an signal you then buy a option from that underlying.
Buying puts is the same as buying calls, just make sure you have the right contracts collected (PUTS).
I highly advice you to take a look at this course from QC about options😊:
⇒How does one go about selling half / liquidating positions while invested in options? Is it the same as you would if you were invested in Equities?
You would have to select the contracts you want to sell. Be careful with liquidating positions, make sure to understand the liquidate function before putting it to use. Because this simply closes all the open contracts/stocks you have.
Jesse Fleming
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!