Hello QuantConnect Community!
I am trying to backtest the following strategy:
- Each day open a short SPY straddle at 9:35am ET at mid spread price using 0DTE
- Close the position if 10% profit is met
- If profit not met, close the position at 12pm ET. Don't hold the position overnight.
- This is sequential, meaning one position at a time
When I am running the strategy, the position is not opening each day and closing each day. For example, the position would open at 9:35am certain day but close several days or even a month later. Like it is inconsistent. I am not sure what I am doing wrong in my code.
I provided some inline comments for better clarity. I am fairly new to QuantConnect so any great feedback would be great. Thank you.
Here is the code:
from AlgorithmImports import *
from QuantConnect.DataSource import *
import datetime
class USEquityOptionsDataAlgorithm(QCAlgorithm):
def Initialize(self) -> None:
self.SetStartDate(2022, 1, 1)
self.SetEndDate(2022, 12, 31)
self.SetCash(100000)
# Requesting data
self.underlying = self.AddEquity("SPY").Symbol
option = self.AddOption("SPY")
self.option_symbol = option.Symbol
# Set our strike/expiry filter for this option chain
option.SetFilter(0, 0)
self.contract_call = None
self.contract_put = None
self.entry_time = datetime.time(hour=9, minute=35)
self.exit_time = datetime.time(hour=12)
self.profit_target = 0.1
self.position_open = False
self.entry_price = None
def OnData(self, slice: Slice) -> None:
if self.Portfolio[self.underlying].Invested:
self.Liquidate(self.underlying)
if self.contract_call is not None and self.Portfolio[self.contract_call.Symbol].Invested:
return
chain = slice.OptionChains.get(self.option_symbol)
if chain:
# Select call and put contracts with zero days to expiration
zero_expiry_calls = [contract for contract in chain if contract.Expiry.date() == self.Time.date() and contract.Right == OptionRight.Call]
zero_expiry_puts = [contract for contract in chain if contract.Expiry.date() == self.Time.date() and contract.Right == OptionRight.Put]
if len(zero_expiry_calls) == 0 or len(zero_expiry_puts) == 0:
return
# Select the call and put contracts with the nearest strike to the underlying price
nearest_strike_call = min(zero_expiry_calls, key=lambda x: abs(chain.Underlying.Price - x.Strike))
nearest_strike_put = min(zero_expiry_puts, key=lambda x: abs(chain.Underlying.Price - x.Strike))
self.contract_call = nearest_strike_call
self.contract_put = nearest_strike_put
current_time = self.Time
current_price = self.Securities[self.contract_call.Symbol].Price
if current_time.time() == self.entry_time and not self.position_open and self.contract_call.Expiry.date() == self.Time.date():
# Open short straddle position
self.entry_price = current_price
self.position_open = True
self.MarketOrder(self.contract_call.Symbol, -1) # Short the call option
self.MarketOrder(self.contract_put.Symbol, -1) # Short the put option
if (current_time.time() >= self.exit_time or self.is_profit_target_met(self.entry_price, current_price)) and self.position_open:
# Close position if profit target is met or it's after 12pm ET
self.exit_price = current_price
self.profit = self.calculate_profit(self.entry_price, self.exit_price)
self.Liquidate(self.contract_call.Symbol) # Close the call option
self.Liquidate(self.contract_put.Symbol) # Close the put option
self.Debug(f"Closed position at {current_time}: Profit = {self.profit}")
self.position_open = False
def calculate_profit(self, entry_price, exit_price):
if entry_price is None or exit_price is None:
return 0.0
return exit_price - entry_price
def is_profit_target_met(self, entry_price, exit_price):
profit = self.calculate_profit(entry_price, exit_price)
return profit >= self.profit_target
Louis Szeto
Hi Peter
I believe there are some logic “holes” needed to be filled up there:
Best
Louis
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.
Peter
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!