Hello, I'm working on a project to trigger new SPXW 0DTE trades on QC using my google sheets here.
My entry and exit logic is coded outside of QC and I want to keep it that way. I want to use QC for backtesting based on the sheet data and also trigger execute trades if there's a new row detected. The execution logic uses bid and ask midpoint of the debit spread options combo and update the limit order every 3 seconds to stay competitive at the mid.
There are no problems with the Google Sheet data ingestion as my debugger shows
Sharing the full code below, any ideas how I can get past this?
from QuantConnect.Algorithm import QCAlgorithm
from QuantConnect.Data.Custom import *
from QuantConnect.Orders import *
from QuantConnect.Securities.Option import OptionPriceModels
from datetime import timedelta, datetime
import csv
import io
class SPXWTradingAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2023, 10, 30)
self.SetCash(100000)
spx = self.AddIndex("SPX").Symbol
option = self.AddIndexOption(spx, "SPXW") # SPXW is the target non-standard contract
option.SetFilter(lambda universe: universe.IncludeWeeklys().Strikes(-2, 2).Expiration(timedelta(0), timedelta(2))) # Note: IncludeWeeklys and limit to 2 days
self.symbol = option.Symbol
option.PriceModel = OptionPriceModels.CrankNicolsonFD()
option.EnableGreekApproximation = True
self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.Every(timedelta(seconds=30)), self.Trade)
self.last_row = None
# Log the last row during initialization
self.Trade()
if self.last_row is not None:
self.Debug("Last row during initialization: " + str(self.last_row))
def Trade(self):
csv_string = self.Download('https://docs.google.com/spreadsheets/d/1wwadCU8msu6FEUJt1ANoZS2qMO2MWiheARrdm7zaQlM/export?format=csv')
csv_reader = csv.DictReader(io.StringIO(csv_string))
for i, order in enumerate(csv_reader):
self.last_row = order
if self.last_row is None:
self.Debug("Last row is None")
return
expiry = datetime.strptime(str(self.last_row['TWS Contract Date']), '%Y%m%d')
optionchain = self.OptionChainProvider.GetOptionContractList(self.symbol, self.Time.date())
contracts = [i for i in optionchain if i.ID.Date.date() == expiry.date()]
if len(contracts) < 1:
self.Debug(f"Not enough option contracts for order: {self.last_row}")
return
contracts.sort(key=lambda x: abs(x.ID.StrikePrice - float(self.last_row['Strike 1'])))
quantity = int(self.last_row['Order Quantity'])
if self.Securities.ContainsKey(contracts[0]):
contract = self.Securities[contracts[0]]
legs = []
total_limit_price = 0
for i in range(1, 3):
if self.last_row[f'Right {i}']:
right = OptionRight.Call if self.last_row[f'Right {i}'] == 'C' else OptionRight.Put
limit_price = (contract.AskPrice + contract.BidPrice) / 2
legs.append(Leg.Create(contracts[i-1], quantity, limit_price))
total_limit_price += limit_price
self.Debug(f"Added leg: {contracts[i-1].Symbol.Value}, {right}, {quantity}")
ticket = self.ComboLegLimitOrder(legs, total_limit_price)
self.Debug(f"Symbol: {ticket.Symbol}; Quantity filled: {ticket.QuantityFilled}; Fill price: {ticket.AverageFillPrice}")
def OnOrderEvent(self, orderEvent):
if orderEvent.Status == OrderStatus.Filled:
self.Debug("Order filled: " + str(orderEvent))
Ashutosh
Hello John,
I was going through the code and saw some flow mistakes with your code.
This piece of code returns a list of Symbol objects and not contracts which was the major error in the code and broke the entire flow.
To get option contracts you would need to use OptionChains from slice object. To get get slice object in your custom method you can make use of CurrentSlice feature in QuantConnect.
The second error breaking the code logic was how you handled the ComboLegLimitOrder feature.
Referring to the QuantConnect documents:
In the code you provided all the quantities were positive and were solid values instead of set ratios.
After making relevant changes to the code I have attached a backtest and would love to hear your review.
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.
John Ener
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!