Hi guys,
I'm stumped - I execute the code attached, which aims to send out a bracket order, conditional on the original entry ticket being filled, but for some reason, the the child stop market / limit orders do not get filled despite price moving through them within any given day. The end of day scheduler catches the order and liquidates but the log shows no sign on any intermediate orders being filled, once the orignal entry gets laid. I've experiemented with moving the limit order / stop order inside the ‘OnData] thus trying to execute everything at once but I would have thought that the onOrderEvent approach would work. I fear that there is some kind of indentation error that is sneakily passing the compiler which I’m failing to see. Any tips would be greatly appreciated. FYI: This is my first go at an algorithm so apologies for any sloppy code! Its an attempt at creating an hourly openingrange breakout using the rolling window etc.
# region imports
from AlgorithmImports import *
# endregion
class HipsterRedBull(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2022, 11 , 22) # Set Start Date
self.SetEndDate(2022, 11, 22)
self.SetCash(100000) # Set Strategy Cash
self.symbol = self.AddEquity("SPY", Resolution.Minute).Symbol
self.Consolidate("SPY", timedelta(minutes=30), self.CustomBarHandler)
self.window = RollingWindow[TradeBar](2)
self.entryTicket = None
self.StopMarketTicket = None
self.TPTicket = None
self.entryTime = datetime.min
self.exitTime = datetime.min
self.bias = 0
self.Schedule.On(self.DateRules.EveryDay(self.symbol), self.TimeRules.BeforeMarketClose(self.symbol, 1) , self.onMarketClose)
def onMarketClose(self):
self.Liquidate()
self.window.Reset()
self.exitTime = self.Time
def CustomBarHandler(self, bar):
if self.Time.hour == 10:
self.window.Add(bar)
def OnData(self, data):
if self.Time.date() == self.exitTime.date() or not self.window.IsReady or self.Portfolio.Invested:
return
# wait at least 1 day before last exit before making another trade
# if rollingwindow is ready and portfolio is not invested
# if close price is above or below opening range high or low, send market order
# record entry time
maxprice = max(self.window[0].High, self.window[1].High)
minprice = min(self.window[0].Low, self.window[1].Low)
self.Plot("Opening Range","OR Max", maxprice)
self.Plot("Opening Range","OR Min", minprice)
if data[self.symbol].Close > maxprice:
self.entryTicket = self.MarketOrder(self.symbol, 100)
self.bias = 1
self.entryTime = self.Time
if data[self.symbol].Close < minprice:
self.entryTicket = self.MarketOrder(self.symbol, -100)
self.bias = -1
self.entryTime = self.Time
def OnOrderEvent(self, orderEvent):
if orderEvent.Status != OrderStatus.Filled:
return
if self.entryTicket is not None and self.entryTicket.OrderId == orderEvent.OrderId:
self.StopMarketTicket = self.StopMarketOrder(self.symbol, -self.entryTicket.QuantityFilled, self.entryTicket.AverageFillPrice - self.bias * 0.5)
self.TPTicket = self.LimitOrder(self.symbol, -self.entryTicket.QuantityFilled, self.entryTicket.AverageFillPrice + self.bias * 0.5)
# if entry ticket exists send out stop market order for stop loss
# if entry ticket exists send out limit order to take profits
# if either take profits or stop loss is hit, record time of order fill
if self.StopMarketTicket is not None and self.StopMarketTicket.OrderId == orderEvent.OrderId:
self.exitTime = self.Time
if self.TPTicket is not None and self.TPTicket.OrderId == orderEvent.OrderId:
self.exitTime = self.Time
Louis Szeto
Hi Alun
This is about the flow of the algorithm. The problem came from the line `self.entryTicket = self.MarketOrder(self.symbol …)`. You're assuming it will finish this line before the other parts are called, but things are going before this line is finished and even async in live mode. The actual flow is like this:
By the above flow, the OrderId was never matched, so the stop loss and profit taker are never submitted. Refer to the attached backtest for a solution to this issue.
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.
Alun Thomas
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!