Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

Handling CancelSubmitted

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Handling CancelSubmitted

    In OnExecutionUpdate(), I am trying to handle changing my SL & TP each time my strategy adds to, or partial closes, a trade position.

    Code:
            protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity,
                                                      Cbi.MarketPosition marketPosition, string orderId, DateTime time)
            {
                /// Handle actions for this execution being an entry order
                if (execution.IsEntryStrategy)
                {
                    /// Place SL and (multiple) TP levels
                    if (marketPosition == MarketPosition.Long)
                    {
                        if (StopLoss != 0)
                            slExitOrder = ExitLongStopMarket(0, true, Position.Quantity, price - StopLoss*TickSize, "Stop Loss", "Long");
    
                        /// Seems what we need is to delete old TP and enter new one for all lots based on (execution.AverageFillPrice + TakeProfit*TickSize)
                        if (TakeProfit > 0)
                            tpExitOrder = ExitLongLimit(0, true, Position.Quantity, Position.AveragePrice + TakeProfit*TickSize, "Take Profit", "Long");
                    }
                    else
                    {
                        if (StopLoss != 0)
                            slExitOrder = ExitShortStopMarket(0, true, Position.Quantity, price + StopLoss*TickSize, "Stop Loss", "Short");
                        
                        /// Seems what we need is to delete old TP and enter new one for all lots based on (execution.AverageFillPrice + TakeProfit*TickSize)
                        if (TakeProfit > 0)
                            tpExitOrder = ExitShortLimit(0, true, Position.Quantity, Position.AveragePrice - TakeProfit*TickSize, "Take Profit", "Short");
                    }
                    /// Save the bar index of this entry; CurrentBar zero index on first (leftmost) bar
                    if (firstEntryBarIdx == -1)
                        firstEntryBarIdx = CurrentBar;
                }
    
                /// Handle actions for this execution being an exit order ('exit strategy')
                else if (execution.Order.OrderAction == OrderAction.Sell  ||  execution.Order.OrderAction == OrderAction.BuyToCover)
                {
                    if (Position.Quantity > 0)
                    {
                        if (slExitOrder != null)
                        {
                            Log($"Trade Closed; attempting to adjust SL order quantity to {Position.Quantity}...");
                            
                            /// Verify slExitOrder state before updating
                            if (slExitOrder.OrderState == OrderState.Working  ||  slExitOrder.OrderState == OrderState.Accepted)
                            {
                                /// Turns out the marketPosition passed in may not be the same as our actual position direction,
                                /// perhaps because this is called in reference to the stop postion(??).  So get correct one
                                var currentMarketPosition = Position.MarketPosition;
                
                                if (currentMarketPosition == MarketPosition.Long)
                                    slExitOrder = ExitLongStopMarket(0, true, Position.Quantity, slExitOrder.StopPrice, "Stop Loss", "Long");
                                else if (currentMarketPosition == MarketPosition.Short)
                                    slExitOrder = ExitShortStopMarket(0, true, Position.Quantity, slExitOrder.StopPrice, "Stop Loss", "Short");
                                else
                                    Log($"...but currentMarketPosition is not Long OR Short!!!!!!");
                            }
                            else
                                Log($"...but Stop Order not in a modifiable state; Current state = {slExitOrder.OrderState.ToString()}");
                        }
                        else
                            Log($"...but Stop Order == NULL, so...No stop order to modify??");
                        
                        if (tpExitOrder != null)
                        {
                            Log($"Trade Closed; attempting to adjust TP order quantity to {Position.Quantity}...");
                            
                            /// Verify slExitOrder state before updating
                            if (tpExitOrder.OrderState == OrderState.Working  ||  tpExitOrder.OrderState == OrderState.Accepted)
                            {
                                var currentMarketPosition = Position.MarketPosition;
                
                                if (currentMarketPosition == MarketPosition.Long)
                                    tpExitOrder = ExitLongLimit(0, true, Position.Quantity, Position.AveragePrice + TakeProfit*TickSize, "Take Profit", "Long");
                                else if (currentMarketPosition == MarketPosition.Short)
                                    tpExitOrder = ExitShortLimit(0, true, Position.Quantity, Position.AveragePrice - TakeProfit*TickSize, "Take Profit", "Short");
                                else
                                    Log($"...but currentMarketPosition is not Long OR Short!!!!!!");
                            }
                            else
                                Log($"...but TP Order not in a modifiable state; Current state = {tpExitOrder.OrderState.ToString()}");
                        }
                        else
                            Log($"...but TP Order == NULL, so...No stop order to modify??");
                    }
                    
                    /// Reset some vars if flat
                    else if (Position.Quantity == 0)
                    {
                        beTriggered1Price = beTriggered2Price = beTriggered3Price = beTriggered4Price = 0;
                        trailTriggered = false;
                        firstEntryBarIdx = -1;
                    }
                }
            }
    But when I closed one contract (of 3), I got this logging:

    Trade Closed; attempting to adjust SL order quantity to 2...
    ...but Stop Order not in a modifiable state; Current state = CancelSubmitted
    Trade Closed; attempting to adjust TP order quantity to 2...
    ...but Stop Order not in a modifiable state; Current state = CancelSubmitted​

    So if the state of my order is not working or accepted... I assume this CancelSubmitted​ was temporary, so... what would be the appropriate way to revisit this and get that SL & TP quantity updated?

    #2
    Hello DerkWehler,

    I am not certain I follow what you are trying to do here. Are you saying that you are closing part of the position in another way besides using the targets?

    If part of your target fills you would not need to do anything, that will directly remove that quantity from the position and the target will be part filled.

    Comment


      #3
      Trade quantity is only reduced by hitting TPs. But I noticed early-on that when part of a position closed, the SL & TP orders did not reduce in size, so I had to add code to reduce them, else I worried that (for example) it might be in 2 Short contracts but have a limit order for 3 which, when hit, would leave me one contract Long instead of Flat.

      Comment


        #4
        Hello DerkWehler,

        How are you closing the position?

        The stops and targets will only reduce in size if your strategy is closing that position itself. If you are running more than one strategy or manually trading that won't allow the strategy to work correctly.

        Comment


          #5
          Originally posted by NinjaTrader_Jesse View Post

          How are you closing the position?

          The stops and targets will only reduce in size if your strategy is closing that position itself. If you are running more than one strategy or manually trading that won't allow the strategy to work correctly.
          Thanks for the reply. I will need to back up a little to explain. This strategy was templated from a previous one I built. It opened 10 contracts and had 3 different TP levels. When it hit TP1, it closed 3; TP2 closed 3, and TP3 closed 4. So the code to set those stops looked like this:

          Code:
                  protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity,
                                                            Cbi.MarketPosition marketPosition, string orderId, DateTime time)
                  {
                      /// Handle actions for this execution being an entry order.  
                      if (execution.IsEntryStrategy)
                      {
                          /// Place SL and (multiple) TP levels
                          if (marketPosition == MarketPosition.Long)
                          {
                              if (StopLoss != 0)
                                  stopOrder = ExitLongStopMarket(0, true, Position.Quantity, price - StopLoss*TickSize, "Stop Loss", "Long");
                              if (TakeProfit1 > 0  &&  QuantityTP1 > 0)
                                  ExitLongLimit(0, true, QuantityTP1, price + TakeProfit1*TickSize, "Take Profit 1", "Long");
                              if (TakeProfit2 > 0  &&  QuantityTP2 > 0)
                                  ExitLongLimit(0, true, QuantityTP2, price + TakeProfit2*TickSize, "Take Profit 2", "Long");
                              if (TakeProfit3 > 0  &&  QuantityTP3 > 0)
                                  ExitLongLimit(0, true, QuantityTP3, price + TakeProfit3*TickSize, "Take Profit 3", "Long");
                          }
                          else
                          {
                              if (StopLoss != 0)
                                  stopOrder = ExitShortStopMarket(0, true, Position.Quantity, price + StopLoss*TickSize, "Stop Loss", "Short");
                              if (TakeProfit1 > 0  &&  QuantityTP1 > 0)
                                  ExitShortLimit(0, true, QuantityTP1, price - TakeProfit1*TickSize, "Take Profit 1", "Short");
                              if (TakeProfit2 > 0  &&  QuantityTP2 > 0)
                                  ExitShortLimit(0, true, QuantityTP2, price - TakeProfit2*TickSize, "Take Profit 2", "Short");
                              if (TakeProfit3 > 0  &&  QuantityTP3 > 0)
                                  ExitShortLimit(0, true, QuantityTP3, price - TakeProfit3*TickSize, "Take Profit 3", "Short");
                          }
                      }
                  }
          But I noticed when it ran, and TP1 was hit, it would close 3 contracts and leave this:


          Click image for larger version  Name:	image.png Views:	0 Size:	52.5 KB ID:	1315849

          New to NT8 at that time, I concluded I needed to add specific code to reduce the lots on the Sell STP. So I added code for that:

          Code:
                  protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity,
                                                            Cbi.MarketPosition marketPosition, string orderId, DateTime time)
                  {
                      if (execution.IsEntryStrategy)
                      {
                          // code like above
                      }
                      /// Handle actions for this execution being an exit order ('exit strategy')
                      else if (execution.Order.OrderAction == OrderAction.Sell  ||  execution.Order.OrderAction == OrderAction.BuyToCover)
                      {
                          /// Derk 3Jul24: Added to try to update stop loss order quantity (after partial close on TP)
                          if (Position.Quantity > 0)
                          {
                              if (stopOrder != null)
                              {
                                  Log($"Trade Closed; attempting to adjust SL order quantity to {Position.Quantity}...");
                                  
                                  /// Verify stopOrder state before updating
                                  if (stopOrder.OrderState == OrderState.Working  ||  stopOrder.OrderState == OrderState.Accepted)
                                  {
                                      /// Turns out the marketPosition passed in may not be the same as our actual position direction,
                                      /// perhaps because this is called in reference to the stop postion(??).  So get correct one
                                      var currentMarketPosition = Position.MarketPosition;
                      
                                      if (currentMarketPosition == MarketPosition.Long)
                                          stopOrder = ExitLongStopMarket(0, true, Position.Quantity, stopOrder.StopPrice, "Stop Loss", "Long");
                                      else if (currentMarketPosition == MarketPosition.Short)
                                          stopOrder = ExitShortStopMarket(0, true, Position.Quantity, stopOrder.StopPrice, "Stop Loss", "Short");
                                      else
                                          Log($"...but currentMarketPosition is not Long OR Short!!!!!!");
                                  }
                                  else
                                      Log($"...but Stop Order not in a modifiable state; Current state = {stopOrder.OrderState.ToString()}");
                              }
                              else
                                  Log($"...but Stop Order == NULL, so...No stop order to modify??");
                          }
                      }
                  }
          You seem to be suggesting this should not happen. That when part of my position closes, the Stop quantity should auto-update(?) Because, as I show in the screen shot, when I disable the code which reduces the contracts, the quantity remains at 10...

          Comment


            #6
            Hello DerkWehler,

            In order to scale out you need to use multiple entries with unique signal names. Your exit orders all target the same entry so that won't work for scaling out.

            Comment


              #7
              Good to know. However, if I do not want to place them all as separate entries, then I need to do something like I have done in order to reduce the contracts when something closes.

              Particularly for the Strategy I made from this; the new one I was referencing in my initial post. It does not place a bunch of contracts at first and then close portions at different targets. It receives signals and adds to, or reduces, or reverses. Consider a position to be represented by a signed integer. So (e.g) 0 means flat, 3 means 3 contracts long, and -2 means 2 contracts short.

              When I get a signal, it will range from -4 to +4, and the new position I want is that signal value + current position. So if I am long 2 (position = +2), and I receive a -3, that means I want my new position to be the sum, -1 (1 contract short). Then if the next signal is -2, I open two more short. Then if I get +1, I close one of the shorts.

              I only use an optional [single] TP & SL for safety, but if enabled, I still need to adjust the SL& TP based on the average entry price.

              When I try to do this, sometimes it works, and sometimes I get that CancelSubmitted state (refer back to original post). Want to know what that state means and how I can get my stops moved please.

              Comment


                #8
                Hello DerkWehler,

                Cancel submitted means that the order is in the process of being cancelled. If you want to use a single entry and have unlinked exit orders you would have to switch to using the unmanaged approach to support that. In the managed approach when using signal names you need to use multiple entries so you can have multiple signal names for the exit orders to be tied to individually. When you use a single signal name that will change how scaling works and can result in incorrect target quantities or closing the whole position when only a part should be closed.

                To move orders you would need to either call the order method again with a new price or use ChangeOrder if you are using order variables.

                Comment


                  #9
                  I thought that was what I am doing, using the unmanaged approach, detailed in the code in my first post.

                  I am using this code to close all or partial position, and to place (all) the orders:

                  Code:
                      /// Convert current position to signed int (neg for short, pos for long)
                      int posSum = 0;
                      string posDir = Position.MarketPosition.ToString();
                      if (posDir != "Flat")
                      {
                          int m = (posDir == "Long") ? 1 : -1;
                          posSum = Position.Quantity * m;
                      }
                                                  
                      int newSum = posSum + signalSum;
                      bool needsOpen = false;
                      string logStr = "";
                      int qty = Math.Abs(signalSum);
                  
                      signalDir = (newSum > 0) ? "Long" : (newSum < 0) ? "Short" : "Flat";
                      Log($"Signal Received @ {DateTime.Now.ToString()} - New Direction = {signalDir}, Signal Sum = {signalSum}, old Pos Sum = {posSum}, New Pos Sum = {newSum}, QuantityMult = {QuantityMult}");
                  ​
                      /// A sum of zero means to go flat
                      if (signalDir == "Flat")    // Implies that current pos is not flat
                      {
                          Log($"\nCLOSING {posDir} POSITION\n");
                          if (posDir == "Long")
                              ExitLong("SigFlat", "Long");
                          else
                              ExitShort("SigFlat", "Short");
                      }
                                                  
                      /// Handle position size modification
                      else if (posDir == signalDir)
                      {
                          /// Open additional contracts
                          if ((posDir == "Long"   &&  signalSum > 0) || (posDir == "Short"  &&  signalSum < 0))
                          {
                              logStr = $"\nSignal to Increase: ENTERING ADDITIONAL {qty} CONTRACTS {signalDir.ToUpper()}\n";
                              needsOpen = true;
                          }
                          /// Close partial contracts
                          else
                          {
                              if ((posDir == "Long"   &&  signalSum < 0) || (posDir == "Short"  &&  signalSum > 0))
                              {
                                  Log($"\nSignal to Reduce: CLOSING {Math.Abs(QuantityMult * signalSum)} {signalDir.ToUpper()} CONTRACTS (out of {Position.Quantity})\n");
                                  if (posDir == "Long")
                                      ExitLong(qty, "SigReduce", "Long");
                                  else
                                      ExitShort(qty, "SigReduce", "Short");
                              }
                          }
                      }
                                                  
                      /// Reverse or (open new position if flat)
                      else if (posDir != signalDir)
                      {
                          string rev = (posDir == "Flat") ? "POSITION IS FLAT; " : "REVERSING {Position.Quantity} {posDir.ToUpper()} POSITION & ";
                          logStr = $"\nSignal to Flip or Open: {rev}ENTERING NEW {signalDir.ToUpper()} POSITION FOR {qty} CONTRACTS\n";
                          needsOpen = true;
                      }
                  ​
                      /// Open new pos (or reverse)
                      if (needsOpen)
                      {
                          if (TradeFiltersOkay(signalDir))
                          {
                              // Do not trade more than user-defined max
                              if (Position.Quantity + qty > MaxContracts)
                              {
                                  Log($"WARNING!! {Position.Quantity} Contracts already open, and max set to {MaxContracts}, so we cannot open {qty} more;  opening {MaxContracts - Position.Quantity} instead");
                                  qty = MaxContracts - Position.Quantity;
                              }
                              Log(logStr);
                              Log($"Before EntryLong/Short(), Position: {Position.MarketPosition}, Quantity: {Position.Quantity}");
                              Log($"Attempting to enter {signalDir} position for {qty} contracts");
                              if (signalDir == "Long")
                                  EnterLong(qty, "Long");
                              else
                                  EnterShort(qty, "Short");
                              lastTradeDir = signalDir;
                          }
                      }
                  As I mentioned, they seem to place / close correctly.

                  Because the code (in original post) checks the Order variables, I also have this code to keep them updated:

                  Code:
                          protected override void OnOrderUpdate(Cbi.Order order, double limitPrice, double stopPrice, int quantity,
                                                                int filled, double averageFillPrice, Cbi.OrderState orderState,
                                                                DateTime time, Cbi.ErrorCode error, string comment)
                          {
                              //Log($"order.Name = {order.Name},   order.OrderAction = {order.OrderAction},   Position.MarketPosition = {Position.MarketPosition},   limitPrice = {limitPrice}");
                  
                              if (order.Name == "Stop Loss")
                              {
                                  if (order.OrderState != OrderState.Filled)
                                      slExitOrder = order;
                                  else
                                      slExitOrder = null;
                              }
                              if (order.Name == "Take Profit")
                              {
                                  if (order.OrderState != OrderState.Filled)
                                      tpExitOrder = order;
                                  else
                                      tpExitOrder = null;
                              }
                          }
                  Given this code and the code from the original post, do you see a flaw in the programming; some reason I sometimes get CancelSubmitted?

                  Comment


                    #10
                    Hello DerkWehler,

                    In that code you are using a single signal name, long or short. If you try to close a partial position that can lead to the targets being out of sync because scaling in the managed approach requires using multiple signal names. For example if you wanted to enter with 4 quantity and scale 2 out you should have two entries long1 and long2. Then when you exit 2 you can specify part of the position by using just long2 as the from entry signal name. There is an example of scaling out in the link below.



                    Comment


                      #11
                      Thanks for the reply. While that might have worked for the EA that entered 3 at once, then scaled out, it does not seem practical for this strategy because it can have unlimited instances of adding and removing contracts, and therefore an indefinite amount of entry signal names.

                      And as mentioned, the code does appear to work, adding or reducing contracts (and worked fine for adjusting SL contracts on my original Strategy). I only have trouble adjusting the volume of the SL & TP due to the state of the order. Are you saying that the CancelSubmitted state is due to some kind of sync issue?

                      Do I need to use a completely unmanaged approach?

                      Comment


                        #12
                        Hello DerkWehler,

                        In that case I would suggest using the unmanaged approach which does not use signal names. That lets you just submit orders that are not linked based on the signal name.

                        Comment


                          #13
                          Originally posted by NinjaTrader_Jesse View Post
                          In that case I would suggest using the unmanaged approach which does not use signal names. That lets you just submit orders that are not linked based on the signal name.
                          I have converted everything to unmanaged, and mostly working well. But in my log, I get this when I close a position (TraceOrders enabled):



                          101 - MES 09-24 - [OnOrderUpdate] - 06:38:53 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

                          Strategy 'Ladder#1/332259020': Cancelled custom managed order: orderId='b42e7d3fb9724d40a66797aecd2ed0b3' account='Sim101' name='TakeProfit3' orderState=CancelSubmitted instrument='MES 09-24' orderAction=Sell orderType='Limit' limitPrice=5599.75 stopPrice=0 quantity=4 tif=Gtc oco='b7f9ed1beca44602a1dc4629e8801ab1_LongExits' filled=0 averageFillPrice=0 onBehalfOf='' id=4743 time='2024-09-06 06:38:53' gtd='2099-12-01' statementDate='2024-09-06'

                          Strategy 'Ladder#1/332259020': Entered internal SubmitOrderUnmanaged() method at 06-Sep-24 06:38:53: BarsInProgress=0 Action=Sell OrderType=Market Quantity=4 LimitPrice=0 StopPrice=0 SignalName='Reverse'

                          Strategy 'Ladder#1/332259020': Entered internal SubmitOrderUnmanaged() method at 06-Sep-24 06:38:53: BarsInProgress=0 Action=SellShort OrderType=Market Quantity=10 LimitPrice=0 StopPrice=0 SignalName='Entry'

                          Unsupported order type. [Market] Skipping.
                          Unsupported order type. [Market] Skipping.


                          I'm trying to figure out where that "Unsupported" message comes from; perhaps I am closing the order incorrectly...

                          This is my close method (and open, and cancel):

                          Code:
                          private void ClosePosition(string name="Close", string dir="", int qty=0)
                          {
                              if (qty == 0)    qty = Position.Quantity;
                              if (dir == "")    dir = Position.MarketPosition.ToString();
                              
                              if (dir == "Long"  &&  Position.MarketPosition == MarketPosition.Long)
                              {
                                  CancelStops();
                                  SubmitOrderUnmanaged(0, OrderAction.Sell, OrderType.Market, qty, 0, 0, "", name);
                              }
                              else if (dir == "Short"  &&  Position.MarketPosition == MarketPosition.Short)
                              {
                                  CancelStops();
                                  SubmitOrderUnmanaged(0, OrderAction.BuyToCover, OrderType.Market, qty, 0, 0, "", name);
                              }
                          }
                          
                          private void PlaceOrder(string dir, int qty)
                          {
                              if (dir == "Long")
                              {
                                  if (Position.MarketPosition == MarketPosition.Short)
                                      CancelStops();
                                  SubmitOrderUnmanaged(0, OrderAction.Buy, OrderType.Market, qty, 0, 0, "", "Entry");
                              }
                              else if (dir == "Short")
                              {
                                  if (Position.MarketPosition == MarketPosition.Long)
                                      CancelStops();
                                  SubmitOrderUnmanaged(0, OrderAction.SellShort, OrderType.Market, qty, 0, 0, "", "Entry");
                              }
                          }
                          
                          private void CancelStops()
                          {
                              if (slOrder != null)
                              {
                                  //Log($"Cancelling slOrder");
                                  CancelOrder(slOrder);
                                  slOrder = null;
                              }
                              if (tpOrder1 != null)
                              {
                                  //Log($"Cancelling tpOrder1");
                                  CancelOrder(tpOrder1);
                                  tpOrder1 = null;
                              }
                              if (tpOrder2 != null)
                              {
                                  //Log($"Cancelling tpOrder2");
                                  CancelOrder(tpOrder2);
                                  tpOrder2 = null;
                              }
                              if (tpOrder3 != null)
                              {
                                  //Log($"Cancelling tpOrder3");
                                  CancelOrder(tpOrder3);
                                  tpOrder3 = null;
                              }
                          }
                          ​​​
                          And when I get a reversal signal, I call it like this:

                          Code:
                              string curPos = Position.MarketPosition.ToString();
                              if (curPos != "Flat"  &&  curPos != signalDir)
                              {
                                  Log($"(Open position is opposite; closing first)");
                                  ClosePosition("Reverse", curPos);
                              }
                              PlaceOrder(signalDir, Quantity);
                          ​​
                          I can only think that one or both of my use of OrderType.Market is causing the Unsupported error...

                          Comment


                            #14
                            Hello DerkWehler,

                            I don't have any details on what may cause that message, you would have to track down how your logic is specifically working by using prints and see what combination of actions are happening at that time.

                            Comment


                              #15
                              Originally posted by NinjaTrader_Jesse View Post
                              Hello DerkWehler,

                              I don't have any details on what may cause that message, you would have to track down how your logic is specifically working by using prints and see what combination of actions are happening at that time.
                              So my ClosePosition() code looks correct?

                              Comment

                              Latest Posts

                              Collapse

                              Topics Statistics Last Post
                              Started by Geovanny Suaza, 02-11-2026, 06:32 PM
                              0 responses
                              603 views
                              0 likes
                              Last Post Geovanny Suaza  
                              Started by Geovanny Suaza, 02-11-2026, 05:51 PM
                              0 responses
                              349 views
                              1 like
                              Last Post Geovanny Suaza  
                              Started by Mindset, 02-09-2026, 11:44 AM
                              0 responses
                              104 views
                              0 likes
                              Last Post Mindset
                              by Mindset
                               
                              Started by Geovanny Suaza, 02-02-2026, 12:30 PM
                              0 responses
                              560 views
                              1 like
                              Last Post Geovanny Suaza  
                              Started by RFrosty, 01-28-2026, 06:49 PM
                              0 responses
                              560 views
                              1 like
                              Last Post RFrosty
                              by RFrosty
                               
                              Working...
                              X