Overall Statistics |
Total Trades 4 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio 0 Tracking Error 0 Treynor Ratio 0 Total Fees $4.00 Estimated Strategy Capacity $140000000.00 Lowest Capacity Asset GOOCV VP83T1ZUHROL |
from AlgorithmImports import * from OrderStatuses import OrderStatusCodes ''' Class to handle bracket orders TODO: CREATE STOP LOSS AND PROFIT ORDERS BASED ON THE ACTUAL FILL PRICE ''' class BracketOrder: def __init__(self, algorithm, symbol, totQuantity, placementPrice, limitPrice, stopPrice, profitPrice): self.algorithm = algorithm self.symbol = symbol self.totQuantity = totQuantity self.placementPrice = placementPrice self.limitPrice = limitPrice self.stopPrice = stopPrice self.profitPrice = profitPrice # Initialize future properties self.filledQuantity = 0 self.parentTicket = None self.stopOrderTicket = None self.profitOrderTicket = None # Place initial order self.PlaceInitialOrder() ''' Place initial order ''' def PlaceInitialOrder(self): self.parentTicket = self.algorithm.StopLimitOrder(self.symbol, self.totQuantity, self.placementPrice, self.limitPrice, tag="Initial order") self.algorithm.bracketOrders[self.parentTicket.OrderId] = self self.algorithm.Debug(f"Parent order placed for {self.symbol}") return ''' Handle order changes Orderstatus enum: https://www.quantconnect.com/docs/algorithm-reference/trading-and-orders#Trading-and-Orders-Tracking-Order-Events ''' def HandleOrderChange(self, orderEvent): # If the orderstatus is PartiallyFilled or Filled it will have a filledQuantity > 0 # Currently make no changes for order statuses that does not (partially) fill an order eventFillQuantity = orderEvent.FillQuantity if (orderEvent.FillQuantity == 0): return ''' Handle scenario where parent order is updated Add/update stop and profit orders ''' if (orderEvent.OrderId == self.parentTicket.OrderId): self.algorithm.Debug(f"Parent order ({self.symbol}) {OrderStatusCodes[orderEvent.Status]}, quantity: {eventFillQuantity}") self.filledQuantity += eventFillQuantity # Place/add to stop order (negative because we are selling) self.PlaceStopOrder(-eventFillQuantity) # Place/add to profit order (negative because we are selling) self.PlaceProfitOrder(-eventFillQuantity) return ''' Handle scenario where stop loss is hit - Cancel all open orders on the symbol - Liquidate portfolio of that stock (should not be necessary, but extra precaution) ''' if (orderEvent.OrderId == self.stopOrderTicket.OrderId): self.CancelTrade() self.algorithm.Debug(f"Stop order ({self.symbol}) {OrderStatusCodes[orderEvent.Status]}, quantity: {eventFillQuantity}") return ''' Handle scenario where profit target is hit - Cancel all open orders on the symbol (for now) - Should in reality check if profit is partially filled (not equal to self.filledQuantity), and update stop loss order - Should probably cancel parent order in case it is partially filled ''' if (orderEvent.OrderId == self.profitOrderTicket.OrderId): self.algorithm.Debug(f"Profit order ({self.symbol}) {OrderStatusCodes[orderEvent.Status]}, quantity: {eventFillQuantity}") self.CancelTrade() return ''' Place/add to stop market order when the parent order is (partially) filled ''' def PlaceStopOrder(self, stopQuantity): # Create new profit order ticket if non exists if (self.stopOrderTicket is None): self.stopOrderTicket = self.algorithm.StopMarketOrder(self.symbol, stopQuantity, self.stopPrice, tag=f"Stop order ({self.stopPrice})") # Connect the stop order ticket to this object self.algorithm.bracketOrders[self.stopOrderTicket.OrderId] = self self.algorithm.Debug(f"Stop order placed ({self.symbol})") return # Update existing stop order ticket updateSettings = UpdateOrderFields() updateSettings.Quantity = stopQuantity response = self.stopOrderTicket.Update(updateSettings) self.algorithm.Debug(f"Stop order updated ({self.symbol})") return ''' Place/add to profit taking order when the parent order is (partially) filled ''' def PlaceProfitOrder(self, profitQuantity): # Create new profit order ticket if non exists if (self.profitOrderTicket is None): self.profitOrderTicket = self.algorithm.LimitOrder(self.symbol, profitQuantity, self.profitPrice, tag=f"Profit order ({self.profitPrice})") # Connect the stop order ticket to this object self.algorithm.bracketOrders[self.profitOrderTicket.OrderId] = self self.algorithm.Debug(f"Profit order placed ({self.symbol})") return # Update existing profit order ticket updateSettings = UpdateOrderFields() updateSettings.Quantity = profitQuantity response = self.profitOrderTicket.Update(updateSettings) self.algorithm.Debug(f"Profit order updated ({self.symbol})") return ''' Cancel trade ''' def CancelTrade(self): # Cancel open orders and liquidate position (pre-caution) self.algorithm.Transactions.CancelOpenOrders(self.symbol) self.algorithm.Liquidate(self.symbol, tag="LIQUIDATION ORDER INSIDE") # Remove order IDs from bracketOrders dictionary # self.algorithm.bracketOrders.pop(self.parentTicket.OrderId, None) # self.algorithm.bracketOrders.pop(self.profitOrderTicket.OrderId, None) # self.algorithm.bracketOrders.pop(self.stopOrderTicket.OrderId, None) return
#region imports from AlgorithmImports import * #endregion OrderStatusCodes = { 0:'NEW', # new order pre-submission to the order processor 1:'SUBMITTED', # order submitted to the market 2:'PARTIALLY FILLED', # partially filled, in market order 3:'FILLED', # completed, filled, in market order 5:'CANCELED', # order cancelled before filled 6:'NONE', # no order state yet 7:'INVALID', # order invalidated before it hit the market (e.g. insufficient capital) 8:'CANCEL PENDING', # order waiting for confirmation of cancellation 9:'UPDATE SUBMITTED' # Order update submitted to the market }
from AlgorithmImports import * from BracketOrder import BracketOrder class TestBracketOrder(QCAlgorithm): def Initialize(self): self.SetStartDate(2022, 6, 1) # Set Start Date self.SetEndDate(2022, 6, 1) # Set End Date self.SetCash(100000) # Set Strategy Cash # Ensure securities are set with adjusted data normalization self.SetSecurityInitializer(lambda x: x.SetDataNormalizationMode(DataNormalizationMode.Adjusted)) # Add SPY for scheduled events self.AddEquity("SPY", resolution=Resolution.Minute, extendedMarketHours=True) # Create scheduled event to liquidate open orders and positions at end of day self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 30), self.EndOfDay) # Add testing assets self.assets = ["GOOG", "AMZN"] for asset in self.assets: self.AddEquity(asset, Resolution.Minute, extendedMarketHours=True) # Initiate bracket order handling dictionary self.bracketOrders = {} self.tickets = [] self.liquidatedOrderIds = [] self.currentLiquidated = None def OnData(self, data: Slice): """OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the stock data """ for bar in data.Bars.Values: if (bar.Time.hour == 9 and bar.Time.minute == 30 and bar.Symbol != "SPY"): symbol = bar.Symbol # Create stop limit order on break of max open/close placementPrice = max(bar.Close, bar.Open) limitPrice = placementPrice + 0.05 qty = 100 # Place order - initiate bracket order object BracketOrder(self, symbol, qty, placementPrice, limitPrice, placementPrice * 0.98, placementPrice * 1.02) # self.Debug(f"Symbol: {bar.Symbol}, Time Bar {bar.Time}, CLOSE {bar.Close}") def OnOrderEvent(self, orderEvent: OrderEvent): # If portfolio liquidation happens from scheduled event at the end of the day, no bracketorder object is created and can not be found in the bracketorders dictionary # Just ignore liquidation orders - they are tagged by "LIQUIDATION ORDER" order = self.Transactions.GetOrderById(orderEvent.OrderId) if ("LIQUIDATION ORDER" in order.Tag): return # If the order status is NEW or SUBMITTED, no changes are made to the order if (orderEvent.Status == OrderStatus.New or orderEvent.Status == OrderStatus.Submitted): return # For any other order statuses we expect the order to be in the dictionary of orders bracketOrder = self.bracketOrders[orderEvent.OrderId] # Make changes in the bracket order object bracketOrder.HandleOrderChange(orderEvent) ''' Liquidate portfolio and open orders at end of day ''' def EndOfDay(self): self.Transactions.CancelOpenOrders() self.currentLiquidated = self.Liquidate(tag="LIQUIDATION ORDER") self.Debug(f"Liquidation performed: {self.currentLiquidated}")
#region imports from AlgorithmImports import * #endregion ''' Random stuff if (orderEvent.OrderId not in self.bracketOrders): if (orderEvent.FillQuantity != 0): self.Debug(f"{self.Time}, Order quantity not zero: {orderEvent.FillQuantity}") assert (1 == 2), "Order quantity not zero" else: return // Place your key bindings in this file to override the defaultsauto[] [ { "key": "ctrl+k", "command": "editor.action.commentLine", "when": "editorTextFocus && !editorReadonly" }, { "key": "ctrl+/", "command": "-editor.action.commentLine", "when": "editorTextFocus && !editorReadonly" } ] # Order partially filled - cancel remaining parent order, update stop order self.filledQuantity += eventFillQuantity cancelRes = self.parentTicket.Cancel("Canceled remaining parent order") # Update existing stop order ticket updateSettings = UpdateOrderFields() updateSettings.Quantity = self.filledQuantity updateRes = self.stopOrderTicket.Update(updateSettings) return '''